From 98b4a0c70574dc51d2ad1120da6fe282308a01a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20JOSE?= Date: Wed, 24 Aug 2022 17:46:02 +0200 Subject: [PATCH 1/2] exposeAllKeys default true for flags route + more info in flag route --- internal/apilogic/handle_request.go | 6 ++++ internal/apilogic/handle_request_test.go | 39 ++++++++++++++++++++++++ pkg/handlers/flags.go | 27 ++++++++++------ 3 files changed, 63 insertions(+), 9 deletions(-) diff --git a/internal/apilogic/handle_request.go b/internal/apilogic/handle_request.go index 25b3040..e0db440 100644 --- a/internal/apilogic/handle_request.go +++ b/internal/apilogic/handle_request.go @@ -2,6 +2,7 @@ package apilogic import ( "net/http" + "strings" "github.com/flagship-io/decision-api/internal/handle" "github.com/flagship-io/decision-api/internal/utils" @@ -23,6 +24,11 @@ func BuildHandleRequest(req *http.Request) (*handle.Request, error) { handleRequest.Mode = mode } + // exposeAllKeys default true for flags route + if strings.Contains(req.URL.String(), "/flags") { + handleRequest.ExposeAllKeys = true + } + // exposeAllKeys url param extend exposeAllKeys := req.URL.Query().Get("exposeAllKeys") if exposeAllKeys != "" { handleRequest.ExposeAllKeys = exposeAllKeys == "true" diff --git a/internal/apilogic/handle_request_test.go b/internal/apilogic/handle_request_test.go index e8ea646..16c480b 100644 --- a/internal/apilogic/handle_request_test.go +++ b/internal/apilogic/handle_request_test.go @@ -51,3 +51,42 @@ func TestBuildHandleRequestHasCorrectSendContextEvent(t *testing.T) { } } + +func TestBuildHandleRequestHasCorrectExposeAllKeys(t *testing.T) { + tests := map[string]struct { + path string + query string + result bool + }{ + "FlagRouteUrlParamEmpty": {"/v2/flags", "", true}, + "FlagRouteUrlParamTrue": {"/v2/flags", "exposeAllKeys=true", true}, + "FlagRouteUrlParamFalse": {"/v2/flags", "exposeAllKeys=false", false}, + "CampaignsRouteUrlParamFalse": {"/v2/campaigns", "", false}, + "CampaignsRouteUrlParamTrue": {"/v2/campaigns", "exposeAllKeys=true", true}, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + body := `{ + "visitor_id": "123", + "context": {} + }` + + req := &http.Request{ + URL: &url.URL{ + Path: test.path, + RawQuery: test.query, + }, + Body: io.NopCloser(strings.NewReader(body)), + Method: "POST", + } + + hr, err := BuildHandleRequest(req) + + assert.NotNil(t, hr) + assert.Nil(t, err) + assert.Equal(t, test.result, hr.ExposeAllKeys) + }) + } + +} diff --git a/pkg/handlers/flags.go b/pkg/handlers/flags.go index 85474a1..ccffad6 100644 --- a/pkg/handlers/flags.go +++ b/pkg/handlers/flags.go @@ -12,9 +12,12 @@ import ( // FlagMetadata represents the metadata informations about a flag key type FlagMetadata struct { - CampaignID string `json:"campaignId"` - VariationGroupID string `json:"variationGroupId"` - VariationID string `json:"variationId"` + CampaignID string `json:"campaignId"` + Slug *string `json:"slug"` + Type string `json:"type"` + VariationGroupID string `json:"variationGroupId"` + VariationID string `json:"variationId"` + Reference bool `json:"reference"` } // FlagInfo represents the informations about a flag key @@ -56,13 +59,19 @@ func sendFlagsResponse(w http.ResponseWriter, decisionResponse *decision_respons for _, c := range decisionResponse.Campaigns { if c.GetVariation() != nil && c.GetVariation().GetModifications() != nil && c.GetVariation().GetModifications().GetValue() != nil && c.GetVariation().GetModifications().GetValue().GetFields() != nil { for k, v := range c.GetVariation().GetModifications().GetValue().GetFields() { + mdata := FlagMetadata{ + CampaignID: c.GetId().Value, + Type: c.GetType().Value, + VariationGroupID: c.GetVariationGroupId().Value, + VariationID: c.GetVariation().GetId().Value, + Reference: c.GetVariation().GetReference(), + } + if c.GetSlug() != nil { + mdata.Slug = &c.GetSlug().Value + } flagInfos[k] = &FlagInfo{ - Value: v, - Metadata: FlagMetadata{ - CampaignID: c.GetId().Value, - VariationGroupID: c.GetVariationGroupId().Value, - VariationID: c.GetVariation().GetId().Value, - }, + Value: v, + Metadata: mdata, } } } From 21e37f03d63da67f7069039bc4db7d0a611aa61d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20JOSE?= Date: Thu, 25 Aug 2022 11:01:51 +0200 Subject: [PATCH 2/2] add swagger --- docs/docs.go | 154 ++++++++++++++++++++++++++++++--- docs/swagger.json | 85 ++++++++++++++++++ docs/swagger.yaml | 56 ++++++++++++ pkg/handlers/flags.go | 24 ++--- pkg/handlers/swagger_models.go | 9 +- 5 files changed, 300 insertions(+), 28 deletions(-) diff --git a/docs/docs.go b/docs/docs.go index 0e8f58f..0270230 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -2,9 +2,16 @@ // This file was generated by swaggo/swag package docs -import "github.com/swaggo/swag" +import ( + "bytes" + "encoding/json" + "strings" + "text/template" -const docTemplate = `{ + "github.com/swaggo/swag" +) + +var doc = `{ "schemes": {{ marshal .Schemes }}, "swagger": "2.0", "info": { @@ -169,6 +176,56 @@ const docTemplate = `{ } } }, + "/flags": { + "post": { + "description": "Get all flags value and metadata for a visitor ID and context", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Flags" + ], + "summary": "Get all flags", + "operationId": "get-flags", + "parameters": [ + { + "description": "Flag request body", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/handlers.campaignsBodySwagger" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/handlers.FlagInfo" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/handlers.errorMessage" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/handlers.errorMessage" + } + } + } + } + }, "/metrics": { "get": { "description": "Gets the metrics like memory consumption \u0026 allocation as well as response time histograms to use with monitoring tools", @@ -192,6 +249,38 @@ const docTemplate = `{ } }, "definitions": { + "handlers.FlagInfo": { + "type": "object", + "properties": { + "metadata": { + "$ref": "#/definitions/handlers.FlagMetadata" + }, + "value": {} + } + }, + "handlers.FlagMetadata": { + "type": "object", + "properties": { + "campaignId": { + "type": "string" + }, + "reference": { + "type": "boolean" + }, + "slug": { + "type": "string" + }, + "type": { + "type": "string" + }, + "variationGroupId": { + "type": "string" + }, + "variationId": { + "type": "string" + } + } + }, "handlers.MetricsResponse": { "type": "object", "properties": { @@ -291,6 +380,9 @@ const docTemplate = `{ "trigger_hit": { "type": "boolean" }, + "visitor_consent": { + "type": "boolean" + }, "visitor_id": { "type": "string" } @@ -350,18 +442,56 @@ const docTemplate = `{ } }` +type swaggerInfo struct { + Version string + Host string + BasePath string + Schemes []string + Title string + Description string +} + // SwaggerInfo holds exported Swagger Info so clients can modify it -var SwaggerInfo = &swag.Spec{ - Version: "2.0", - Host: "", - BasePath: "/v2", - Schemes: []string{}, - Title: "Flagship Decision API", - Description: "This is the Flagship Decision API documentation", - InfoInstanceName: "swagger", - SwaggerTemplate: docTemplate, +var SwaggerInfo = swaggerInfo{ + Version: "2.0", + Host: "", + BasePath: "/v2", + Schemes: []string{}, + Title: "Flagship Decision API", + Description: "This is the Flagship Decision API documentation", +} + +type s struct{} + +func (s *s) ReadDoc() string { + sInfo := SwaggerInfo + sInfo.Description = strings.Replace(sInfo.Description, "\n", "\\n", -1) + + t, err := template.New("swagger_info").Funcs(template.FuncMap{ + "marshal": func(v interface{}) string { + a, _ := json.Marshal(v) + return string(a) + }, + "escape": func(v interface{}) string { + // escape tabs + str := strings.Replace(v.(string), "\t", "\\t", -1) + // replace " with \", and if that results in \\", replace that with \\\" + str = strings.Replace(str, "\"", "\\\"", -1) + return strings.Replace(str, "\\\\\"", "\\\\\\\"", -1) + }, + }).Parse(doc) + if err != nil { + return doc + } + + var tpl bytes.Buffer + if err := t.Execute(&tpl, sInfo); err != nil { + return doc + } + + return tpl.String() } func init() { - swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo) + swag.Register("swagger", &s{}) } diff --git a/docs/swagger.json b/docs/swagger.json index ffb3e5d..7b7259c 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -161,6 +161,56 @@ } } }, + "/flags": { + "post": { + "description": "Get all flags value and metadata for a visitor ID and context", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Flags" + ], + "summary": "Get all flags", + "operationId": "get-flags", + "parameters": [ + { + "description": "Flag request body", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/handlers.campaignsBodySwagger" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/handlers.FlagInfo" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/handlers.errorMessage" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/handlers.errorMessage" + } + } + } + } + }, "/metrics": { "get": { "description": "Gets the metrics like memory consumption \u0026 allocation as well as response time histograms to use with monitoring tools", @@ -184,6 +234,38 @@ } }, "definitions": { + "handlers.FlagInfo": { + "type": "object", + "properties": { + "metadata": { + "$ref": "#/definitions/handlers.FlagMetadata" + }, + "value": {} + } + }, + "handlers.FlagMetadata": { + "type": "object", + "properties": { + "campaignId": { + "type": "string" + }, + "reference": { + "type": "boolean" + }, + "slug": { + "type": "string" + }, + "type": { + "type": "string" + }, + "variationGroupId": { + "type": "string" + }, + "variationId": { + "type": "string" + } + } + }, "handlers.MetricsResponse": { "type": "object", "properties": { @@ -283,6 +365,9 @@ "trigger_hit": { "type": "boolean" }, + "visitor_consent": { + "type": "boolean" + }, "visitor_id": { "type": "string" } diff --git a/docs/swagger.yaml b/docs/swagger.yaml index f89eb62..bfc265d 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -1,5 +1,26 @@ basePath: /v2 definitions: + handlers.FlagInfo: + properties: + metadata: + $ref: '#/definitions/handlers.FlagMetadata' + value: {} + type: object + handlers.FlagMetadata: + properties: + campaignId: + type: string + reference: + type: boolean + slug: + type: string + type: + type: string + variationGroupId: + type: string + variationId: + type: string + type: object handlers.MetricsResponse: properties: cmdline: @@ -64,6 +85,8 @@ definitions: $ref: '#/definitions/handlers.campaignsBodyContextSwagger' trigger_hit: type: boolean + visitor_consent: + type: boolean visitor_id: type: string required: @@ -210,6 +233,39 @@ paths: summary: Get a single campaigns for the visitor tags: - Campaigns + /flags: + post: + consumes: + - application/json + description: Get all flags value and metadata for a visitor ID and context + operationId: get-flags + parameters: + - description: Flag request body + in: body + name: request + required: true + schema: + $ref: '#/definitions/handlers.campaignsBodySwagger' + produces: + - application/json + responses: + "200": + description: OK + schema: + additionalProperties: + $ref: '#/definitions/handlers.FlagInfo' + type: object + "400": + description: Bad Request + schema: + $ref: '#/definitions/handlers.errorMessage' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/handlers.errorMessage' + summary: Get all flags + tags: + - Flags /metrics: get: description: Gets the metrics like memory consumption & allocation as well as diff --git a/pkg/handlers/flags.go b/pkg/handlers/flags.go index ccffad6..6663828 100644 --- a/pkg/handlers/flags.go +++ b/pkg/handlers/flags.go @@ -26,18 +26,18 @@ type FlagInfo struct { Metadata FlagMetadata `json:"metadata"` } -// // Flags returns a flags handler -// // @Summary Get all flags -// // @Tags Flags -// // @Description Get all flags value and metadata for a visitor ID and context -// // @ID get-flags -// // @Accept json -// // @Produce json -// // @Param request body campaignsBodySwagger true "Flag request body" -// // @Success 200 {object} map[string]FlagInfo{} -// // @Failure 400 {object} errorMessage -// // @Failure 500 {object} errorMessage -// // @Router /flags [post] +// Flags returns a flags handler +// @Summary Get all flags +// @Tags Flags +// @Description Get all flags value and metadata for a visitor ID and context +// @ID get-flags +// @Accept json +// @Produce json +// @Param request body campaignsBodySwagger true "Flag request body" +// @Success 200 {object} map[string]FlagInfo{} +// @Failure 400 {object} errorMessage +// @Failure 500 {object} errorMessage +// @Router /flags [post] func Flags(context *connectors.DecisionContext) func(http.ResponseWriter, *http.Request) { return func(w http.ResponseWriter, req *http.Request) { apilogic.HandleCampaigns(w, req, context, requestFlagsHandler, utils.NewTracker()) diff --git a/pkg/handlers/swagger_models.go b/pkg/handlers/swagger_models.go index 03d3a18..80a5f79 100644 --- a/pkg/handlers/swagger_models.go +++ b/pkg/handlers/swagger_models.go @@ -33,10 +33,11 @@ type campaignsBodyContextSwagger struct { //nolint type campaignsBodySwagger struct { - VisitorID string `json:"visitor_id" binding:"required"` - AnonymousID *string `json:"anonymous_id"` - Context campaignsBodyContextSwagger `json:"context"` - TriggerHit bool `json:"trigger_hit"` + VisitorID string `json:"visitor_id" binding:"required"` + AnonymousID *string `json:"anonymous_id"` + Context campaignsBodyContextSwagger `json:"context"` + VisitorConsent bool `json:"visitor_consent"` + TriggerHit bool `json:"trigger_hit"` } // nolint