diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e298d3b6279..ad7d07625fb 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -121,7 +121,7 @@ jobs: username: ${DOCKER_USERNAME} password: ${DOCKER_PASSWORD} repository: gofeatureflag/go-feature-flag-cli - readme: "./cmd/lint/DOCKERHUB.md" + readme: "./cmd/cli/DOCKERHUB.md" dockerhub-relay-proxy: runs-on: ubuntu-latest @@ -254,7 +254,7 @@ jobs: token: ${{ secrets.PERSONAL_GITHUB_TOKEN }} - name: yq - portable yaml processor - uses: mikefarah/yq@v4.44.5 + uses: mikefarah/yq@v4.44.6 - name: Bump chart appVersion working-directory: ./release/ diff --git a/.schema/flag-schema.json b/.schema/flag-schema.json index a52e844fac8..e2a68b4e170 100644 --- a/.schema/flag-schema.json +++ b/.schema/flag-schema.json @@ -3,6 +3,18 @@ "$defs": { "DTO": { "properties": { + "trackEvents": { + "type": "boolean" + }, + "disable": { + "type": "boolean" + }, + "version": { + "type": "string" + }, + "converter": { + "type": "string" + }, "variations": { "additionalProperties": true, "type": "object", @@ -42,30 +54,6 @@ "type": "object", "title": "metadata", "description": "A field containing information about your flag such as an issue tracker link a description etc..." - }, - "rule": { - "type": "string" - }, - "percentage": { - "type": "number" - }, - "true": true, - "false": true, - "default": true, - "rollout": { - "$ref": "#/$defs/Rollout" - }, - "trackEvents": { - "type": "boolean" - }, - "disable": { - "type": "boolean" - }, - "version": { - "type": "string" - }, - "converter": { - "type": "string" } }, "additionalProperties": false, @@ -111,32 +99,6 @@ "additionalProperties": false, "type": "object" }, - "ProgressivePercentageV0": { - "properties": { - "initial": { - "type": "number" - }, - "end": { - "type": "number" - } - }, - "additionalProperties": false, - "type": "object" - }, - "ProgressiveReleaseRampV0": { - "properties": { - "start": { - "type": "string", - "format": "date-time" - }, - "end": { - "type": "string", - "format": "date-time" - } - }, - "additionalProperties": false, - "type": "object" - }, "ProgressiveRollout": { "properties": { "initial": { @@ -180,33 +142,6 @@ "date" ] }, - "ProgressiveV0": { - "properties": { - "percentage": { - "$ref": "#/$defs/ProgressivePercentageV0" - }, - "releaseRamp": { - "$ref": "#/$defs/ProgressiveReleaseRampV0" - } - }, - "additionalProperties": false, - "type": "object" - }, - "Rollout": { - "properties": { - "experimentation": { - "$ref": "#/$defs/ExperimentationDto" - }, - "progressive": { - "$ref": "#/$defs/ProgressiveV0" - }, - "scheduled": { - "$ref": "#/$defs/ScheduledRolloutV0" - } - }, - "additionalProperties": false, - "type": "object" - }, "Rule": { "properties": { "name": { @@ -246,18 +181,6 @@ "additionalProperties": false, "type": "object" }, - "ScheduledRolloutV0": { - "properties": { - "steps": { - "items": { - "$ref": "#/$defs/ScheduledStepV0" - }, - "type": "array" - } - }, - "additionalProperties": false, - "type": "object" - }, "ScheduledStep": { "properties": { "variations": { @@ -304,84 +227,6 @@ }, "additionalProperties": false, "type": "object" - }, - "ScheduledStepV0": { - "properties": { - "variations": { - "additionalProperties": true, - "type": "object", - "title": "variations", - "description": "All the variations available for this flag. You need at least 2 variations and it is a key value pair. All the variations should have the same type." - }, - "targeting": { - "items": { - "$ref": "#/$defs/Rule" - }, - "type": "array", - "title": "targeting", - "description": "List of rule to target a subset of the users based on the evaluation context." - }, - "bucketingKey": { - "type": "string" - }, - "defaultRule": { - "$ref": "#/$defs/Rule", - "title": "defaultRule", - "description": "How do we evaluate the flag if the user is not part of any of the targeting rule." - }, - "scheduledRollout": { - "items": { - "$ref": "#/$defs/ScheduledStep" - }, - "type": "array", - "title": "scheduledRollout", - "description": "Configure an update on some fields of your flag over time." - }, - "experimentation": { - "$ref": "#/$defs/ExperimentationDto", - "title": "experimentation", - "description": "Configure an experimentation. It will allow you to configure a start date and an end date for your flag." - }, - "metadata": { - "type": "object", - "title": "metadata", - "description": "A field containing information about your flag such as an issue tracker link a description etc..." - }, - "rule": { - "type": "string" - }, - "percentage": { - "type": "number" - }, - "true": true, - "false": true, - "default": true, - "rollout": { - "$ref": "#/$defs/Rollout" - }, - "trackEvents": { - "type": "boolean" - }, - "disable": { - "type": "boolean" - }, - "version": { - "type": "string" - }, - "converter": { - "type": "string" - }, - "date": { - "type": "string", - "format": "date-time" - } - }, - "additionalProperties": false, - "type": "object", - "required": [ - "variations", - "defaultRule" - ] } }, "additionalProperties": { diff --git a/README.md b/README.md index da76d02d469..5985887feb2 100644 --- a/README.md +++ b/README.md @@ -615,7 +615,7 @@ Thanks so much to our contributors. [Become a sponsor](https://github.com/sponsors/thomaspoignant) and show your support to GO Feature Flag. These are our really cool sponsors! -CybozuEmilien FugierGiulia +User avatar: Cybozu ## Adopters diff --git a/cmd/relayproxy/api/server.go b/cmd/relayproxy/api/server.go index c9bda394a2e..485424d61cb 100644 --- a/cmd/relayproxy/api/server.go +++ b/cmd/relayproxy/api/server.go @@ -19,7 +19,7 @@ import ( "github.com/thomaspoignant/go-feature-flag/cmd/relayproxy/metric" "github.com/thomaspoignant/go-feature-flag/cmd/relayproxy/ofrep" "github.com/thomaspoignant/go-feature-flag/cmd/relayproxy/service" - "go.opentelemetry.io/contrib/instrumentation/github.com/labstack/echo/otelecho" // nolint + "go.opentelemetry.io/contrib/instrumentation/github.com/labstack/echo/otelecho" "go.uber.org/zap" ) @@ -54,37 +54,41 @@ func (s *Server) initRoutes() { s.apiEcho.HideBanner = true s.apiEcho.HidePort = true s.apiEcho.Debug = s.config.IsDebugEnabled() + s.apiEcho.Use(otelecho.Middleware("go-feature-flag")) + // Timeout middleware has to be the first middleware in the list + // (see: https://github.com/labstack/echo/blob/3b017855b4d331002e2b8b28e903679b875ae3e9/middleware/timeout.go#L17) + s.apiEcho.Use(middleware.TimeoutWithConfig( + middleware.TimeoutConfig{ + Skipper: func(c echo.Context) bool { + // ignore websocket in the timeout + return strings.HasPrefix(c.Request().URL.String(), "/ws") + }, + Timeout: time.Duration(s.config.RestAPITimeout) * time.Millisecond, + OnTimeoutRouteErrorHandler: func(err error, c echo.Context) { + s.zapLog.Error("Timeout on route", zap.String("route", c.Path()), zap.Error(err)) + }, + ErrorMessage: `Timeout on the server, please retry later`, + }), + ) s.apiEcho.Use(custommiddleware.ZapLogger(s.zapLog, s.config)) - s.apiEcho.Use(middleware.BodyDumpWithConfig(middleware.BodyDumpConfig{ - Skipper: func(_ echo.Context) bool { - return !s.zapLog.Core().Enabled(zap.DebugLevel) + Skipper: func(c echo.Context) bool { + isSwagger := strings.HasPrefix(c.Request().URL.String(), "/swagger") + return isSwagger || !s.zapLog.Core().Enabled(zap.DebugLevel) }, Handler: func(_ echo.Context, reqBody []byte, _ []byte) { s.zapLog.Debug("Request info", zap.ByteString("request_body", reqBody)) }, })) - if s.services.Metrics != (metric.Metrics{}) { s.apiEcho.Use(echoprometheus.NewMiddlewareWithConfig(echoprometheus.MiddlewareConfig{ Subsystem: metric.GOFFSubSystem, Registerer: s.services.Metrics.Registry, })) } - - s.apiEcho.Use(otelecho.Middleware("go-feature-flag")) s.apiEcho.Use(middleware.CORSWithConfig(middleware.DefaultCORSConfig)) s.apiEcho.Use(custommiddleware.VersionHeader(s.config)) s.apiEcho.Use(middleware.Recover()) - s.apiEcho.Use(middleware.TimeoutWithConfig( - middleware.TimeoutConfig{ - Skipper: func(c echo.Context) bool { - // ignore websocket in the timeout - return strings.HasPrefix(c.Request().URL.String(), "/ws") - }, - Timeout: time.Duration(s.config.RestAPITimeout) * time.Millisecond, - }), - ) // Init controllers cAllFlags := controller.NewAllFlags(s.services.GOFeatureFlagService, s.services.Metrics) diff --git a/cmd/relayproxy/helm-charts/relay-proxy/Chart.yaml b/cmd/relayproxy/helm-charts/relay-proxy/Chart.yaml index ccd53e8c447..1daff12c10c 100644 --- a/cmd/relayproxy/helm-charts/relay-proxy/Chart.yaml +++ b/cmd/relayproxy/helm-charts/relay-proxy/Chart.yaml @@ -5,8 +5,8 @@ sources: - "https://github.com/thomaspoignant/go-feature-flag" description: A Helm chart to deploy go-feature-flag-relay proxy into Kubernetes type: application -version: 1.39.1 -appVersion: "v1.39.1" +version: 1.40.0 +appVersion: "v1.40.0" icon: https://raw.githubusercontent.com/thomaspoignant/go-feature-flag/main/logo.png maintainers: - name: thomaspoignant diff --git a/cmd/relayproxy/helm-charts/relay-proxy/README.md b/cmd/relayproxy/helm-charts/relay-proxy/README.md index 4df96fee23a..28918d1ee5a 100644 --- a/cmd/relayproxy/helm-charts/relay-proxy/README.md +++ b/cmd/relayproxy/helm-charts/relay-proxy/README.md @@ -1,6 +1,6 @@ # relay-proxy -![Version: 1.39.1](https://img.shields.io/badge/Version-1.39.1-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: v1.39.1](https://img.shields.io/badge/AppVersion-v1.39.1-informational?style=flat-square) +![Version: 1.40.0](https://img.shields.io/badge/Version-1.40.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: v1.40.0](https://img.shields.io/badge/AppVersion-v1.40.0-informational?style=flat-square) A Helm chart to deploy go-feature-flag-relay proxy into Kubernetes diff --git a/cmd/relayproxy/testdata/controller/config_flags.yaml b/cmd/relayproxy/testdata/controller/config_flags.yaml index da1549b6606..2a9aa90e71d 100644 --- a/cmd/relayproxy/testdata/controller/config_flags.yaml +++ b/cmd/relayproxy/testdata/controller/config_flags.yaml @@ -1,136 +1,136 @@ array-flag: - variations: - Default: - - batmanDefault - - supermanDefault - - superherosDefault - "False": - - batmanFalse - - supermanFalse - - superherosFalse - "True": - - batmanTrue - - supermanTrue - - superherosTrue - targeting: - - name: rule1 - query: anonymous eq true - percentage: - "False": 90 - "True": 10 - defaultRule: - name: defaultRule - variation: Default + variations: + Default: + - batmanDefault + - supermanDefault + - superherosDefault + "False": + - batmanFalse + - supermanFalse + - superherosFalse + "True": + - batmanTrue + - supermanTrue + - superherosTrue + targeting: + - name: rule1 + query: anonymous eq true + percentage: + "False": 90 + "True": 10 + defaultRule: + name: defaultRule + variation: Default disable-flag: - variations: - Default: default - "False": "false" - "True": "true" - targeting: - - name: rule1 - query: admin eq true - percentage: - "False": 0 - "True": 100 - defaultRule: - name: defaultRule - variation: Default - disable: true + variations: + Default: default + "False": "false" + "True": "true" + targeting: + - name: rule1 + query: admin eq true + percentage: + "False": 0 + "True": 100 + defaultRule: + name: defaultRule + variation: Default + disable: true flag-only-for-admin: - variations: - Default: false - "False": false - "True": true - targeting: - - name: rule1 - query: admin eq true - percentage: - "False": 0 - "True": 100 - defaultRule: - name: defaultRule - variation: Default + variations: + Default: false + "False": false + "True": true + targeting: + - name: rule1 + query: admin eq true + percentage: + "False": 0 + "True": 100 + defaultRule: + name: defaultRule + variation: Default new-admin-access: - variations: - Default: false - "False": false - "True": true - defaultRule: - name: defaultRule - percentage: - "False": 70 - "True": 30 + variations: + Default: false + "False": false + "True": true + defaultRule: + name: defaultRule + percentage: + "False": 70 + "True": 30 number-flag: - variations: - Default: 1 - "False": 3 - "True": 2 - targeting: - - name: rule1 - query: anonymous eq true - percentage: - "False": 0 - "True": 100 - defaultRule: - name: defaultRule - variation: Default + variations: + Default: 1 + "False": 3 + "True": 2 + targeting: + - name: rule1 + query: anonymous eq true + percentage: + "False": 0 + "True": 100 + defaultRule: + name: defaultRule + variation: Default targeting-key-rule: - variations: - false_var: false - true_var: true - targeting: - - query: targetingKey eq "specific-targeting-key" - variation: true_var - defaultRule: - variation: false_var + variations: + false_var: false + true_var: true + targeting: + - query: targetingKey eq "specific-targeting-key" + variation: true_var + defaultRule: + variation: false_var test-flag-rule-apply: - variations: - Default: - test: test - "False": - test3: test - "True": - test2: test - targeting: - - name: rule1 - query: key eq "random-key" - percentage: - "False": 0 - "True": 100 - defaultRule: - name: defaultRule - variation: Default + variations: + Default: + test: test + "False": + test3: test + "True": + test2: test + targeting: + - name: rule1 + query: key eq "random-key" + percentage: + "False": 0 + "True": 100 + defaultRule: + name: defaultRule + variation: Default test-flag-rule-apply-false: - variations: - Default: - test: test - "False": - test3: test - "True": - test2: test - targeting: - - name: rule1 - query: anonymous eq true - percentage: - "False": 90 - "True": 10 - defaultRule: - name: defaultRule - variation: Default + variations: + Default: + test: test + "False": + test3: test + "True": + test2: test + targeting: + - name: rule1 + query: anonymous eq true + percentage: + "False": 90 + "True": 10 + defaultRule: + name: defaultRule + variation: Default test-flag-rule-not-apply: - variations: - Default: - test: test - "False": - test3: test - "True": - test2: test - targeting: - - name: rule1 - query: key eq \"key\" - percentage: - "False": 0 - "True": 100 - defaultRule: - name: defaultRule - variation: Default + variations: + Default: + test: test + "False": + test3: test + "True": + test2: test + targeting: + - name: rule1 + query: key eq "key" + percentage: + "False": 0 + "True": 100 + defaultRule: + name: defaultRule + variation: Default diff --git a/cmd/relayproxy/testdata/controller/flag_change/flag_change_with_config_change.json b/cmd/relayproxy/testdata/controller/flag_change/flag_change_with_config_change.json index 1ab742c93f6..a630cb6f65b 100644 --- a/cmd/relayproxy/testdata/controller/flag_change/flag_change_with_config_change.json +++ b/cmd/relayproxy/testdata/controller/flag_change/flag_change_with_config_change.json @@ -1,5 +1,5 @@ { - "hash": 2343199996, + "hash": 2781898380, "flags": { "array-flag": 1051133493, "disable-flag": 903048676, @@ -9,6 +9,6 @@ "targeting-key-rule": 4223863808, "test-flag-rule-apply": 3859871038, "test-flag-rule-apply-false": 1652367510, - "test-flag-rule-not-apply": 1389723366 + "test-flag-rule-not-apply": 370621622 } -} \ No newline at end of file +} diff --git a/cmd/relayproxy/testdata/controller/flag_change/flag_change_without_config_change.json b/cmd/relayproxy/testdata/controller/flag_change/flag_change_without_config_change.json index 1ab742c93f6..a630cb6f65b 100644 --- a/cmd/relayproxy/testdata/controller/flag_change/flag_change_without_config_change.json +++ b/cmd/relayproxy/testdata/controller/flag_change/flag_change_without_config_change.json @@ -1,5 +1,5 @@ { - "hash": 2343199996, + "hash": 2781898380, "flags": { "array-flag": 1051133493, "disable-flag": 903048676, @@ -9,6 +9,6 @@ "targeting-key-rule": 4223863808, "test-flag-rule-apply": 3859871038, "test-flag-rule-apply-false": 1652367510, - "test-flag-rule-not-apply": 1389723366 + "test-flag-rule-not-apply": 370621622 } -} \ No newline at end of file +} diff --git a/examples/openfeature_kotlin_server/kotlin-app/build.gradle.kts b/examples/openfeature_kotlin_server/kotlin-app/build.gradle.kts index 43f19568868..5ef598ff4a8 100644 --- a/examples/openfeature_kotlin_server/kotlin-app/build.gradle.kts +++ b/examples/openfeature_kotlin_server/kotlin-app/build.gradle.kts @@ -17,7 +17,7 @@ repositories { dependencies { testImplementation(kotlin("test")) implementation("dev.openfeature.contrib.providers:go-feature-flag:0.4.0") - implementation("dev.openfeature:sdk:1.12.2") + implementation("dev.openfeature:sdk:1.13.0") } tasks.test { diff --git a/examples/openfeature_kotlin_server/kotlin-app/settings.gradle.kts b/examples/openfeature_kotlin_server/kotlin-app/settings.gradle.kts index 46e06946a64..0c794404f8a 100644 --- a/examples/openfeature_kotlin_server/kotlin-app/settings.gradle.kts +++ b/examples/openfeature_kotlin_server/kotlin-app/settings.gradle.kts @@ -6,7 +6,7 @@ pluginManagement { } plugins { - id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0" + id("org.gradle.toolchains.foojay-resolver-convention") version "0.9.0" } rootProject.name = "openfeature_kotlin" \ No newline at end of file diff --git a/examples/openfeature_react/react-app/package-lock.json b/examples/openfeature_react/react-app/package-lock.json index 35d91a985c2..25b69e0d376 100644 --- a/examples/openfeature_react/react-app/package-lock.json +++ b/examples/openfeature_react/react-app/package-lock.json @@ -9,24 +9,24 @@ "version": "0.0.0", "dependencies": { "@openfeature/core": "^1.3.0", - "@openfeature/go-feature-flag-web-provider": "^0.2.1", - "@openfeature/react-sdk": "^0.4.8", + "@openfeature/go-feature-flag-web-provider": "^0.2.3", + "@openfeature/react-sdk": "^0.4.9", "@openfeature/web-sdk": "^1.3.2", - "react": "^18.2.0", - "react-dom": "^18.2.0" + "react": "^19.0.0", + "react-dom": "^19.0.0" }, "devDependencies": { - "@types/react": "^18.3.12", - "@types/react-dom": "^18.3.1", - "@typescript-eslint/eslint-plugin": "^8.16.0", - "@typescript-eslint/parser": "^8.16.0", + "@types/react": "^19.0.1", + "@types/react-dom": "^19.0.2", + "@typescript-eslint/eslint-plugin": "^8.18.0", + "@typescript-eslint/parser": "^8.18.0", "@vitejs/plugin-react": "^4.3.4", "autoprefixer": "^10.4.20", - "eslint": "^9.16.0", - "eslint-plugin-react-hooks": "^5.0.0", - "eslint-plugin-react-refresh": "^0.4.14", + "eslint": "^9.17.0", + "eslint-plugin-react-hooks": "^5.1.0", + "eslint-plugin-react-refresh": "^0.4.16", "typescript": "^5.7.2", - "vite": "^6.0.1" + "vite": "^6.0.3" } }, "node_modules/@ampproject/remapping": { @@ -825,9 +825,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.16.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.16.0.tgz", - "integrity": "sha512-tw2HxzQkrbeuvyj1tG2Yqq+0H9wGoI2IMk4EOsQeX+vmd75FtJAzf+gTA69WF+baUKRYQ3x2kbLE08js5OsTVg==", + "version": "9.17.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.17.0.tgz", + "integrity": "sha512-Sxc4hqcs1kTu0iID3kcZDW3JHq2a77HO9P8CP6YEA/FpH3Ll8UXE2r/86Rz9YJLKme39S9vU5OWNjC6Xl0Cr3w==", "dev": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1004,17 +1004,20 @@ "integrity": "sha512-dRBJjnYhEa6XoF9BNf9sW4sHuXmigfBbbatA5djbRXRBDExrXsMydMpEWQqKYhd7XwdwFatuh2q+UkVbXriUKA==" }, "node_modules/@openfeature/go-feature-flag-web-provider": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/@openfeature/go-feature-flag-web-provider/-/go-feature-flag-web-provider-0.2.1.tgz", - "integrity": "sha512-PuDUZ+Uv5ciYH8ElQ6rvFVJkNVogvoWtdEU33ABvX5pFFO3rBalJ7m9+P/XXpyIMMDGnZV/Nl/W0bWW3MpH/5A==", + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@openfeature/go-feature-flag-web-provider/-/go-feature-flag-web-provider-0.2.3.tgz", + "integrity": "sha512-M0XHxmU4zYkW2Ku/XAl8mT//vGfk6vxRIZVPvtKivaeWZmkvZZl8ypjSTHxmA2nfUzyIrzrhVqRoOW5XxTzgqA==", + "dependencies": { + "copy-anything": "3.0.5" + }, "peerDependencies": { "@openfeature/web-sdk": "^1.0.0" } }, "node_modules/@openfeature/react-sdk": { - "version": "0.4.8", - "resolved": "https://registry.npmjs.org/@openfeature/react-sdk/-/react-sdk-0.4.8.tgz", - "integrity": "sha512-v0mmOUc546o7FoOS2EY7Z7aY/ocEsVQ/tcpN0qlLwO4Nfl/zNGExGc4X/owPP2XVbWIWws5JrOutK48Hal7Zgw==", + "version": "0.4.9", + "resolved": "https://registry.npmjs.org/@openfeature/react-sdk/-/react-sdk-0.4.9.tgz", + "integrity": "sha512-yoU93s4kcVY7olvKE1FxMxsnXmlS8txkEd/kEqAXMTw65C7IHrvOyyhaSigbjLmJaSfFgf+XVwt2WDJ02vGQCQ==", "peerDependencies": { "@openfeature/web-sdk": "^1.3.0", "react": ">=16.8.0" @@ -1315,42 +1318,35 @@ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "dev": true }, - "node_modules/@types/prop-types": { - "version": "15.7.12", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz", - "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==", - "dev": true - }, "node_modules/@types/react": { - "version": "18.3.12", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.12.tgz", - "integrity": "sha512-D2wOSq/d6Agt28q7rSI3jhU7G6aiuzljDGZ2hTZHIkrTLUI+AF3WMeKkEZ9nN2fkBAlcktT6vcZjDFiIhMYEQw==", + "version": "19.0.1", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.1.tgz", + "integrity": "sha512-YW6614BDhqbpR5KtUYzTA+zlA7nayzJRA9ljz9CQoxthR0sDisYZLuvSMsil36t4EH/uAt8T52Xb4sVw17G+SQ==", "dev": true, "dependencies": { - "@types/prop-types": "*", "csstype": "^3.0.2" } }, "node_modules/@types/react-dom": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.1.tgz", - "integrity": "sha512-qW1Mfv8taImTthu4KoXgDfLuk4bydU6Q/TkADnDWWHwi4NX4BR+LWfTp2sVmTqRrsHvyDDTelgelxJ+SsejKKQ==", + "version": "19.0.2", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.0.2.tgz", + "integrity": "sha512-c1s+7TKFaDRRxr1TxccIX2u7sfCnc3RxkVyBIUA2lCpyqCF+QoAwQ/CBg7bsMdVwP120HEH143VQezKtef5nCg==", "dev": true, - "dependencies": { - "@types/react": "*" + "peerDependencies": { + "@types/react": "^19.0.0" } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.16.0.tgz", - "integrity": "sha512-5YTHKV8MYlyMI6BaEG7crQ9BhSc8RxzshOReKwZwRWN0+XvvTOm+L/UYLCYxFpfwYuAAqhxiq4yae0CMFwbL7Q==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.18.0.tgz", + "integrity": "sha512-NR2yS7qUqCL7AIxdJUQf2MKKNDVNaig/dEB0GBLU7D+ZdHgK1NoH/3wsgO3OnPVipn51tG3MAwaODEGil70WEw==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.16.0", - "@typescript-eslint/type-utils": "8.16.0", - "@typescript-eslint/utils": "8.16.0", - "@typescript-eslint/visitor-keys": "8.16.0", + "@typescript-eslint/scope-manager": "8.18.0", + "@typescript-eslint/type-utils": "8.18.0", + "@typescript-eslint/utils": "8.18.0", + "@typescript-eslint/visitor-keys": "8.18.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -1365,24 +1361,20 @@ }, "peerDependencies": { "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", - "eslint": "^8.57.0 || ^9.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" } }, "node_modules/@typescript-eslint/parser": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.16.0.tgz", - "integrity": "sha512-D7DbgGFtsqIPIFMPJwCad9Gfi/hC0PWErRRHFnaCWoEDYi5tQUDiJCTmGUbBiLzjqAck4KcXt9Ayj0CNlIrF+w==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.18.0.tgz", + "integrity": "sha512-hgUZ3kTEpVzKaK3uNibExUYm6SKKOmTU2BOxBSvOYwtJEPdVQ70kZJpPjstlnhCHcuc2WGfSbpKlb/69ttyN5Q==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "8.16.0", - "@typescript-eslint/types": "8.16.0", - "@typescript-eslint/typescript-estree": "8.16.0", - "@typescript-eslint/visitor-keys": "8.16.0", + "@typescript-eslint/scope-manager": "8.18.0", + "@typescript-eslint/types": "8.18.0", + "@typescript-eslint/typescript-estree": "8.18.0", + "@typescript-eslint/visitor-keys": "8.18.0", "debug": "^4.3.4" }, "engines": { @@ -1393,22 +1385,18 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.16.0.tgz", - "integrity": "sha512-mwsZWubQvBki2t5565uxF0EYvG+FwdFb8bMtDuGQLdCCnGPrDEDvm1gtfynuKlnpzeBRqdFCkMf9jg1fnAK8sg==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.18.0.tgz", + "integrity": "sha512-PNGcHop0jkK2WVYGotk/hxj+UFLhXtGPiGtiaWgVBVP1jhMoMCHlTyJA+hEj4rszoSdLTK3fN4oOatrL0Cp+Xw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "8.16.0", - "@typescript-eslint/visitor-keys": "8.16.0" + "@typescript-eslint/types": "8.18.0", + "@typescript-eslint/visitor-keys": "8.18.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1419,13 +1407,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.16.0.tgz", - "integrity": "sha512-IqZHGG+g1XCWX9NyqnI/0CX5LL8/18awQqmkZSl2ynn8F76j579dByc0jhfVSnSnhf7zv76mKBQv9HQFKvDCgg==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.18.0.tgz", + "integrity": "sha512-er224jRepVAVLnMF2Q7MZJCq5CsdH2oqjP4dT7K6ij09Kyd+R21r7UVJrF0buMVdZS5QRhDzpvzAxHxabQadow==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "8.16.0", - "@typescript-eslint/utils": "8.16.0", + "@typescript-eslint/typescript-estree": "8.18.0", + "@typescript-eslint/utils": "8.18.0", "debug": "^4.3.4", "ts-api-utils": "^1.3.0" }, @@ -1437,18 +1425,14 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" } }, "node_modules/@typescript-eslint/types": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.16.0.tgz", - "integrity": "sha512-NzrHj6thBAOSE4d9bsuRNMvk+BvaQvmY4dDglgkgGC0EW/tB3Kelnp3tAKH87GEwzoxgeQn9fNGRyFJM/xd+GQ==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.18.0.tgz", + "integrity": "sha512-FNYxgyTCAnFwTrzpBGq+zrnoTO4x0c1CKYY5MuUTzpScqmY5fmsh2o3+57lqdI3NZucBDCzDgdEbIaNfAjAHQA==", "dev": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1459,13 +1443,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.16.0.tgz", - "integrity": "sha512-E2+9IzzXMc1iaBy9zmo+UYvluE3TW7bCGWSF41hVWUE01o8nzr1rvOQYSxelxr6StUvRcTMe633eY8mXASMaNw==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.18.0.tgz", + "integrity": "sha512-rqQgFRu6yPkauz+ms3nQpohwejS8bvgbPyIDq13cgEDbkXt4LH4OkDMT0/fN1RUtzG8e8AKJyDBoocuQh8qNeg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "8.16.0", - "@typescript-eslint/visitor-keys": "8.16.0", + "@typescript-eslint/types": "8.18.0", + "@typescript-eslint/visitor-keys": "8.18.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -1480,22 +1464,20 @@ "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "peerDependencies": { + "typescript": ">=4.8.4 <5.8.0" } }, "node_modules/@typescript-eslint/utils": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.16.0.tgz", - "integrity": "sha512-C1zRy/mOL8Pj157GiX4kaw7iyRLKfJXBR3L82hk5kS/GyHcOFmy4YUq/zfZti72I9wnuQtA/+xzft4wCC8PJdA==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.18.0.tgz", + "integrity": "sha512-p6GLdY383i7h5b0Qrfbix3Vc3+J2k6QWw6UMUeY5JGfm3C5LbZ4QIZzJNoNOfgyRe0uuYKjvVOsO/jD4SJO+xg==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.16.0", - "@typescript-eslint/types": "8.16.0", - "@typescript-eslint/typescript-estree": "8.16.0" + "@typescript-eslint/scope-manager": "8.18.0", + "@typescript-eslint/types": "8.18.0", + "@typescript-eslint/typescript-estree": "8.18.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1505,21 +1487,17 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.16.0.tgz", - "integrity": "sha512-pq19gbaMOmFE3CbL0ZB8J8BFCo2ckfHBfaIsaOZgBIF4EoISJIdLX5xRhd0FGB0LlHReNRuzoJoMGpTjq8F2CQ==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.18.0.tgz", + "integrity": "sha512-pCh/qEA8Lb1wVIqNvBke8UaRjJ6wrAWkJO5yyIbs8Yx6TNGYyfNjOo61tLv+WwLvoLPp4BQ8B7AHKijl8NGUfw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "8.16.0", + "@typescript-eslint/types": "8.18.0", "eslint-visitor-keys": "^4.2.0" }, "engines": { @@ -1741,10 +1719,24 @@ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true }, + "node_modules/copy-anything": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-3.0.5.tgz", + "integrity": "sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==", + "dependencies": { + "is-what": "^4.1.8" + }, + "engines": { + "node": ">=12.13" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, "node_modules/cross-spawn": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.5.tgz", - "integrity": "sha512-ZVJrKKYunU38/76t0RMOulHOnUcbU9GbpWKAOZ0mhjr7CX6FVrH+4FrAapSOekrgFQ3f/8gwMEuIft0aKq6Hug==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "dependencies": { "path-key": "^3.1.0", @@ -1839,9 +1831,9 @@ } }, "node_modules/eslint": { - "version": "9.16.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.16.0.tgz", - "integrity": "sha512-whp8mSQI4C8VXd+fLgSM0lh3UlmcFtVwUQjyKCFfsp+2ItAIYhlq/hqGahGqHE6cv9unM41VlqKk2VtKYR2TaA==", + "version": "9.17.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.17.0.tgz", + "integrity": "sha512-evtlNcpJg+cZLcnVKwsai8fExnqjGPicK7gnUtlNuzu+Fv9bI0aLpND5T44VLQtoMEnI57LoXO9XAkIXwohKrA==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", @@ -1849,7 +1841,7 @@ "@eslint/config-array": "^0.19.0", "@eslint/core": "^0.9.0", "@eslint/eslintrc": "^3.2.0", - "@eslint/js": "9.16.0", + "@eslint/js": "9.17.0", "@eslint/plugin-kit": "^0.2.3", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", @@ -1858,7 +1850,7 @@ "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", - "cross-spawn": "^7.0.5", + "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.2.0", @@ -1898,9 +1890,9 @@ } }, "node_modules/eslint-plugin-react-hooks": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.0.0.tgz", - "integrity": "sha512-hIOwI+5hYGpJEc4uPRmz2ulCjAGD/N13Lukkh8cLV0i2IRk/bdZDYjgLVHj+U9Z704kLIdIO6iueGvxNur0sgw==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.1.0.tgz", + "integrity": "sha512-mpJRtPgHN2tNAvZ35AMfqeB3Xqeo273QxrHJsbBEPWODRM4r0yB6jfoROqKEYrOn27UtRPpcpHc2UqyBSuUNTw==", "dev": true, "engines": { "node": ">=10" @@ -1910,12 +1902,12 @@ } }, "node_modules/eslint-plugin-react-refresh": { - "version": "0.4.14", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.14.tgz", - "integrity": "sha512-aXvzCTK7ZBv1e7fahFuR3Z/fyQQSIQ711yPgYRj+Oj64tyTgO4iQIDmYXDBqvSWQ/FA4OSCsXOStlF+noU0/NA==", + "version": "0.4.16", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.16.tgz", + "integrity": "sha512-slterMlxAhov/DZO8NScf6mEeMBBXodFUolijDvrtTxyezyLoTQaa73FyYus/VbTdftd8wBgBxPMRk3poleXNQ==", "dev": true, "peerDependencies": { - "eslint": ">=7" + "eslint": ">=8.40" } }, "node_modules/eslint-scope": { @@ -2374,6 +2366,17 @@ "node": ">=0.12.0" } }, + "node_modules/is-what": { + "version": "4.1.16", + "resolved": "https://registry.npmjs.org/is-what/-/is-what-4.1.16.tgz", + "integrity": "sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==", + "engines": { + "node": ">=12.13" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -2383,7 +2386,8 @@ "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true }, "node_modules/js-yaml": { "version": "4.1.0", @@ -2482,17 +2486,6 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, - "node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" - }, - "bin": { - "loose-envify": "cli.js" - } - }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -2752,26 +2745,22 @@ ] }, "node_modules/react": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", - "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", - "dependencies": { - "loose-envify": "^1.1.0" - }, + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/react/-/react-19.0.0.tgz", + "integrity": "sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==", "engines": { "node": ">=0.10.0" } }, "node_modules/react-dom": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", - "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.0.0.tgz", + "integrity": "sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ==", "dependencies": { - "loose-envify": "^1.1.0", - "scheduler": "^0.23.2" + "scheduler": "^0.25.0" }, "peerDependencies": { - "react": "^18.3.1" + "react": "^19.0.0" } }, "node_modules/react-refresh": { @@ -2863,12 +2852,9 @@ } }, "node_modules/scheduler": { - "version": "0.23.2", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", - "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", - "dependencies": { - "loose-envify": "^1.1.0" - } + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.25.0.tgz", + "integrity": "sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA==" }, "node_modules/semver": { "version": "7.6.2", @@ -3013,9 +2999,9 @@ } }, "node_modules/vite": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.0.1.tgz", - "integrity": "sha512-Ldn6gorLGr4mCdFnmeAOLweJxZ34HjKnDm4HGo6P66IEqTxQb36VEdFJQENKxWjupNfoIjvRUnswjn1hpYEpjQ==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.0.3.tgz", + "integrity": "sha512-Cmuo5P0ENTN6HxLSo6IHsjCLn/81Vgrp81oaiFFMRa8gGDj5xEjIcEpf2ZymZtZR8oU0P2JX5WuUp/rlXcHkAw==", "dev": true, "dependencies": { "esbuild": "^0.24.0", diff --git a/examples/openfeature_react/react-app/package.json b/examples/openfeature_react/react-app/package.json index e33465548db..2cde65c2b6d 100644 --- a/examples/openfeature_react/react-app/package.json +++ b/examples/openfeature_react/react-app/package.json @@ -11,23 +11,23 @@ }, "dependencies": { "@openfeature/core": "^1.3.0", - "@openfeature/go-feature-flag-web-provider": "^0.2.1", - "@openfeature/react-sdk": "^0.4.8", + "@openfeature/go-feature-flag-web-provider": "^0.2.3", + "@openfeature/react-sdk": "^0.4.9", "@openfeature/web-sdk": "^1.3.2", - "react": "^18.2.0", - "react-dom": "^18.2.0" + "react": "^19.0.0", + "react-dom": "^19.0.0" }, "devDependencies": { - "@types/react": "^18.3.12", - "@types/react-dom": "^18.3.1", - "@typescript-eslint/eslint-plugin": "^8.16.0", - "@typescript-eslint/parser": "^8.16.0", + "@types/react": "^19.0.1", + "@types/react-dom": "^19.0.2", + "@typescript-eslint/eslint-plugin": "^8.18.0", + "@typescript-eslint/parser": "^8.18.0", "@vitejs/plugin-react": "^4.3.4", "autoprefixer": "^10.4.20", - "eslint": "^9.16.0", - "eslint-plugin-react-hooks": "^5.0.0", - "eslint-plugin-react-refresh": "^0.4.14", + "eslint": "^9.17.0", + "eslint-plugin-react-hooks": "^5.1.0", + "eslint-plugin-react-refresh": "^0.4.16", "typescript": "^5.7.2", - "vite": "^6.0.1" + "vite": "^6.0.3" } } diff --git a/examples/openfeature_web/webapp/package-lock.json b/examples/openfeature_web/webapp/package-lock.json index 48809af47c8..224fb273ec7 100644 --- a/examples/openfeature_web/webapp/package-lock.json +++ b/examples/openfeature_web/webapp/package-lock.json @@ -9,9 +9,9 @@ "version": "1.0.0", "license": "MIT", "dependencies": { - "@openfeature/go-feature-flag-web-provider": "^0.2.1", + "@openfeature/go-feature-flag-web-provider": "^0.2.3", "@openfeature/web-sdk": "^1.3.2", - "highlight.js": "^11.10.0", + "highlight.js": "^11.11.0", "uuid": "^11.0.3" }, "devDependencies": { @@ -22,7 +22,7 @@ "babel-plugin-transform-class-properties": "^6.24.1", "babel-preset-env": "^1.7.0", "browser-sync": "^3.0.3", - "eslint": "^9.16.0", + "eslint": "^9.17.0", "eslint-webpack-plugin": "^4.2.0", "htmlnano": "^2.1.1", "imagemin-cli": "^8.0.0", @@ -34,8 +34,8 @@ "posthtml": "^0.16.6", "posthtml-cli": "^0.10.0", "posthtml-modules": "^0.9.1", - "stylelint": "^16.11.0", - "webpack": "^5.96.1", + "stylelint": "^16.12.0", + "webpack": "^5.97.1", "webpack-cli": "^5.1.4" } }, @@ -1821,9 +1821,9 @@ "dev": true }, "node_modules/@eslint/js": { - "version": "9.16.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.16.0.tgz", - "integrity": "sha512-tw2HxzQkrbeuvyj1tG2Yqq+0H9wGoI2IMk4EOsQeX+vmd75FtJAzf+gTA69WF+baUKRYQ3x2kbLE08js5OsTVg==", + "version": "9.17.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.17.0.tgz", + "integrity": "sha512-Sxc4hqcs1kTu0iID3kcZDW3JHq2a77HO9P8CP6YEA/FpH3Ll8UXE2r/86Rz9YJLKme39S9vU5OWNjC6Xl0Cr3w==", "dev": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2082,9 +2082,12 @@ "peer": true }, "node_modules/@openfeature/go-feature-flag-web-provider": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/@openfeature/go-feature-flag-web-provider/-/go-feature-flag-web-provider-0.2.1.tgz", - "integrity": "sha512-PuDUZ+Uv5ciYH8ElQ6rvFVJkNVogvoWtdEU33ABvX5pFFO3rBalJ7m9+P/XXpyIMMDGnZV/Nl/W0bWW3MpH/5A==", + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@openfeature/go-feature-flag-web-provider/-/go-feature-flag-web-provider-0.2.3.tgz", + "integrity": "sha512-M0XHxmU4zYkW2Ku/XAl8mT//vGfk6vxRIZVPvtKivaeWZmkvZZl8ypjSTHxmA2nfUzyIrzrhVqRoOW5XxTzgqA==", + "dependencies": { + "copy-anything": "3.0.5" + }, "peerDependencies": { "@openfeature/web-sdk": "^1.0.0" } @@ -2279,148 +2282,148 @@ "dev": true }, "node_modules/@webassemblyjs/ast": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.12.1.tgz", - "integrity": "sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", + "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", "dev": true, "dependencies": { - "@webassemblyjs/helper-numbers": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6" + "@webassemblyjs/helper-numbers": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2" } }, "node_modules/@webassemblyjs/floating-point-hex-parser": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", - "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", + "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", "dev": true }, "node_modules/@webassemblyjs/helper-api-error": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", - "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", + "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", "dev": true }, "node_modules/@webassemblyjs/helper-buffer": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz", - "integrity": "sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", + "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", "dev": true }, "node_modules/@webassemblyjs/helper-numbers": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", - "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", + "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", "dev": true, "dependencies": { - "@webassemblyjs/floating-point-hex-parser": "1.11.6", - "@webassemblyjs/helper-api-error": "1.11.6", + "@webassemblyjs/floating-point-hex-parser": "1.13.2", + "@webassemblyjs/helper-api-error": "1.13.2", "@xtuc/long": "4.2.2" } }, "node_modules/@webassemblyjs/helper-wasm-bytecode": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", - "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", + "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", "dev": true }, "node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz", - "integrity": "sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", + "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-buffer": "1.12.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/wasm-gen": "1.12.1" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/wasm-gen": "1.14.1" } }, "node_modules/@webassemblyjs/ieee754": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", - "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", + "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", "dev": true, "dependencies": { "@xtuc/ieee754": "^1.2.0" } }, "node_modules/@webassemblyjs/leb128": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", - "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", + "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", "dev": true, "dependencies": { "@xtuc/long": "4.2.2" } }, "node_modules/@webassemblyjs/utf8": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", - "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", + "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", "dev": true }, "node_modules/@webassemblyjs/wasm-edit": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz", - "integrity": "sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", + "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-buffer": "1.12.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/helper-wasm-section": "1.12.1", - "@webassemblyjs/wasm-gen": "1.12.1", - "@webassemblyjs/wasm-opt": "1.12.1", - "@webassemblyjs/wasm-parser": "1.12.1", - "@webassemblyjs/wast-printer": "1.12.1" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/helper-wasm-section": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-opt": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1", + "@webassemblyjs/wast-printer": "1.14.1" } }, "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.12.1.tgz", - "integrity": "sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", + "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/ieee754": "1.11.6", - "@webassemblyjs/leb128": "1.11.6", - "@webassemblyjs/utf8": "1.11.6" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" } }, "node_modules/@webassemblyjs/wasm-opt": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.12.1.tgz", - "integrity": "sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", + "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-buffer": "1.12.1", - "@webassemblyjs/wasm-gen": "1.12.1", - "@webassemblyjs/wasm-parser": "1.12.1" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1" } }, "node_modules/@webassemblyjs/wasm-parser": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz", - "integrity": "sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", + "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-api-error": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/ieee754": "1.11.6", - "@webassemblyjs/leb128": "1.11.6", - "@webassemblyjs/utf8": "1.11.6" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-api-error": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" } }, "node_modules/@webassemblyjs/wast-printer": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz", - "integrity": "sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", + "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/ast": "1.14.1", "@xtuc/long": "4.2.2" } }, @@ -5067,6 +5070,20 @@ "node": ">= 0.6" } }, + "node_modules/copy-anything": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-3.0.5.tgz", + "integrity": "sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==", + "dependencies": { + "is-what": "^4.1.8" + }, + "engines": { + "node": ">=12.13" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, "node_modules/core-js": { "version": "2.6.12", "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", @@ -5134,9 +5151,9 @@ } }, "node_modules/cross-spawn": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.5.tgz", - "integrity": "sha512-ZVJrKKYunU38/76t0RMOulHOnUcbU9GbpWKAOZ0mhjr7CX6FVrH+4FrAapSOekrgFQ3f/8gwMEuIft0aKq6Hug==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "dependencies": { "path-key": "^3.1.0", @@ -6103,9 +6120,9 @@ } }, "node_modules/eslint": { - "version": "9.16.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.16.0.tgz", - "integrity": "sha512-whp8mSQI4C8VXd+fLgSM0lh3UlmcFtVwUQjyKCFfsp+2ItAIYhlq/hqGahGqHE6cv9unM41VlqKk2VtKYR2TaA==", + "version": "9.17.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.17.0.tgz", + "integrity": "sha512-evtlNcpJg+cZLcnVKwsai8fExnqjGPicK7gnUtlNuzu+Fv9bI0aLpND5T44VLQtoMEnI57LoXO9XAkIXwohKrA==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", @@ -6113,7 +6130,7 @@ "@eslint/config-array": "^0.19.0", "@eslint/core": "^0.9.0", "@eslint/eslintrc": "^3.2.0", - "@eslint/js": "9.16.0", + "@eslint/js": "9.17.0", "@eslint/plugin-kit": "^0.2.3", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", @@ -6122,7 +6139,7 @@ "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", - "cross-spawn": "^7.0.5", + "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.2.0", @@ -7855,9 +7872,9 @@ } }, "node_modules/highlight.js": { - "version": "11.10.0", - "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.10.0.tgz", - "integrity": "sha512-SYVnVFswQER+zu1laSya563s+F8VDGt7o35d4utbamowvUNLLMovFqwCLSocpZTz3MgaSRA1IbqRWZv97dtErQ==", + "version": "11.11.0", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.11.0.tgz", + "integrity": "sha512-6ErL7JlGu2CNFHyRQEuDogOyGPNiqcuWdt4iSSFUPyferNTGlNTPFqeV36Y/XwA4V/TJ8l0sxp6FTnxud/mf8g==", "engines": { "node": ">=12.0.0" } @@ -9063,6 +9080,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-what": { + "version": "4.1.16", + "resolved": "https://registry.npmjs.org/is-what/-/is-what-4.1.16.tgz", + "integrity": "sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==", + "engines": { + "node": ">=12.13" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, "node_modules/is-wsl": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", @@ -13971,9 +13999,9 @@ } }, "node_modules/stylelint": { - "version": "16.11.0", - "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.11.0.tgz", - "integrity": "sha512-zrl4IrKmjJQ+h9FoMp69UMCq5SxeHk0URhxUBj4d3ISzo/DplOFBJZc7t7Dr6otB+1bfbbKNLOmCDpzKSlW+Nw==", + "version": "16.12.0", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.12.0.tgz", + "integrity": "sha512-F8zZ3L/rBpuoBZRvI4JVT20ZanPLXfQLzMOZg1tzPflRVh9mKpOZ8qcSIhh1my3FjAjZWG4T2POwGnmn6a6hbg==", "dev": true, "funding": [ { @@ -14022,7 +14050,7 @@ "string-width": "^4.2.3", "supports-hyperlinks": "^3.1.0", "svg-tags": "^1.0.0", - "table": "^6.8.2", + "table": "^6.9.0", "write-file-atomic": "^5.0.1" }, "bin": { @@ -14241,9 +14269,9 @@ } }, "node_modules/table": { - "version": "6.8.2", - "resolved": "https://registry.npmjs.org/table/-/table-6.8.2.tgz", - "integrity": "sha512-w2sfv80nrAh2VCbqR5AK27wswXhqcck2AhfnNW76beQXskGZ1V12GwS//yYVa3d3fcvAip2OUnbDAjW2k3v9fA==", + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/table/-/table-6.9.0.tgz", + "integrity": "sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==", "dev": true, "dependencies": { "ajv": "^8.0.1", @@ -15086,16 +15114,16 @@ } }, "node_modules/webpack": { - "version": "5.96.1", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.96.1.tgz", - "integrity": "sha512-l2LlBSvVZGhL4ZrPwyr8+37AunkcYj5qh8o6u2/2rzoPc8gxFJkLj1WxNgooi9pnoc06jh0BjuXnamM4qlujZA==", + "version": "5.97.1", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.97.1.tgz", + "integrity": "sha512-EksG6gFY3L1eFMROS/7Wzgrii5mBAFe4rIr3r2BTfo7bcc+DWwFZ4OJ/miOuHJO/A85HwyI4eQ0F6IKXesO7Fg==", "dev": true, "dependencies": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.6", - "@webassemblyjs/ast": "^1.12.1", - "@webassemblyjs/wasm-edit": "^1.12.1", - "@webassemblyjs/wasm-parser": "^1.12.1", + "@webassemblyjs/ast": "^1.14.1", + "@webassemblyjs/wasm-edit": "^1.14.1", + "@webassemblyjs/wasm-parser": "^1.14.1", "acorn": "^8.14.0", "browserslist": "^4.24.0", "chrome-trace-event": "^1.0.2", diff --git a/examples/openfeature_web/webapp/package.json b/examples/openfeature_web/webapp/package.json index 5faf52c4694..11a366a77c4 100644 --- a/examples/openfeature_web/webapp/package.json +++ b/examples/openfeature_web/webapp/package.json @@ -37,7 +37,7 @@ "babel-plugin-transform-class-properties": "^6.24.1", "babel-preset-env": "^1.7.0", "browser-sync": "^3.0.3", - "eslint": "^9.16.0", + "eslint": "^9.17.0", "eslint-webpack-plugin": "^4.2.0", "htmlnano": "^2.1.1", "imagemin-cli": "^8.0.0", @@ -49,14 +49,14 @@ "posthtml": "^0.16.6", "posthtml-cli": "^0.10.0", "posthtml-modules": "^0.9.1", - "stylelint": "^16.11.0", - "webpack": "^5.96.1", + "stylelint": "^16.12.0", + "webpack": "^5.97.1", "webpack-cli": "^5.1.4" }, "dependencies": { - "@openfeature/go-feature-flag-web-provider": "^0.2.1", + "@openfeature/go-feature-flag-web-provider": "^0.2.3", "@openfeature/web-sdk": "^1.3.2", - "highlight.js": "^11.10.0", + "highlight.js": "^11.11.0", "uuid": "^11.0.3" } } diff --git a/exporter/data_exporter.go b/exporter/data_exporter.go index f5326807e22..3038a1d25d7 100644 --- a/exporter/data_exporter.go +++ b/exporter/data_exporter.go @@ -2,6 +2,7 @@ package exporter import ( "context" + "fmt" "log" "log/slog" "sync" @@ -59,13 +60,10 @@ type Scheduler struct { // the maximum number of events that can be present in the cache. func (dc *Scheduler) AddEvent(event FeatureEvent) { if !dc.exporter.IsBulk() { - dc.mutex.Lock() - // if we are not in bulk we are directly flushing the data - dc.localCache = append(dc.localCache, event) - go func() { - defer dc.mutex.Unlock() - dc.flush() - }() + err := sendEvents(dc.ctx, dc.exporter, dc.logger, []FeatureEvent{event}) + if err != nil { + dc.logger.Error(err.Error()) + } return } @@ -116,30 +114,42 @@ func (dc *Scheduler) GetLogger(level slog.Level) *log.Logger { // flush will call the data exporter and clear the cache func (dc *Scheduler) flush() { - if len(dc.localCache) > 0 { - switch exp := dc.exporter.(type) { - case DeprecatedExporter: - // use dc exporter as a DeprecatedExporter - err := exp.Export(dc.ctx, dc.GetLogger(slog.LevelError), dc.localCache) - slog.Warn("You are using an exporter with the old logger."+ - "Please update your custom exporter to comply to the new Exporter interface.", - slog.Any("err", err)) - if err != nil { - dc.logger.Error("error while exporting data", slog.Any("err", err)) - return - } - break - case Exporter: - err := exp.Export(dc.ctx, dc.logger, dc.localCache) - if err != nil { - dc.logger.Error("error while exporting data", slog.Any("err", err)) - return - } - break - default: - dc.logger.Error("this is not a valid exporter") - return - } + err := sendEvents(dc.ctx, dc.exporter, dc.logger, dc.localCache) + if err != nil { + dc.logger.Error(err.Error()) + return } dc.localCache = make([]FeatureEvent, 0) } + +func sendEvents(ctx context.Context, exporter CommonExporter, logger *fflog.FFLogger, events []FeatureEvent) error { + if len(events) == 0 { + return nil + } + + switch exp := exporter.(type) { + case DeprecatedExporter: + var legacyLogger *log.Logger + if logger != nil { + legacyLogger = logger.GetLogLogger(slog.LevelError) + } + // use dc exporter as a DeprecatedExporter + err := exp.Export(ctx, legacyLogger, events) + slog.Warn("You are using an exporter with the old logger."+ + "Please update your custom exporter to comply to the new Exporter interface.", + slog.Any("err", err)) + if err != nil { + return fmt.Errorf("error while exporting data: %w", err) + } + break + case Exporter: + err := exp.Export(ctx, logger, events) + if err != nil { + return fmt.Errorf("error while exporting data: %w", err) + } + break + default: + return fmt.Errorf("this is not a valid exporter") + } + return nil +} diff --git a/exporter/data_exporter_test.go b/exporter/data_exporter_test.go index 6252810d6dc..5a1c997c6fb 100644 --- a/exporter/data_exporter_test.go +++ b/exporter/data_exporter_test.go @@ -103,7 +103,7 @@ func TestDataExporterScheduler_exporterReturnError(t *testing.T) { dc.AddEvent(event) } assert.Equal(t, inputEvents[:201], mockExporter.GetExportedEvents()) - handler.AssertMessage("error while exporting data") + handler.AssertMessage("error while exporting data: random err") } func TestDataExporterScheduler_nonBulkExporter(t *testing.T) { diff --git a/go.mod b/go.mod index 891fc0f295d..e7603ef8cae 100644 --- a/go.mod +++ b/go.mod @@ -1,10 +1,10 @@ module github.com/thomaspoignant/go-feature-flag -go 1.22.8 +go 1.23.0 require ( - cloud.google.com/go/pubsub v1.45.1 - cloud.google.com/go/storage v1.47.0 + cloud.google.com/go/pubsub v1.45.3 + cloud.google.com/go/storage v1.48.0 github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.0 github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.5.0 github.com/BurntSushi/toml v1.4.0 @@ -12,13 +12,13 @@ require ( github.com/atc0005/go-teams-notify/v2 v2.13.0 github.com/aws/aws-lambda-go v1.47.0 github.com/aws/aws-sdk-go v1.55.5 - github.com/aws/aws-sdk-go-v2 v1.32.5 - github.com/aws/aws-sdk-go-v2/config v1.28.5 - github.com/aws/aws-sdk-go-v2/credentials v1.17.46 - github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.41 - github.com/aws/aws-sdk-go-v2/service/kinesis v1.32.6 - github.com/aws/aws-sdk-go-v2/service/s3 v1.69.0 - github.com/aws/aws-sdk-go-v2/service/sqs v1.37.1 + github.com/aws/aws-sdk-go-v2 v1.32.6 + github.com/aws/aws-sdk-go-v2/config v1.28.6 + github.com/aws/aws-sdk-go-v2/credentials v1.17.47 + github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.43 + github.com/aws/aws-sdk-go-v2/service/kinesis v1.32.7 + github.com/aws/aws-sdk-go-v2/service/s3 v1.71.0 + github.com/aws/aws-sdk-go-v2/service/sqs v1.37.2 github.com/aws/smithy-go v1.22.1 github.com/awslabs/aws-lambda-go-api-proxy v0.16.2 github.com/fsouza/fake-gcs-server v1.50.2 @@ -38,7 +38,7 @@ require ( github.com/knadh/koanf/providers/posflag v0.1.0 github.com/knadh/koanf/v2 v2.1.2 github.com/labstack/echo-contrib v0.17.1 - github.com/labstack/echo/v4 v4.12.0 + github.com/labstack/echo/v4 v4.13.2 github.com/luci/go-render v0.0.0-20160219211803-9a04cc21af0f github.com/nikunjy/rules v1.5.0 github.com/pablor21/echo-etag/v4 v4.0.3 @@ -59,28 +59,28 @@ require ( github.com/xitongsys/parquet-go v1.6.2 github.com/xitongsys/parquet-go-source v0.0.0-20230830030807-0dd610dbff1d go.mongodb.org/mongo-driver v1.17.1 - go.opentelemetry.io/contrib/exporters/autoexport v0.57.0 - go.opentelemetry.io/contrib/instrumentation/github.com/labstack/echo/otelecho v0.57.0 - go.opentelemetry.io/contrib/samplers/jaegerremote v0.26.0 - go.opentelemetry.io/otel v1.32.0 - go.opentelemetry.io/otel/sdk v1.32.0 - go.opentelemetry.io/otel/trace v1.32.0 + go.opentelemetry.io/contrib/exporters/autoexport v0.58.0 + go.opentelemetry.io/contrib/instrumentation/github.com/labstack/echo/otelecho v0.58.0 + go.opentelemetry.io/contrib/samplers/jaegerremote v0.27.0 + go.opentelemetry.io/otel v1.33.0 + go.opentelemetry.io/otel/sdk v1.33.0 + go.opentelemetry.io/otel/trace v1.33.0 go.uber.org/zap v1.27.0 - golang.org/x/net v0.31.0 + golang.org/x/net v0.32.0 golang.org/x/oauth2 v0.24.0 - google.golang.org/api v0.209.0 - google.golang.org/grpc v1.68.0 + google.golang.org/api v0.211.0 + google.golang.org/grpc v1.68.1 gopkg.in/yaml.v3 v3.0.1 - k8s.io/api v0.31.3 - k8s.io/apimachinery v0.31.3 - k8s.io/client-go v0.31.3 + k8s.io/api v0.32.0 + k8s.io/apimachinery v0.32.0 + k8s.io/client-go v0.32.0 ) require ( cel.dev/expr v0.16.1 // indirect cloud.google.com/go v0.116.0 // indirect - cloud.google.com/go/auth v0.10.2 // indirect - cloud.google.com/go/auth/oauth2adapt v0.2.5 // indirect + cloud.google.com/go/auth v0.12.1 // indirect + cloud.google.com/go/auth/oauth2adapt v0.2.6 // indirect cloud.google.com/go/compute/metadata v0.5.2 // indirect cloud.google.com/go/iam v1.2.2 // indirect cloud.google.com/go/monitoring v1.21.2 // indirect @@ -98,18 +98,18 @@ require ( github.com/apache/arrow/go/arrow v0.0.0-20200730104253-651201b0f516 // indirect github.com/apache/thrift v0.14.2 // indirect github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.20 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.24 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.24 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.21 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.25 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.25 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 // indirect - github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.24 // indirect + github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.25 // indirect github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.5 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.5 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.5 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.24.6 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.5 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.33.1 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.6 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.6 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.6 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.24.7 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.6 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.33.2 // indirect github.com/bahlo/generic-list-go v0.2.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/blang/semver v3.5.1+incompatible // indirect @@ -147,7 +147,6 @@ require ( github.com/go-openapi/swag v0.23.0 // indirect github.com/go-viper/mapstructure/v2 v2.2.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang-jwt/jwt v3.2.2+incompatible // indirect github.com/golang-jwt/jwt/v5 v5.2.1 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.4 // indirect @@ -160,7 +159,7 @@ require ( github.com/googleapis/gax-go/v2 v2.14.0 // indirect github.com/gorilla/handlers v1.5.2 // indirect github.com/gorilla/mux v1.8.1 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.23.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-uuid v1.0.3 // indirect @@ -205,7 +204,7 @@ require ( github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect github.com/prometheus/client_model v0.6.1 // indirect - github.com/prometheus/common v0.60.1 // indirect + github.com/prometheus/common v0.61.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect github.com/samber/lo v1.44.0 // indirect @@ -229,48 +228,49 @@ require ( github.com/yusufpapurcu/wmi v1.2.3 // indirect go.einride.tech/aip v0.68.0 // indirect go.opencensus.io v0.24.0 // indirect - go.opentelemetry.io/contrib/bridges/prometheus v0.57.0 // indirect + go.opentelemetry.io/auto/sdk v1.1.0 // indirect + go.opentelemetry.io/contrib/bridges/prometheus v0.58.0 // indirect go.opentelemetry.io/contrib/detectors/gcp v1.29.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.8.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.8.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.32.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.32.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.32.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.32.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.32.0 // indirect - go.opentelemetry.io/otel/exporters/prometheus v0.54.0 // indirect - go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.8.0 // indirect - go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.32.0 // indirect - go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.32.0 // indirect - go.opentelemetry.io/otel/log v0.8.0 // indirect - go.opentelemetry.io/otel/metric v1.32.0 // indirect - go.opentelemetry.io/otel/sdk/log v0.8.0 // indirect - go.opentelemetry.io/otel/sdk/metric v1.32.0 // indirect - go.opentelemetry.io/proto/otlp v1.3.1 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.9.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.9.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.33.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.33.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0 // indirect + go.opentelemetry.io/otel/exporters/prometheus v0.55.0 // indirect + go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.9.0 // indirect + go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.33.0 // indirect + go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.33.0 // indirect + go.opentelemetry.io/otel/log v0.9.0 // indirect + go.opentelemetry.io/otel/metric v1.33.0 // indirect + go.opentelemetry.io/otel/sdk/log v0.9.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.33.0 // indirect + go.opentelemetry.io/proto/otlp v1.4.0 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/crypto v0.29.0 // indirect + golang.org/x/crypto v0.31.0 // indirect golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 // indirect - golang.org/x/sync v0.9.0 // indirect - golang.org/x/sys v0.27.0 // indirect - golang.org/x/term v0.26.0 // indirect - golang.org/x/text v0.20.0 // indirect + golang.org/x/sync v0.10.0 // indirect + golang.org/x/sys v0.28.0 // indirect + golang.org/x/term v0.27.0 // indirect + golang.org/x/text v0.21.0 // indirect golang.org/x/time v0.8.0 // indirect golang.org/x/tools v0.26.0 // indirect golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect - google.golang.org/genproto v0.0.0-20241113202542-65e8d215514f // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20241104194629-dd2ea8efbc28 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20241113202542-65e8d215514f // indirect + google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576 // indirect google.golang.org/grpc/stats/opentelemetry v0.0.0-20240907200651-3ffb98b2c93a // indirect google.golang.org/protobuf v1.35.2 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect k8s.io/klog/v2 v2.130.1 // indirect - k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect - k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // indirect - sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect + k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f // indirect + k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect + sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.4.2 // indirect sigs.k8s.io/yaml v1.4.0 // indirect ) diff --git a/go.sum b/go.sum index 302987687d8..948e8ef1292 100644 --- a/go.sum +++ b/go.sum @@ -33,10 +33,10 @@ cloud.google.com/go v0.100.1/go.mod h1:fs4QogzfH5n2pBXBP9vRiU+eCny7lD2vmFZy79Iuw cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A= cloud.google.com/go v0.116.0 h1:B3fRrSDkLRt5qSHWe40ERJvhvnQwdZiHu0bJOpldweE= cloud.google.com/go v0.116.0/go.mod h1:cEPSRWPzZEswwdr9BxE6ChEn01dWlTaF05LiC2Xs70U= -cloud.google.com/go/auth v0.10.2 h1:oKF7rgBfSHdp/kuhXtqU/tNDr0mZqhYbEh+6SiqzkKo= -cloud.google.com/go/auth v0.10.2/go.mod h1:xxA5AqpDrvS+Gkmo9RqrGGRh6WSNKKOXhY3zNOr38tI= -cloud.google.com/go/auth/oauth2adapt v0.2.5 h1:2p29+dePqsCHPP1bqDJcKj4qxRyYCcbzKpFyKGt3MTk= -cloud.google.com/go/auth/oauth2adapt v0.2.5/go.mod h1:AlmsELtlEBnaNTL7jCj8VQFLy6mbZv0s4Q7NGBeQ5E8= +cloud.google.com/go/auth v0.12.1 h1:n2Bj25BUMM0nvE9D2XLTiImanwZhO3DkfWSYS/SAJP4= +cloud.google.com/go/auth v0.12.1/go.mod h1:BFMu+TNpF3DmvfBO9ClqTR/SiqVIm7LukKF9mbendF4= +cloud.google.com/go/auth/oauth2adapt v0.2.6 h1:V6a6XDu2lTwPZWOawrAa9HUK+DB2zfJyTuciBG5hFkU= +cloud.google.com/go/auth/oauth2adapt v0.2.6/go.mod h1:AlmsELtlEBnaNTL7jCj8VQFLy6mbZv0s4Q7NGBeQ5E8= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= @@ -74,8 +74,8 @@ cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+ cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= cloud.google.com/go/pubsub v1.19.0/go.mod h1:/O9kmSe9bb9KRnIAWkzmqhPjHo6LtzGOBYd/kr06XSs= -cloud.google.com/go/pubsub v1.45.1 h1:ZC/UzYcrmK12THWn1P72z+Pnp2vu/zCZRXyhAfP1hJY= -cloud.google.com/go/pubsub v1.45.1/go.mod h1:3bn7fTmzZFwaUjllitv1WlsNMkqBgGUb3UdMhI54eCc= +cloud.google.com/go/pubsub v1.45.3 h1:prYj8EEAAAwkp6WNoGTE4ahe0DgHoyJd5Pbop931zow= +cloud.google.com/go/pubsub v1.45.3/go.mod h1:cGyloK/hXC4at7smAtxFnXprKEFTqmMXNNd9w+bd94Q= cloud.google.com/go/secretmanager v1.3.0/go.mod h1:+oLTkouyiYiabAQNugCeTS3PAArGiMJuBqvJnJsyH+U= cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= @@ -83,8 +83,8 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= cloud.google.com/go/storage v1.21.0/go.mod h1:XmRlxkgPjlBONznT2dDUU/5XlpU2OjMnKuqnZI01LAA= -cloud.google.com/go/storage v1.47.0 h1:ajqgt30fnOMmLfWfu1PWcb+V9Dxz6n+9WKjdNg5R4HM= -cloud.google.com/go/storage v1.47.0/go.mod h1:Ks0vP374w0PW6jOUameJbapbQKXqkjGd/OJRp2fb9IQ= +cloud.google.com/go/storage v1.48.0 h1:FhBDHACbVtdPx7S/AbcKujPWiHvfO6F8OXGgCEbB2+o= +cloud.google.com/go/storage v1.48.0/go.mod h1:aFoDYNMAjv67lp+xcuZqjUKv/ctmplzQ3wJgodA7b+M= cloud.google.com/go/trace v1.0.0/go.mod h1:4iErSByzxkyHWzzlAj63/Gmjz0NH1ASqhJguHpGcr6A= cloud.google.com/go/trace v1.2.0/go.mod h1:Wc8y/uYyOhPy12KEnXG9XGrvfMz5F5SrYecQlbW1rwM= cloud.google.com/go/trace v1.11.2 h1:4ZmaBdL8Ng/ajrgKqY5jfvzqMXbrDcBsUGXOT9aqTtI= @@ -200,66 +200,66 @@ github.com/aws/aws-sdk-go v1.43.31/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4 github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU= github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= github.com/aws/aws-sdk-go-v2 v1.16.2/go.mod h1:ytwTPBG6fXTZLxxeeCCWj2/EMYp/xDUgX+OET6TLNNU= -github.com/aws/aws-sdk-go-v2 v1.32.5 h1:U8vdWJuY7ruAkzaOdD7guwJjD06YSKmnKCJs7s3IkIo= -github.com/aws/aws-sdk-go-v2 v1.32.5/go.mod h1:P5WJBrYqqbWVaOxgH0X/FYYD47/nooaPOZPlQdmiN2U= +github.com/aws/aws-sdk-go-v2 v1.32.6 h1:7BokKRgRPuGmKkFMhEg/jSul+tB9VvXhcViILtfG8b4= +github.com/aws/aws-sdk-go-v2 v1.32.6/go.mod h1:P5WJBrYqqbWVaOxgH0X/FYYD47/nooaPOZPlQdmiN2U= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.1/go.mod h1:n8Bs1ElDD2wJ9kCRTczA83gYbBmjSwZp3umc6zF4EeM= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7 h1:lL7IfaFzngfx0ZwUGOZdsFFnQ5uLvR0hWqqhyE7Q9M8= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7/go.mod h1:QraP0UcVlQJsmHfioCrveWOC1nbiWUl3ej08h4mXWoc= github.com/aws/aws-sdk-go-v2/config v1.15.3/go.mod h1:9YL3v07Xc/ohTsxFXzan9ZpFpdTOFl4X65BAKYaz8jg= -github.com/aws/aws-sdk-go-v2/config v1.28.5 h1:Za41twdCXbuyyWv9LndXxZZv3QhTG1DinqlFsSuvtI0= -github.com/aws/aws-sdk-go-v2/config v1.28.5/go.mod h1:4VsPbHP8JdcdUDmbTVgNL/8w9SqOkM5jyY8ljIxLO3o= +github.com/aws/aws-sdk-go-v2/config v1.28.6 h1:D89IKtGrs/I3QXOLNTH93NJYtDhm8SYa9Q5CsPShmyo= +github.com/aws/aws-sdk-go-v2/config v1.28.6/go.mod h1:GDzxJ5wyyFSCoLkS+UhGB0dArhb9mI+Co4dHtoTxbko= github.com/aws/aws-sdk-go-v2/credentials v1.11.2/go.mod h1:j8YsY9TXTm31k4eFhspiQicfXPLZ0gYXA50i4gxPE8g= -github.com/aws/aws-sdk-go-v2/credentials v1.17.46 h1:AU7RcriIo2lXjUfHFnFKYsLCwgbz1E7Mm95ieIRDNUg= -github.com/aws/aws-sdk-go-v2/credentials v1.17.46/go.mod h1:1FmYyLGL08KQXQ6mcTlifyFXfJVCNJTVGuQP4m0d/UA= +github.com/aws/aws-sdk-go-v2/credentials v1.17.47 h1:48bA+3/fCdi2yAwVt+3COvmatZ6jUDNkDTIsqDiMUdw= +github.com/aws/aws-sdk-go-v2/credentials v1.17.47/go.mod h1:+KdckOejLW3Ks3b0E3b5rHsr2f9yuORBum0WPnE5o5w= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.3/go.mod h1:uk1vhHHERfSVCUnqSqz8O48LBYDSC+k6brng09jcMOk= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.20 h1:sDSXIrlsFSFJtWKLQS4PUWRvrT580rrnuLydJrCQ/yA= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.20/go.mod h1:WZ/c+w0ofps+/OUqMwWgnfrgzZH1DZO1RIkktICsqnY= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.21 h1:AmoU1pziydclFT/xRV+xXE/Vb8fttJCLRPv8oAkprc0= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.21/go.mod h1:AjUdLYe4Tgs6kpH4Bv7uMZo7pottoyHMn4eTcIcneaY= github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.3/go.mod h1:0dHuD2HZZSiwfJSy1FO5bX1hQ1TxVV1QXXjpn3XUE44= -github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.41 h1:hqcxMc2g/MwwnRMod9n6Bd+t+9Nf7d5qRg7RaXKPd6o= -github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.41/go.mod h1:d1eH0VrttvPmrCraU68LOyNdu26zFxQFjrVSb5vdhog= +github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.43 h1:iLdpkYZ4cXIQMO7ud+cqMWR1xK5ESbt1rvN77tRi1BY= +github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.43/go.mod h1:OgbsKPAswXDd5kxnR4vZov69p3oYjbvUyIRBAAV0y9o= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.9/go.mod h1:AnVH5pvai0pAF4lXRq0bmhbes1u9R8wTE+g+183bZNM= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.24 h1:4usbeaes3yJnCFC7kfeyhkdkPtoRYPa/hTmCqMpKpLI= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.24/go.mod h1:5CI1JemjVwde8m2WG3cz23qHKPOxbpkq0HaoreEgLIY= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.25 h1:s/fF4+yDQDoElYhfIVvSNyeCydfbuTKzhxSXDXCPasU= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.25/go.mod h1:IgPfDv5jqFIzQSNbUEMoitNooSMXjRSDkhXv8jiROvU= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.3/go.mod h1:ssOhaLpRlh88H3UmEcsBoVKq309quMvm3Ds8e9d4eJM= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.24 h1:N1zsICrQglfzaBnrfM0Ys00860C+QFwu6u/5+LomP+o= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.24/go.mod h1:dCn9HbJ8+K31i8IQ8EWmWj0EiIk0+vKiHNMxTTYveAg= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.25 h1:ZntTCl5EsYnhN/IygQEUugpdwbhdkom9uHcbCftiGgA= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.25/go.mod h1:DBdPrgeocww+CSl1C8cEV8PN1mHMBhuCDLpXezyvWkE= github.com/aws/aws-sdk-go-v2/internal/ini v1.3.10/go.mod h1:8DcYQcz0+ZJaSxANlHIsbbi6S+zMwjwdDqwW3r9AzaE= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 h1:VaRN3TlFdd6KxX1x3ILT5ynH6HvKgqdiXoTxAF4HQcQ= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1/go.mod h1:FbtygfRFze9usAadmnGJNc8KsP346kEe+y2/oyhGAGc= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.24 h1:JX70yGKLj25+lMC5Yyh8wBtvB01GDilyRuJvXJ4piD0= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.24/go.mod h1:+Ln60j9SUTD0LEwnhEB0Xhg61DHqplBrbZpLgyjoEHg= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.25 h1:r67ps7oHCYnflpgDy2LZU0MAQtQbYIOqNNnqGO6xQkE= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.25/go.mod h1:GrGY+Q4fIokYLtjCVB/aFfCVL6hhGUFl8inD18fDalE= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.1/go.mod h1:GeUru+8VzrTXV/83XyMJ80KpH8xO89VPoUileyNQ+tc= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 h1:iXtILhvDxB6kPvEXgsDhGaZCSC6LQET5ZHSdJozeI0Y= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1/go.mod h1:9nu0fVANtYiAePIBh2/pFUSwtJ402hLnp854CNoDOeE= github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.3/go.mod h1:Seb8KNmD6kVTjwRjVEgOT5hPin6sq+v4C2ycJQDwuH8= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.5 h1:gvZOjQKPxFXy1ft3QnEyXmT+IqneM9QAUWlM3r0mfqw= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.5/go.mod h1:DLWnfvIcm9IET/mmjdxeXbBKmTCm0ZB8p1za9BVteM8= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.6 h1:HCpPsWqmYQieU7SS6E9HXfdAMSud0pteVXieJmcpIRI= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.6/go.mod h1:ngUiVRCco++u+soRRVBIvBZxSMMvOVMXA4PJ36JLfSw= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.3/go.mod h1:wlY6SVjuwvh3TVRpTqdy4I1JpBFLX4UGeKZdWntaocw= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.5 h1:wtpJ4zcwrSbwhECWQoI/g6WM9zqCcSpHDJIWSbMLOu4= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.5/go.mod h1:qu/W9HXQbbQ4+1+JcZp0ZNPV31ym537ZJN+fiS7Ti8E= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.6 h1:50+XsN70RS7dwJ2CkVNXzj7U2L1HKP8nqTd3XWEXBN4= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.6/go.mod h1:WqgLmwY7so32kG01zD8CPTJWVWM+TzJoOVHwTg4aPug= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.3/go.mod h1:Bm/v2IaN6rZ+Op7zX+bOUMdL4fsrYZiD0dsjLhNKwZc= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.5 h1:P1doBzv5VEg1ONxnJss1Kh5ZG/ewoIE4MQtKKc6Crgg= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.5/go.mod h1:NOP+euMW7W3Ukt28tAxPuoWao4rhhqJD3QEBk7oCg7w= -github.com/aws/aws-sdk-go-v2/service/kinesis v1.32.6 h1:yN7WEx9ksiP5+9zdKtoQYrUT51HvYw+EA1TXsElvMyk= -github.com/aws/aws-sdk-go-v2/service/kinesis v1.32.6/go.mod h1:j8MNat6qtGw5OoEACRbWtT8r5my4nRWfM/6Uk+NsuC4= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.6 h1:BbGDtTi0T1DYlmjBiCr/le3wzhA37O8QTC5/Ab8+EXk= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.6/go.mod h1:hLMJt7Q8ePgViKupeymbqI0la+t9/iYFBjxQCFwuAwI= +github.com/aws/aws-sdk-go-v2/service/kinesis v1.32.7 h1:QTtbqxI+i2gaWjcTwJZtm8/xEl9kiQXXbOatGabNuXA= +github.com/aws/aws-sdk-go-v2/service/kinesis v1.32.7/go.mod h1:5aKZaOb2yfdeAOvfam0/6HoUXg01pN172bn7MqpM35c= github.com/aws/aws-sdk-go-v2/service/kms v1.16.3/go.mod h1:QuiHPBqlOFCi4LqdSskYYAWpQlx3PKmohy+rE2F+o5g= github.com/aws/aws-sdk-go-v2/service/s3 v1.26.3/go.mod h1:g1qvDuRsJY+XghsV6zg00Z4KJ7DtFFCx8fJD2a491Ak= -github.com/aws/aws-sdk-go-v2/service/s3 v1.69.0 h1:Q2ax8S21clKOnHhhr933xm3JxdJebql+R7aNo7p7GBQ= -github.com/aws/aws-sdk-go-v2/service/s3 v1.69.0/go.mod h1:ralv4XawHjEMaHOWnTFushl0WRqim/gQWesAMF6hTow= +github.com/aws/aws-sdk-go-v2/service/s3 v1.71.0 h1:nyuzXooUNJexRT0Oy0UQY6AhOzxPxhtt4DcBIHyCnmw= +github.com/aws/aws-sdk-go-v2/service/s3 v1.71.0/go.mod h1:sT/iQz8JK3u/5gZkT+Hmr7GzVZehUMkRZpOaAwYXeGY= github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.15.4/go.mod h1:PJc8s+lxyU8rrre0/4a0pn2wgwiDvOEzoOjcJUBr67o= github.com/aws/aws-sdk-go-v2/service/sns v1.17.4/go.mod h1:kElt+uCcXxcqFyc+bQqZPFD9DME/eC6oHBXvFzQ9Bcw= github.com/aws/aws-sdk-go-v2/service/sqs v1.18.3/go.mod h1:skmQo0UPvsjsuYYSYMVmrPc1HWCbHUJyrCEp+ZaLzqM= -github.com/aws/aws-sdk-go-v2/service/sqs v1.37.1 h1:39WvSrVq9DD6UHkD+fx5x19P5KpRQfNdtgReDVNbelc= -github.com/aws/aws-sdk-go-v2/service/sqs v1.37.1/go.mod h1:3gwPzC9LER/BTQdQZ3r6dUktb1rSjABF1D3Sr6nS7VU= +github.com/aws/aws-sdk-go-v2/service/sqs v1.37.2 h1:mFLfxLZB/TVQwNJAYox4WaxpIu+dFVIcExrmRmRCOhw= +github.com/aws/aws-sdk-go-v2/service/sqs v1.37.2/go.mod h1:GnvfTdlvcpD+or3oslHPOn4Mu6KaCwlCp+0p0oqWnrM= github.com/aws/aws-sdk-go-v2/service/ssm v1.24.1/go.mod h1:NR/xoKjdbRJ+qx0pMR4mI+N/H1I1ynHwXnO6FowXJc0= github.com/aws/aws-sdk-go-v2/service/sso v1.11.3/go.mod h1:7UQ/e69kU7LDPtY40OyoHYgRmgfGM4mgsLYtcObdveU= -github.com/aws/aws-sdk-go-v2/service/sso v1.24.6 h1:3zu537oLmsPfDMyjnUS2g+F2vITgy5pB74tHI+JBNoM= -github.com/aws/aws-sdk-go-v2/service/sso v1.24.6/go.mod h1:WJSZH2ZvepM6t6jwu4w/Z45Eoi75lPN7DcydSRtJg6Y= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.5 h1:K0OQAsDywb0ltlFrZm0JHPY3yZp/S9OaoLU33S7vPS8= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.5/go.mod h1:ORITg+fyuMoeiQFiVGoqB3OydVTLkClw/ljbblMq6Cc= +github.com/aws/aws-sdk-go-v2/service/sso v1.24.7 h1:rLnYAfXQ3YAccocshIH5mzNNwZBkBo+bP6EhIxak6Hw= +github.com/aws/aws-sdk-go-v2/service/sso v1.24.7/go.mod h1:ZHtuQJ6t9A/+YDuxOLnbryAmITtr8UysSny3qcyvJTc= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.6 h1:JnhTZR3PiYDNKlXy50/pNeix9aGMo6lLpXwJ1mw8MD4= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.6/go.mod h1:URronUEGfXZN1VpdktPSD1EkAL9mfrV+2F4sjH38qOY= github.com/aws/aws-sdk-go-v2/service/sts v1.16.3/go.mod h1:bfBj0iVmsUyUg4weDB4NxktD9rDGeKSVWnjTnwbx9b8= -github.com/aws/aws-sdk-go-v2/service/sts v1.33.1 h1:6SZUVRQNvExYlMLbHdlKB48x0fLbc2iVROyaNEwBHbU= -github.com/aws/aws-sdk-go-v2/service/sts v1.33.1/go.mod h1:GqWyYCwLXnlUB1lOAXQyNSPqPLQJvmo8J0DWBzp9mtg= +github.com/aws/aws-sdk-go-v2/service/sts v1.33.2 h1:s4074ZO1Hk8qv65GqNXqDjmkf4HSQqJukaLuuW0TpDA= +github.com/aws/aws-sdk-go-v2/service/sts v1.33.2/go.mod h1:mVggCnIWoM09jP71Wh+ea7+5gAp53q+49wDFs1SW5z8= github.com/aws/smithy-go v1.11.2/go.mod h1:3xHYmszWVx2c0kIwQeEVf9uSm4fYZt67FBJnwub1bgM= github.com/aws/smithy-go v1.22.1 h1:/HPHZQ0g7f4eUeK6HKglFz8uwVfZKgoI25rb/J+dnro= github.com/aws/smithy-go v1.22.1/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= @@ -427,8 +427,6 @@ github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRx github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt v3.2.1+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= -github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= -github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= github.com/golang-jwt/jwt/v4 v4.4.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= @@ -529,8 +527,8 @@ github.com/google/pprof v0.0.0-20210506205249-923b5ab0fc1a/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af h1:kmjWCqn2qkEml422C2Rrd27c3VGxi6a/6HNq8QmHRKM= -github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo= +github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo= +github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/renameio/v2 v2.0.0 h1:UifI23ZTGY8Tt29JbYFiuyIU3eX+RNFtUwefq9qAhxg= github.com/google/renameio/v2 v2.0.0/go.mod h1:BtmJXm5YlszgC+TD4HOEEUFgkJP3nLxehU6hfe7jRt4= @@ -563,8 +561,8 @@ github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/ad github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.23.0 h1:ad0vkEBuk23VJzZR9nkLVG0YAoN9coASF1GusYX6AlU= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.23.0/go.mod h1:igFoXX2ELCW06bol23DWPB5BEWfZISOzSP5K2sbLea0= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0 h1:TmHmbvxPmaegwhDubVz0lICL0J5Ka2vwTzhoePEXsGE= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0/go.mod h1:qztMSjm835F2bXf+5HKAPIS5qsmQDqZna/PgVt4rWtI= github.com/hanwen/go-fuse v1.0.0/go.mod h1:unqXarDXqzAk0rt98O2tVndEPIpUgLD9+rwFisZH3Ok= github.com/hanwen/go-fuse/v2 v2.1.0/go.mod h1:oRyA5eK+pvJyv5otpO/DgccS8y/RvYMaO00GgRLGryc= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -703,8 +701,8 @@ github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0 github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/labstack/echo-contrib v0.17.1 h1:7I/he7ylVKsDUieaGRZ9XxxTYOjfQwVzHzUYrNykfCU= github.com/labstack/echo-contrib v0.17.1/go.mod h1:SnsCZtwHBAZm5uBSAtQtXQHI3wqEA73hvTn0bYMKnZA= -github.com/labstack/echo/v4 v4.12.0 h1:IKpw49IMryVB2p1a4dzwlhP1O2Tf2E0Ir/450lH+kI0= -github.com/labstack/echo/v4 v4.12.0/go.mod h1:UP9Cr2DJXbOK3Kr9ONYzNowSh7HP0aG0ShAyycHSJvM= +github.com/labstack/echo/v4 v4.13.2 h1:9aAt4hstpH54qIcqkuUXRLTf+v7yOTfMPWzDtuqLmtA= +github.com/labstack/echo/v4 v4.13.2/go.mod h1:uc9gDtHB8UWt3FfbYx0HyxcCuvR4YuPYOxF/1QjoV/c= github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0= github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= @@ -777,10 +775,10 @@ github.com/nxadm/tail v1.4.11 h1:8feyoE3OzPrcshW5/MJ4sGESc5cqmGkGCWlco4l0bqY= github.com/nxadm/tail v1.4.11/go.mod h1:OTaG3NK980DZzxbRq6lEuzgU+mug70nY11sMd4JXXHc= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= -github.com/onsi/ginkgo/v2 v2.19.0 h1:9Cnnf7UHo57Hy3k6/m5k3dRfGTMXGvxhHFvkDTCTpvA= -github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To= -github.com/onsi/gomega v1.27.7 h1:fVih9JD6ogIiHUN6ePK7HJidyEDpWGVB5mzM7cWNXoU= -github.com/onsi/gomega v1.27.7/go.mod h1:1p8OOlwo2iUUDsHnOrjE5UKYJ+e3W8eQ3qSlRahPmr4= +github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM= +github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= +github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4= +github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= @@ -815,8 +813,8 @@ github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/j github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= -github.com/prometheus/common v0.60.1 h1:FUas6GcOw66yB/73KC+BOZoFJmbo/1pojoILArPAaSc= -github.com/prometheus/common v0.60.1/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw= +github.com/prometheus/common v0.61.0 h1:3gv/GThfX0cV2lpO7gkTUwZru38mxevy90Bj8YFSRQQ= +github.com/prometheus/common v0.61.0/go.mod h1:zr29OCN/2BsJRaFwG8QOBr41D6kkchKbpeNH7pAjb/s= github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= github.com/r3labs/diff/v3 v3.0.1 h1:CBKqf3XmNRHXKmdU7mZP1w7TV0pDyVCis1AUHtA4Xtg= @@ -956,61 +954,63 @@ go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= -go.opentelemetry.io/contrib/bridges/prometheus v0.57.0 h1:UW0+QyeyBVhn+COBec3nGhfnFe5lwB0ic1JBVjzhk0w= -go.opentelemetry.io/contrib/bridges/prometheus v0.57.0/go.mod h1:ppciCHRLsyCio54qbzQv0E4Jyth/fLWDTJYfvWpcSVk= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/contrib/bridges/prometheus v0.58.0 h1:gQFwWiqm4JUvOjpdmyU0di+2pVQ8QNpk1Ak/54Y6NcY= +go.opentelemetry.io/contrib/bridges/prometheus v0.58.0/go.mod h1:CNyFi9PuvHtEJNmMFHaXZMuA4XmgRXIqpFcHdqzLvVU= go.opentelemetry.io/contrib/detectors/gcp v1.29.0 h1:TiaiXB4DpGD3sdzNlYQxruQngn5Apwzi1X0DRhuGvDQ= go.opentelemetry.io/contrib/detectors/gcp v1.29.0/go.mod h1:GW2aWZNwR2ZxDLdv8OyC2G8zkRoQBuURgV7RPQgcPoU= -go.opentelemetry.io/contrib/exporters/autoexport v0.57.0 h1:jmTVJ86dP60C01K3slFQa2NQ/Aoi7zA+wy7vMOKD9H4= -go.opentelemetry.io/contrib/exporters/autoexport v0.57.0/go.mod h1:EJBheUMttD/lABFyLXhce47Wr6DPWYReCzaZiXadH7g= -go.opentelemetry.io/contrib/instrumentation/github.com/labstack/echo/otelecho v0.57.0 h1:0q9nZfgQarTPiePf+H4GLNE/9w5yasXMsRFPvTTZI1Q= -go.opentelemetry.io/contrib/instrumentation/github.com/labstack/echo/otelecho v0.57.0/go.mod h1:Fi8pgZRfhlYA6WEVVdeDdRigT/+y7YO8I0C3QXZg1QU= +go.opentelemetry.io/contrib/exporters/autoexport v0.58.0 h1:qVsDVgZd/bC6ZKDOHSjILpm0T/BWvASC9cQU3GYga78= +go.opentelemetry.io/contrib/exporters/autoexport v0.58.0/go.mod h1:bAv7mY+5qTsFPFaRpr75vDOocX09I36QH4Rg0slEG/U= +go.opentelemetry.io/contrib/instrumentation/github.com/labstack/echo/otelecho v0.58.0 h1:DBk8Zh+Yn3WtWCdGSx1pbEV9/naLtjG16c1zwQA2MBI= +go.opentelemetry.io/contrib/instrumentation/github.com/labstack/echo/otelecho v0.58.0/go.mod h1:DFx32LPclW1MNdSKIMrjjetsk0tJtYhAvuGjDIG2SKE= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0 h1:r6I7RJCN86bpD/FQwedZ0vSixDpwuWREjW9oRMsmqDc= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0/go.mod h1:B9yO6b04uB80CzjedvewuqDhxJxi11s7/GtiGa8bAjI= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 h1:TT4fX+nBOA/+LUkobKGW1ydGcn+G3vRw9+g5HwCphpk= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0/go.mod h1:L7UH0GbB0p47T4Rri3uHjbpCFYrVrwc1I25QhNPiGK8= -go.opentelemetry.io/contrib/propagators/b3 v1.32.0 h1:MazJBz2Zf6HTN/nK/s3Ru1qme+VhWU5hm83QxEP+dvw= -go.opentelemetry.io/contrib/propagators/b3 v1.32.0/go.mod h1:B0s70QHYPrJwPOwD1o3V/R8vETNOG9N3qZf4LDYvA30= -go.opentelemetry.io/contrib/samplers/jaegerremote v0.26.0 h1:/SKXyZLAnuj981HVc8G5ZylYK3qD2W6AYR6cJx5kIHw= -go.opentelemetry.io/contrib/samplers/jaegerremote v0.26.0/go.mod h1:cOEzME0M2OKeHB45lJiOKfvUCdg/r75mf7YS5w0tbmE= -go.opentelemetry.io/otel v1.32.0 h1:WnBN+Xjcteh0zdk01SVqV55d/m62NJLJdIyb4y/WO5U= -go.opentelemetry.io/otel v1.32.0/go.mod h1:00DCVSB0RQcnzlwyTfqtxSm+DRr9hpYrHjNGiBHVQIg= -go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.8.0 h1:WzNab7hOOLzdDF/EoWCt4glhrbMPVMOO5JYTmpz36Ls= -go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.8.0/go.mod h1:hKvJwTzJdp90Vh7p6q/9PAOd55dI6WA6sWj62a/JvSs= -go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.8.0 h1:S+LdBGiQXtJdowoJoQPEtI52syEP/JYBUpjO49EQhV8= -go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.8.0/go.mod h1:5KXybFvPGds3QinJWQT7pmXf+TN5YIa7CNYObWRkj50= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.32.0 h1:j7ZSD+5yn+lo3sGV69nW04rRR0jhYnBwjuX3r0HvnK0= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.32.0/go.mod h1:WXbYJTUaZXAbYd8lbgGuvih0yuCfOFC5RJoYnoLcGz8= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.32.0 h1:t/Qur3vKSkUCcDVaSumWF2PKHt85pc7fRvFuoVT8qFU= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.32.0/go.mod h1:Rl61tySSdcOJWoEgYZVtmnKdA0GeKrSqkHC1t+91CH8= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.32.0 h1:IJFEoHiytixx8cMiVAO+GmHR6Frwu+u5Ur8njpFO6Ac= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.32.0/go.mod h1:3rHrKNtLIoS0oZwkY2vxi+oJcwFRWdtUyRII+so45p8= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.32.0 h1:9kV11HXBHZAvuPUZxmMWrH8hZn/6UnHX4K0mu36vNsU= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.32.0/go.mod h1:JyA0FHXe22E1NeNiHmVp7kFHglnexDQ7uRWDiiJ1hKQ= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.32.0 h1:cMyu9O88joYEaI47CnQkxO1XZdpoTF9fEnW2duIddhw= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.32.0/go.mod h1:6Am3rn7P9TVVeXYG+wtcGE7IE1tsQ+bP3AuWcKt/gOI= -go.opentelemetry.io/otel/exporters/prometheus v0.54.0 h1:rFwzp68QMgtzu9PgP3jm9XaMICI6TsofWWPcBDKwlsU= -go.opentelemetry.io/otel/exporters/prometheus v0.54.0/go.mod h1:QyjcV9qDP6VeK5qPyKETvNjmaaEc7+gqjh4SS0ZYzDU= -go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.8.0 h1:CHXNXwfKWfzS65yrlB2PVds1IBZcdsX8Vepy9of0iRU= -go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.8.0/go.mod h1:zKU4zUgKiaRxrdovSS2amdM5gOc59slmo/zJwGX+YBg= -go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.32.0 h1:SZmDnHcgp3zwlPBS2JX2urGYe/jBKEIT6ZedHRUyCz8= -go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.32.0/go.mod h1:fdWW0HtZJ7+jNpTKUR0GpMEDP69nR8YBJQxNiVCE3jk= -go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.32.0 h1:cC2yDI3IQd0Udsux7Qmq8ToKAx1XCilTQECZ0KDZyTw= -go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.32.0/go.mod h1:2PD5Ex6z8CFzDbTdOlwyNIUywRr1DN0ospafJM1wJ+s= -go.opentelemetry.io/otel/log v0.8.0 h1:egZ8vV5atrUWUbnSsHn6vB8R21G2wrKqNiDt3iWertk= -go.opentelemetry.io/otel/log v0.8.0/go.mod h1:M9qvDdUTRCopJcGRKg57+JSQ9LgLBrwwfC32epk5NX8= -go.opentelemetry.io/otel/metric v1.32.0 h1:xV2umtmNcThh2/a/aCP+h64Xx5wsj8qqnkYZktzNa0M= -go.opentelemetry.io/otel/metric v1.32.0/go.mod h1:jH7CIbbK6SH2V2wE16W05BHCtIDzauciCRLoc/SyMv8= -go.opentelemetry.io/otel/sdk v1.32.0 h1:RNxepc9vK59A8XsgZQouW8ue8Gkb4jpWtJm9ge5lEG4= -go.opentelemetry.io/otel/sdk v1.32.0/go.mod h1:LqgegDBjKMmb2GC6/PrTnteJG39I8/vJCAP9LlJXEjU= -go.opentelemetry.io/otel/sdk/log v0.8.0 h1:zg7GUYXqxk1jnGF/dTdLPrK06xJdrXgqgFLnI4Crxvs= -go.opentelemetry.io/otel/sdk/log v0.8.0/go.mod h1:50iXr0UVwQrYS45KbruFrEt4LvAdCaWWgIrsN3ZQggo= -go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU= -go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ= -go.opentelemetry.io/otel/trace v1.32.0 h1:WIC9mYrXf8TmY/EXuULKc8hR17vE+Hjv2cssQDe03fM= -go.opentelemetry.io/otel/trace v1.32.0/go.mod h1:+i4rkvCraA+tG6AzwloGaCtkx53Fa+L+V8e9a7YvhT8= +go.opentelemetry.io/contrib/propagators/b3 v1.33.0 h1:ig/IsHyyoQ1F1d6FUDIIW5oYpsuTVtN16AyGOgdjAHQ= +go.opentelemetry.io/contrib/propagators/b3 v1.33.0/go.mod h1:EsVYoNy+Eol5znb6wwN3XQTILyjl040gUpEnUSNZfsk= +go.opentelemetry.io/contrib/samplers/jaegerremote v0.27.0 h1:Co7WDtZosbvNcG4Nqbs3AEVuHNsN6EMc1/1uGKAvyJk= +go.opentelemetry.io/contrib/samplers/jaegerremote v0.27.0/go.mod h1:IohbtCIY5Erb6wKnDddXOMNlG7GwyZnkrgcqjPmhpaA= +go.opentelemetry.io/otel v1.33.0 h1:/FerN9bax5LoK51X/sI0SVYrjSE0/yUL7DpxW4K3FWw= +go.opentelemetry.io/otel v1.33.0/go.mod h1:SUUkR6csvUQl+yjReHu5uM3EtVV7MBm5FHKRlNx4I8I= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.9.0 h1:gA2gh+3B3NDvRFP30Ufh7CC3TtJRbUSf2TTD0LbCagw= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.9.0/go.mod h1:smRTR+02OtrVGjvWE1sQxhuazozKc/BXvvqqnmOxy+s= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.9.0 h1:Za0Z/j9Gf3Z9DKQ1choU9xI2noCxlkcyFFP2Ob3miEQ= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.9.0/go.mod h1:jMRB8N75meTNjDFQyJBA/2Z9en21CsxwMctn08NHY6c= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.33.0 h1:7F29RDmnlqk6B5d+sUqemt8TBfDqxryYW5gX6L74RFA= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.33.0/go.mod h1:ZiGDq7xwDMKmWDrN1XsXAj0iC7hns+2DhxBFSncNHSE= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.33.0 h1:bSjzTvsXZbLSWU8hnZXcKmEVaJjjnandxD0PxThhVU8= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.33.0/go.mod h1:aj2rilHL8WjXY1I5V+ra+z8FELtk681deydgYT8ikxU= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0 h1:Vh5HayB/0HHfOQA7Ctx69E/Y/DcQSMPpKANYVMQ7fBA= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0/go.mod h1:cpgtDBaqD/6ok/UG0jT15/uKjAY8mRA53diogHBg3UI= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0 h1:5pojmb1U1AogINhN3SurB+zm/nIcusopeBNp42f45QM= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0/go.mod h1:57gTHJSE5S1tqg+EKsLPlTWhpHMsWlVmer+LA926XiA= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0 h1:wpMfgF8E1rkrT1Z6meFh1NDtownE9Ii3n3X2GJYjsaU= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0/go.mod h1:wAy0T/dUbs468uOlkT31xjvqQgEVXv58BRFWEgn5v/0= +go.opentelemetry.io/otel/exporters/prometheus v0.55.0 h1:sSPw658Lk2NWAv74lkD3B/RSDb+xRFx46GjkrL3VUZo= +go.opentelemetry.io/otel/exporters/prometheus v0.55.0/go.mod h1:nC00vyCmQixoeaxF6KNyP42II/RHa9UdruK02qBmHvI= +go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.9.0 h1:iI15wfQb5ZtAVTdS5WROxpYmw6Kjez3hT9SuzXhrgGQ= +go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.9.0/go.mod h1:yepwlNzVVxHWR5ugHIrll+euPQPq4pvysHTDr/daV9o= +go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.33.0 h1:FiOTYABOX4tdzi8A0+mtzcsTmi6WBOxk66u0f1Mj9Gs= +go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.33.0/go.mod h1:xyo5rS8DgzV0Jtsht+LCEMwyiDbjpsxBpWETwFRF0/4= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.33.0 h1:W5AWUn/IVe8RFb5pZx1Uh9Laf/4+Qmm4kJL5zPuvR+0= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.33.0/go.mod h1:mzKxJywMNBdEX8TSJais3NnsVZUaJ+bAy6UxPTng2vk= +go.opentelemetry.io/otel/log v0.9.0 h1:0OiWRefqJ2QszpCiqwGO0u9ajMPe17q6IscQvvp3czY= +go.opentelemetry.io/otel/log v0.9.0/go.mod h1:WPP4OJ+RBkQ416jrFCQFuFKtXKD6mOoYCQm6ykK8VaU= +go.opentelemetry.io/otel/metric v1.33.0 h1:r+JOocAyeRVXD8lZpjdQjzMadVZp2M4WmQ+5WtEnklQ= +go.opentelemetry.io/otel/metric v1.33.0/go.mod h1:L9+Fyctbp6HFTddIxClbQkjtubW6O9QS3Ann/M82u6M= +go.opentelemetry.io/otel/sdk v1.33.0 h1:iax7M131HuAm9QkZotNHEfstof92xM+N8sr3uHXc2IM= +go.opentelemetry.io/otel/sdk v1.33.0/go.mod h1:A1Q5oi7/9XaMlIWzPSxLRWOI8nG3FnzHJNbiENQuihM= +go.opentelemetry.io/otel/sdk/log v0.9.0 h1:YPCi6W1Eg0vwT/XJWsv2/PaQ2nyAJYuF7UUjQSBe3bc= +go.opentelemetry.io/otel/sdk/log v0.9.0/go.mod h1:y0HdrOz7OkXQBuc2yjiqnEHc+CRKeVhRE3hx4RwTmV4= +go.opentelemetry.io/otel/sdk/metric v1.33.0 h1:Gs5VK9/WUJhNXZgn8MR6ITatvAmKeIuCtNbsP3JkNqU= +go.opentelemetry.io/otel/sdk/metric v1.33.0/go.mod h1:dL5ykHZmm1B1nVRk9dDjChwDmt81MjVp3gLkQRwKf/Q= +go.opentelemetry.io/otel/trace v1.33.0 h1:cCJuF7LRjUFso9LPnEAHJDB2pqzp+hbO8eu1qqW2d/s= +go.opentelemetry.io/otel/trace v1.33.0/go.mod h1:uIcdVUZMpTAmz0tI1z04GoVSezK37CbGV4fr1f2nBck= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= -go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= +go.opentelemetry.io/proto/otlp v1.4.0 h1:TA9WRvW6zMwP+Ssb6fLoUIuirti1gGbP28GcKG1jgeg= +go.opentelemetry.io/proto/otlp v1.4.0/go.mod h1:PPBWZIP98o2ElSqI35IHfu7hIhSwvc5N38Jw8pXuGFY= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= @@ -1056,8 +1056,8 @@ golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0 golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= -golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ= -golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg= +golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -1151,8 +1151,8 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo= -golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM= +golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI= +golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1188,8 +1188,8 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ= -golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1275,16 +1275,16 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= -golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= -golang.org/x/term v0.26.0 h1:WEQa6V3Gja/BhNxg540hBip/kkaYtRg3cxg4oXSw4AU= -golang.org/x/term v0.26.0/go.mod h1:Si5m1o57C5nBNQo5z1iq+XDijt21BDBDp2bK0QI8e3E= +golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= +golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1298,8 +1298,8 @@ golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug= -golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1420,8 +1420,8 @@ google.golang.org/api v0.69.0/go.mod h1:boanBiw+h5c3s+tBPgEzLDRHfFLWV0qXxRHz3ws7 google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA= google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8= google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs= -google.golang.org/api v0.209.0 h1:Ja2OXNlyRlWCWu8o+GgI4yUn/wz9h/5ZfFbKz+dQX+w= -google.golang.org/api v0.209.0/go.mod h1:I53S168Yr/PNDNMi5yPnDc0/LGRZO6o7PoEbl/HY3CM= +google.golang.org/api v0.211.0 h1:IUpLjq09jxBSV1lACO33CGY3jsRcbctfGzhj+ZSE/Bg= +google.golang.org/api v0.211.0/go.mod h1:XOloB4MXFH4UTlQSGuNUxw0UT74qdENK8d6JNsXKLi0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -1511,12 +1511,12 @@ google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2 google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= google.golang.org/genproto v0.0.0-20220401170504-314d38edb7de/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20241113202542-65e8d215514f h1:zDoHYmMzMacIdjNe+P2XiTmPsLawi/pCbSPfxt6lTfw= -google.golang.org/genproto v0.0.0-20241113202542-65e8d215514f/go.mod h1:Q5m6g8b5KaFFzsQFIGdJkSJDGeJiybVenoYFMMa3ohI= -google.golang.org/genproto/googleapis/api v0.0.0-20241104194629-dd2ea8efbc28 h1:M0KvPgPmDZHPlbRbaNU1APr28TvwvvdUPlSv7PUvy8g= -google.golang.org/genproto/googleapis/api v0.0.0-20241104194629-dd2ea8efbc28/go.mod h1:dguCy7UOdZhTvLzDyt15+rOrawrpM4q7DD9dQ1P11P4= -google.golang.org/genproto/googleapis/rpc v0.0.0-20241113202542-65e8d215514f h1:C1QccEa9kUwvMgEUORqQD9S17QesQijxjZ84sO82mfo= -google.golang.org/genproto/googleapis/rpc v0.0.0-20241113202542-65e8d215514f/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI= +google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 h1:ToEetK57OidYuqD4Q5w+vfEnPvPpuTwedCNVohYJfNk= +google.golang.org/genproto v0.0.0-20241118233622-e639e219e697/go.mod h1:JJrvXBWRZaFMxBufik1a4RpFw4HhgVtBBWQeQgUj2cc= +google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 h1:CkkIfIt50+lT6NHAVoRYEyAvQGFM7xEwXUUywFvEb3Q= +google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576/go.mod h1:1R3kvZ1dtP3+4p4d3G8uJ8rFk/fWlScl38vanWACI08= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576 h1:8ZmaLZE4XWrtU3MyClkYqqtl6Oegr3235h7jxsDyqCY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576/go.mod h1:5uTbfoYQed2U9p3KIj2/Zzm02PYhndfdmML0qC3q3FU= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -1545,8 +1545,8 @@ google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9K google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= -google.golang.org/grpc v1.68.0 h1:aHQeeJbo8zAkAa3pRzrVjZlbz6uSfeOXlJNQM0RAbz0= -google.golang.org/grpc v1.68.0/go.mod h1:fmSPC5AsjSBCK54MyHRx48kpOti1/jRfOlwEWywNjWA= +google.golang.org/grpc v1.68.1 h1:oI5oTa11+ng8r8XMMN7jAOmWfPZWbYpCFaMUTACxkM0= +google.golang.org/grpc v1.68.1/go.mod h1:+q1XYFJjShcqn0QZHvCyeR4CXPA+llXIeUIfIe00waw= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/grpc/stats/opentelemetry v0.0.0-20240907200651-3ffb98b2c93a h1:UIpYSuWdWHSzjwcAFRLjKcPXFZVVLXGEM23W+NWqipw= google.golang.org/grpc/stats/opentelemetry v0.0.0-20240907200651-3ffb98b2c93a/go.mod h1:9i1T9n4ZinTUZGgzENMi8MDDgbGC5mqTS75JAv6xN3A= @@ -1603,25 +1603,25 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.31.3 h1:umzm5o8lFbdN/hIXbrK9oRpOproJO62CV1zqxXrLgk8= -k8s.io/api v0.31.3/go.mod h1:UJrkIp9pnMOI9K2nlL6vwpxRzzEX5sWgn8kGQe92kCE= -k8s.io/apimachinery v0.31.3 h1:6l0WhcYgasZ/wk9ktLq5vLaoXJJr5ts6lkaQzgeYPq4= -k8s.io/apimachinery v0.31.3/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= -k8s.io/client-go v0.31.3 h1:CAlZuM+PH2cm+86LOBemaJI/lQ5linJ6UFxKX/SoG+4= -k8s.io/client-go v0.31.3/go.mod h1:2CgjPUTpv3fE5dNygAr2NcM8nhHzXvxB8KL5gYc3kJs= +k8s.io/api v0.32.0 h1:OL9JpbvAU5ny9ga2fb24X8H6xQlVp+aJMFlgtQjR9CE= +k8s.io/api v0.32.0/go.mod h1:4LEwHZEf6Q/cG96F3dqR965sYOfmPM7rq81BLgsE0p0= +k8s.io/apimachinery v0.32.0 h1:cFSE7N3rmEEtv4ei5X6DaJPHHX0C+upp+v5lVPiEwpg= +k8s.io/apimachinery v0.32.0/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE= +k8s.io/client-go v0.32.0 h1:DimtMcnN/JIKZcrSrstiwvvZvLjG0aSxy8PxN8IChp8= +k8s.io/client-go v0.32.0/go.mod h1:boDWvdM1Drk4NJj/VddSLnx59X3OPgwrOo0vGbtq9+8= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= -k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag= -k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98= -k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A= -k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f h1:GA7//TjRY9yWGy1poLzYYJJ4JRdzg3+O6e8I+e+8T5Y= +k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f/go.mod h1:R/HEjbvWI0qdfb8viZUeVZm0X6IZnxAydC7YU42CMw4= +k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro= +k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= -sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= -sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= -sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= +sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8= +sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo= +sigs.k8s.io/structured-merge-diff/v4 v4.4.2 h1:MdmvkGuXi/8io6ixD5wud3vOLwc1rj0aNqRlpuvjmwA= +sigs.k8s.io/structured-merge-diff/v4 v4.4.2/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4= sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= diff --git a/internal/flag/internal_flag_test.go b/internal/flag/internal_flag_test.go index 812a63062b0..a069a761ee1 100644 --- a/internal/flag/internal_flag_test.go +++ b/internal/flag/internal_flag_test.go @@ -2556,6 +2556,26 @@ func TestInternalFlag_IsValid(t *testing.T) { errorMsg: "invalid progressive rollout, end variation C does not exist", wantErr: assert.Error, }, + { + name: "should error if rule query is not valid for the parser", + fields: fields{ + Variations: &map[string]*interface{}{ + "A": testconvert.Interface("A"), + "B": testconvert.Interface("B"), + }, + Rules: &[]flag.Rule{ + { + Query: testconvert.String("invalid"), + VariationResult: testconvert.String("A"), + }, + }, + DefaultRule: &flag.Rule{ + VariationResult: testconvert.String("A"), + }, + }, + errorMsg: "invalid query: Invalid rule", + wantErr: assert.Error, + }, } for _, tt := range tests { diff --git a/internal/flag/rule.go b/internal/flag/rule.go index 265e606f2a2..b254dc8e5e8 100644 --- a/internal/flag/rule.go +++ b/internal/flag/rule.go @@ -233,8 +233,8 @@ func (r *Rule) IsValid(defaultRule bool, variations map[string]*interface{}) err } // targeting without query - if !defaultRule && r.Query == nil { - return fmt.Errorf("each targeting should have a query") + if err := r.isQueryValid(defaultRule); err != nil { + return err } // Validate the percentage of the rule @@ -284,6 +284,27 @@ func (r *Rule) IsValid(defaultRule bool, variations map[string]*interface{}) err return nil } +func (r *Rule) isQueryValid(defaultRule bool) error { + if defaultRule { + return nil + } + + if r.Query == nil { + return fmt.Errorf("each targeting should have a query") + } + + // Validate the query with the parser + ev, err := parser.NewEvaluator(r.GetTrimmedQuery()) + if err != nil { + return err + } + _, err = ev.Process(map[string]interface{}{}) + if err != nil { + return fmt.Errorf("invalid query: %w", err) + } + return nil +} + // GetTrimmedQuery is removing the break lines and return func (r *Rule) GetTrimmedQuery() string { splitQuery := strings.Split(r.GetQuery(), "\n") diff --git a/openfeature/provider_tests/dotnet-integration-tests/ProviderTests/ProviderTests.csproj b/openfeature/provider_tests/dotnet-integration-tests/ProviderTests/ProviderTests.csproj index dd40ecd3f16..e745436ca44 100644 --- a/openfeature/provider_tests/dotnet-integration-tests/ProviderTests/ProviderTests.csproj +++ b/openfeature/provider_tests/dotnet-integration-tests/ProviderTests/ProviderTests.csproj @@ -12,11 +12,11 @@ - + - + diff --git a/openfeature/provider_tests/go-integration-tests/go.mod b/openfeature/provider_tests/go-integration-tests/go.mod index bde0377499e..52586b96690 100644 --- a/openfeature/provider_tests/go-integration-tests/go.mod +++ b/openfeature/provider_tests/go-integration-tests/go.mod @@ -3,7 +3,7 @@ module github.com/thomaspoignant/go-feature-flag/openfeature/provider_tests/go-i go 1.22.5 require ( - github.com/open-feature/go-sdk v1.13.1 + github.com/open-feature/go-sdk v1.14.0 github.com/open-feature/go-sdk-contrib/providers/go-feature-flag v0.2.1 github.com/stretchr/testify v1.10.0 ) diff --git a/openfeature/provider_tests/go-integration-tests/go.sum b/openfeature/provider_tests/go-integration-tests/go.sum index f28dc1c48b8..42c70c0cc85 100644 --- a/openfeature/provider_tests/go-integration-tests/go.sum +++ b/openfeature/provider_tests/go-integration-tests/go.sum @@ -11,8 +11,8 @@ github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/open-feature/go-sdk v1.13.1 h1:RJbS70eyi7Jd3Zm5bFnaahNKNDXn+RAVnctpGu+uPis= -github.com/open-feature/go-sdk v1.13.1/go.mod h1:O8r4mhgeRIsjJ0ZBXlnE0BtbT/79W44gQceR7K8KYgo= +github.com/open-feature/go-sdk v1.14.0 h1:+B+Z94QS4HXPAn6OnaWWjMNAJkHlh6pIqW2Y1194yF8= +github.com/open-feature/go-sdk v1.14.0/go.mod h1:t337k0VB/t/YxJ9S0prT30ISUHwYmUd/jhUZgFcOvGg= github.com/open-feature/go-sdk-contrib/providers/go-feature-flag v0.2.1 h1:gSOuM77DaqDRmXRxxYpefHqquafnU+1OoIT7umew6iU= github.com/open-feature/go-sdk-contrib/providers/go-feature-flag v0.2.1/go.mod h1:q2VhNjhNzet8gPQQ3kH9Bwj1v71YaHL3ge7503ll4rE= github.com/open-feature/go-sdk-contrib/providers/ofrep v0.1.4 h1:BElq1EOES8DfLjW6UIFMNVG7w9MzoeC7JpD/1rXouhk= @@ -25,8 +25,8 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM= golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= -golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= -golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= diff --git a/openfeature/provider_tests/java-integration-tests/pom.xml b/openfeature/provider_tests/java-integration-tests/pom.xml index c582c4c949f..f707a12e21e 100644 --- a/openfeature/provider_tests/java-integration-tests/pom.xml +++ b/openfeature/provider_tests/java-integration-tests/pom.xml @@ -18,13 +18,13 @@ org.junit.jupiter junit-jupiter-engine - 5.11.3 + 5.11.4 test dev.openfeature sdk - 1.12.2 + 1.13.0 dev.openfeature.contrib.providers diff --git a/openfeature/providers/python-provider/poetry.lock b/openfeature/providers/python-provider/poetry.lock index dab3561fb4a..d7ddb9c39bf 100644 --- a/openfeature/providers/python-provider/poetry.lock +++ b/openfeature/providers/python-provider/poetry.lock @@ -336,13 +336,13 @@ testing = ["pytest", "pytest-benchmark"] [[package]] name = "pydantic" -version = "2.10.2" +version = "2.10.3" description = "Data validation using Python type hints" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic-2.10.2-py3-none-any.whl", hash = "sha256:cfb96e45951117c3024e6b67b25cdc33a3cb7b2fa62e239f7af1378358a1d99e"}, - {file = "pydantic-2.10.2.tar.gz", hash = "sha256:2bc2d7f17232e0841cbba4641e65ba1eb6fafb3a08de3a091ff3ce14a197c4fa"}, + {file = "pydantic-2.10.3-py3-none-any.whl", hash = "sha256:be04d85bbc7b65651c5f8e6b9976ed9c6f41782a55524cef079a34a0bb82144d"}, + {file = "pydantic-2.10.3.tar.gz", hash = "sha256:cb5ac360ce894ceacd69c403187900a02c4b20b693a9dd1d643e1effab9eadf9"}, ] [package.dependencies] diff --git a/website/versioned_docs/version-v1.40.0/_category_.json b/website/versioned_docs/version-v1.40.0/_category_.json new file mode 100644 index 00000000000..c640b3bdf2d --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/_category_.json @@ -0,0 +1,5 @@ +{ + "position": 0, + "collapsible": true, + "collapsed": false +} diff --git a/website/versioned_docs/version-v1.40.0/configure_flag/_category_.json b/website/versioned_docs/version-v1.40.0/configure_flag/_category_.json new file mode 100644 index 00000000000..93abd20b37d --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/configure_flag/_category_.json @@ -0,0 +1,10 @@ +{ + "position": 20, + "collapsible": true, + "collapsed": true, + "label": "Configure your feature flags", + "link": { + "type": "generated-index", + "title": "Configure your feature flags" + } +} diff --git a/website/versioned_docs/version-v1.40.0/configure_flag/custom_bucketing.md b/website/versioned_docs/version-v1.40.0/configure_flag/custom_bucketing.md new file mode 100644 index 00000000000..dc1a7079ede --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/configure_flag/custom_bucketing.md @@ -0,0 +1,39 @@ +--- +sidebar_position: 22 +description: How to bucket users based on a custom identifier +--- + +# Custom bucketing + +When evaluating flags, the `targetingKey` is usually given a user ID. This key ensures that a user will always be in the same group for each flag. + +Sometimes, it is desireable to _bucket_ users based on a different value. The `bucketingKey` field in the flag configuration allows you to define a different identifier to be used instead. For example: + +```yaml +first-flag: + bucketingKey: "teamId" + variations: + A: false + B: true + defaultRule: # When no targeting match we use the defaultRule + percentage: + A: 50 + B: 50 +``` + +With this flag configuration, the `teamId` value will be used for hashing instead of `targetingKey`. The value must be provided to the evaluation context: + + +```go +user = ffcontext.NewEvaluationContextBuilder("user126") + .AddCustom("teamId", "f74b72") + .Build() + +ffclient.BoolVariation("first-flag", user, false) +``` + +As a result, users who are members of the same team will receive the same flag variation, consistently. A different `bucketingKey` can be used per experiment, though normally you'll only have a handful of possible values. + +This is useful for A/B testing, permissions management and other use cases where targeting a consistent group of users is required. + +**Note**: if a value in the corresponding `bucketingKey` is not found in the evaluation context, the flag rules will not be evaluated, and the SDK will return the default value. \ No newline at end of file diff --git a/website/versioned_docs/version-v1.40.0/configure_flag/export_flags_usage.mdx b/website/versioned_docs/version-v1.40.0/configure_flag/export_flags_usage.mdx new file mode 100644 index 00000000000..37791a6d6b8 --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/configure_flag/export_flags_usage.mdx @@ -0,0 +1,125 @@ +--- +sidebar_position: 40 +description: How to export evaluation data? +--- + + + + + +import {Cards} from '@site/src/components/doc/cardv2'; +import {ConfigCardContent} from "@site/src/components/doc/configCardContent"; +import customlogo from '@site/static/docs/collectors/custom.png'; +import filelogo from '@site/static/docs/collectors/file.png'; +import googlelogo from '@site/static/docs/collectors/google.png'; +import loglogo from '@site/static/docs/collectors/log.png'; +import s3logo from '@site/static/docs/collectors/s3.png'; +import webhooklogo from '@site/static/docs/collectors/webhook.png'; +import sqslogo from '@site/static/docs/collectors/sqs.png'; +import kafkalogo from '@site/static/docs/collectors/kafka.png'; +import pubsublogo from '@site/static/docs/collectors/pubsub.png'; +import kinesislogo from '@site/static/docs/collectors/kinesis.png'; +import azbloblogo from '@site/static/docs/collectors/azblob.png'; + + +# How to export evaluation data +GO Feature Flag allows for the collection of flag usage data. +During flag evaluation, the key, flag variation and other non-sensitive information used are collected and cached for a +configurable period of time. + +The usage data is then written to a file in a chosen format (`parquet`, `JSON` or `CSV`) at a specified interval and +exported to your desired location. This provides a single source for easy processing of the data. The feature can be +configured with options for file format, flush interval, and file location. + +To use, simply configure and use the feature flag as normal, and analyze the collected usage data. + +## Available exporters + + }, + { + logoImg: sqslogo, + title: "AWS SQS", + content: + }, + { + logoImg: kafkalogo, + title: "Kafka", + content: + }, + { + logoImg: googlelogo, + title: "Google Storage", + content: + }, + { + logoImg: pubsublogo, + title: "Google PubSub", + content: + }, + { + logoImg: webhooklogo, + title: "Webhook", + content: + }, + { + logoImg: filelogo, + title: "Local File", + content: + }, + { + logoImg: loglogo, + title: "Log", + content: + }, + { + logoImg: kinesislogo, + title: "AWS Kinesis", + content: + }, + { + logoImg: azbloblogo, + title: "Azure Blob Storage", + content: + }, + { + logoImg: customlogo, + title: "Custom ...", + content: + }, +]}/> diff --git a/website/versioned_docs/version-v1.40.0/configure_flag/flag_format.mdx b/website/versioned_docs/version-v1.40.0/configure_flag/flag_format.mdx new file mode 100644 index 00000000000..83c11ac8ad3 --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/configure_flag/flag_format.mdx @@ -0,0 +1,410 @@ +--- +sidebar_position: 20 +description: What is a flag and how you can create them. +--- + +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; + +# How to configure a flag + +**GO Feature Flag** core feature is to centralize all your feature flags in a source file, and to avoid hosting and maintaining a backend server to manage them. + +Your file must be a valid `YAML`, `JSON` or `TOML` file with a list of flags +_(examples: [`YAML`](https://github.com/thomaspoignant/go-feature-flag/tree/main/testdata/flag-config.yaml), +[`JSON`](https://github.com/thomaspoignant/go-feature-flag/tree/main/testdata/flag-config.json), +[`TOML`](https://github.com/thomaspoignant/go-feature-flag/tree/main/testdata/flag-config.toml))_. + +:::tip +The easiest way to create your configuration file is to used +[**GO Feature Flag Editor** available at [https://editor.gofeatureflag.org](https://editor.gofeatureflag.org). + +If you prefer to do it manually please follow instruction bellow. +::: + +## Editor + +Creating the first version of the flag is not always pleasant, that's why we have created +[**GO Feature Flag Editor**](https://editor.gofeatureflag.org) a simple editor to create your files. + +## Example + +A flag configuration looks like: + + + + +```yaml +# This is your configuration for your first flag +first-flag: + variations: # All possible return value for your feature flag + A: false + B: true + targeting: # If you want to target a subset of your users in particular + - query: key eq "random-key" + percentage: + A: 0 + B: 100 + defaultRule: # When no targeting match we use the defaultRule + variation: A + metadata: + issue_link: https://github.com/thomaspoignant/go-feature-flag/issues/XXX + description: this is my first feature flag + +# A second example of a flag configuration +second-flag: + variations: + A: "valueA" + B: "valueB" + defaultValue: "a default value" + targeting: + - name: rule1 + query: key eq "not-a-key" + percentage: + A: 10 + B: 90 + defaultRule: + name: defaultRule + variation: defaultValue + version: "12" + experimentation: + start: 2021-03-20T00:00:00.1-05:00 + end: 2021-03-21T00:00:00.1-05:00 +``` + + + + +```json +{ + "first-flag": { + "variations": { + "A": false, + "B": true + }, + "targeting": [ + { + "query": "key eq \"random-key\"", + "percentage": { + "A": 0, + "B": 100 + } + } + ], + "defaultRule": { + "variation": "A" + }, + "metadata": { + "issue_link": "https://github.com/thomaspoignant/go-feature-flag/issues/XXX", + "description": "this is my first feature flag" + } + }, + + "second-flag": { + "variations": { + "A": "valueA", + "B": "valueB", + "defaultValue": "a default value" + }, + "targeting": [ + { + "name": "rule1", + "query": "key eq \"not-a-key\"", + "percentage": { + "A": 10, + "B": 90 + } + } + ], + "defaultRule": { + "name": "defaultRule", + "variation": "defaultValue" + }, + "version": "12", + "experimentation": { + "start": "2021-03-20T05:00:00.100Z", + "end": "2021-03-21T05:00:00.100Z" + } + } +} +``` + + + + +```toml +[first-flag.variations] +A = false +B = true + +[[first-flag.targeting]] +query = 'key eq "random-key"' + + [first-flag.targeting.percentage] + A = 0 + B = 100 + +[first-flag.defaultRule] +variation = "A" + +[first-flag.metadata] +issue_link = "https://github.com/thomaspoignant/go-feature-flag/issues/XXX" +description = "this is my first feature flag" + +[second-flag] +version = "12" + + [second-flag.variations] + A = "valueA" + B = "valueB" + defaultValue = "a default value" + + [[second-flag.targeting]] + name = "rule1" + query = 'key eq "not-a-key"' + + [second-flag.targeting.percentage] + A = 10 + B = 90 + + [second-flag.defaultRule] + name = "defaultRule" + variation = "defaultValue" + + [second-flag.experimentation] + start = 2021-03-20T05:00:00.100Z + end = 2021-03-21T05:00:00.100Z +``` + + + + +## Format details + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldDescription
+ flag-key + + Name of your flag. +
+ + It must be unique. +
+ On the example the flag keys are test-flag and{" "} + test-flag2. +
+
+ bucketing-key +
+ (optional) +
+

+ Selects a key from the evaluation context that will be used in place + of the targetingKey for the purpose of evaluating which + variant to serve. + + If bucketingKey is set and the value is missing from the context, the flag will not be evaluated. +

+
+ variations + +

Variations are all the variations available for this flag.

+

+ It is represented as a key/value element. The key is the name of the + variation and the value could be of any types available ( + string, float, int,{" "} + map, array, bool). +

+

You can have as many variations as needed.

+
+          

Some examples:

+
+ variationString: test +
+ variationBool: true +
+ variationInt: 1000 +
+ variationFloat: 1000.23 +
+ variationArray:
- item1 +
- item2 +
+ variationObj: +
item1: 123 +
item2: this is a string +
item3: false +
+
+
+ targeting +
+ (optional) +
+

+ Targeting contains the list of rules you have to target a subset of + your users. +
+ You can have as many target as needed. +

+

+ This field is an array and contains a list of rules. +

+

+ + See rules format to have more info on + how to write a rule. + +

+
+ defaultRule + +

+ DefaultRule is the rule that is applied if the user does not match in + any targeting. +

+

+ + See rules format to have more info on + how to write a rule. + +

+
+ trackEvents +
+ (optional) +
+

+ false if you don't want to export the data in your data + exporter. +

+

+ Default: true +

+
+ disable +
+ (optional) +
+

+ true if the flag is disabled. +

+

+ Default: false +

+
+ version +
+ (optional) +
+

+ The `version` is the version of your flag. +
+ This string is used to display the information in the notifiers and + data collection, you have to update it yourself. +

+

+ Default: "" +

+
+ metadata +
+ (optional) +
+

+ This field allows adding a wealth of information about a particular + feature flag, such as a configuration URL or the originating Jira + issue. +

+
+ scheduledRollout +
+ (optional) +
+

`scheduledRollout` allows to patch your flag over time.

+

+ You can add several steps that update the flag, this is typically + used if you want to gradually add more user in your flag. +

+

+ + See Scheduled rollout to have + more info on how to use it. + +

+
+ experimentation +
+ (optional) +
+

+ Experimentation allows you to configure a start date and an end date + for your flag. When the experimentation is not running, the flag will + serve the default value. +

+

+ + See Experimentation rollout{" "} + to have more info on how to use it. + +

+
+ +## Advanced configurations + +You can have advanced configurations for your flag for them to have specific behavior, such as: + +- [Specific rollout strategies](../category/rollout-strategies/) +- [Don't track a flag](../go_module/data_collection/index.md#dont-track-a-flag) diff --git a/website/versioned_docs/version-v1.40.0/configure_flag/rollout/_category_.json b/website/versioned_docs/version-v1.40.0/configure_flag/rollout/_category_.json new file mode 100644 index 00000000000..0128b230fcf --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/configure_flag/rollout/_category_.json @@ -0,0 +1,11 @@ +{ + "position": 30, + "collapsible": true, + "collapsed": true, + "label": "Rollout strategies", + "link": { + "type": "generated-index", + "title": "Rollout strategies", + "description": "A critical part of every new feature release is orchestrating the actual launch schedule between Product, Engineering, and Marketing teams. Delivering powerful user experiences typically requires software teams to manage complex releases and make manual updates at inconvenient times. But it doesn’t have to! Having a complex rollout strategy allows you to have lifecycle for your flags." + } +} diff --git a/website/versioned_docs/version-v1.40.0/configure_flag/rollout/canary.mdx b/website/versioned_docs/version-v1.40.0/configure_flag/rollout/canary.mdx new file mode 100644 index 00000000000..cca4d50e6be --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/configure_flag/rollout/canary.mdx @@ -0,0 +1,68 @@ +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +# Canary Release + +**Canary release** is a technique to reduce the risk of introducing a new software version in production by slowly rolling out the change to a small subset of users before rolling it out to the entire infrastructure and making it available to everybody. + +This is the easiest rollout strategy available. +You just have to select a percentage of your users in your flag, and the `True` behavior will apply to them. + +## Example + + + + +```yaml +canary-flag: + variations: + oldBehavior: false + canary: true + defaultRule: + # highlight-start + percentage: + oldBehavior: 99 + canary: 1 + # highlight-end +``` + + + + +```json + { + "canary-flag": { + "variations": { + "oldBehavior": false, + "canary": true + }, + "defaultRule": { +# highlight-start + "percentage": { + "oldBehavior": 99, + "canary": 1 + } +# highlight-end + } + } +} +``` + + + + + +```toml +[canary-flag.variations] +oldBehavior = false +canary = true + +# highlight-start +[canary-flag.defaultRule.percentage] +oldBehavior = 99 +canary = 1 +# highlight-end +``` + + + diff --git a/website/versioned_docs/version-v1.40.0/configure_flag/rollout/experimentation.mdx b/website/versioned_docs/version-v1.40.0/configure_flag/rollout/experimentation.mdx new file mode 100644 index 00000000000..f560898c68e --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/configure_flag/rollout/experimentation.mdx @@ -0,0 +1,107 @@ +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +# Experimentation rollout / A/B Testing +An **experimentation rollout** is when your flag is configured to be served only for a determined time. + +1. It means that before the rollout start date, the `default` value is served to all users. +2. Between the dates the flag is evaluated. +3. After the end date the `default` value is served to all users. + +## Example + + + + +```yaml +experimentation-flag: + variations: + variationA: A + variationB: B + defaultRule: + percentage: + variationA: 50 + variationB: 50 + # highlight-start + experimentation: + start: 2021-03-20T00:00:00.1-05:00 + end: 2021-03-21T00:00:00.1-05:00 + # highlight-end +``` + + + + +```json +{ + "experimentation-flag": { + "variations": { + "variationA": "A", + "variationB": "B" + }, + "defaultRule": { + "percentage": { + "variationA": 50, + "variationB": 50 + } + }, +# highlight-start + "experimentation": { + "start": "2021-03-20T05:00:00.100Z", + "end": "2021-03-21T05:00:00.100Z" + }, +# highlight-end + } +} +``` + + + + +```toml +[experimentation-flag.variations] +variationA = "A" +variationB = "B" + +[experimentation-flag.defaultRule.percentage] +variationA = 50 +variationB = 50 + +# highlight-start +[experimentation-flag.experimentation] +start = 2021-03-20T05:00:00.100Z +end = 2021-03-21T05:00:00.100Z +# highlight-end +``` + + + + +Check this [example](https://github.com/thomaspoignant/go-feature-flag/tree/main/examples/rollout_experimentation) to see how it works. + +## Configuration fields + +:::info +The dates are in the format supported natively by your flag file format. +::: + +| Field | Description | +|-------------|-------------------------------------------------| +| **`start`** | The date the flag will be started to be served. | +| **`end`** | The date the flag will be stopped to be served. | + +## A/B testing + +:::info +A/B test is the shorthand for a simple controlled experiment. +As the name implies, two versions (A and B) of a single variable are compared, which are identical except for one variation that might affect a user's behavior. +A/B tests are widely considered the simplest form of controlled experiment. + +_**(source wikipedia)**_ +::: + +To have a proper A/B testing solution with the module you should use the experimentation rollout combined with the [export of your data](../../go_module/data_collection/). + +This combination will allow to have your experimentation running for a dedicated time, and you will have the data to know exactly which user was on which version of the flag. + +To setup the duration of your A/B test you can use a tool [ab-test-duration-calculator](https://vwo.com/tools/ab-test-duration-calculator/) from vwo, that will help you to set up the test duration correctly. diff --git a/website/versioned_docs/version-v1.40.0/configure_flag/rollout/progressive.mdx b/website/versioned_docs/version-v1.40.0/configure_flag/rollout/progressive.mdx new file mode 100644 index 00000000000..e54990c62b9 --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/configure_flag/rollout/progressive.mdx @@ -0,0 +1,164 @@ +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +# Progressive rollout + +A **progressive rollout** allows you to change the value of your flag over time. + +## Example + + + + +```yaml +progressive-flag: + variations: + variationA: A + variationB: B + defaultRule: +# highlight-start + progressiveRollout: + initial: + variation: variationA + date: 2024-01-01T05:00:00.100Z + end: + variation: variationB + date: 2024-01-05T05:00:00.100Z +# highlight-end +``` + + + + +```json +{ + "progressive-flag": { + "variations": { + "variationA": "A", + "variationB": "B" + }, + "defaultRule": { +# highlight-start + "progressiveRollout": { + "initial": { + "variation": "variationA", + "date": "2024-01-01T05:00:00.100Z" + }, + "end": { + "variation": "variationB", + "date": "2024-01-05T05:00:00.100Z" + } + }, +# highlight-end + } + } +} +``` + + + + +```toml +[progressive-flag.variations] +variationA = "A" +variationB = "B" +# highlight-start +[progressive-flag.defaultRule.progressiveRollout.initial] +variation = "variationA" +date = 2024-01-01T05:00:00.100Z + +[progressive-flag.defaultRule.progressiveRollout.end] +variation = "variationB" +date = 2024-01-05T05:00:00.100Z +# highlight-end +``` + + + + +- Before the `initial` date, the flag will return variationA +- Between the `initial` and `end` date, the flag will gradually shift to return variationB instead of variationA. +- After the `end` date, the flag will return variationB. + +### Using the percentage field (advanced) +If you intend to partially rollout or keep both features. + + + + +```yaml + progressiveRollout: + initial: + variation: variationA + # highlight-start + percentage: 20 + # highlight-end + date: 2024-01-01T05:00:00.100Z + end: + variation: variationB + # highlight-start + percentage: 80 + # highlight-end + date: 2024-01-05T05:00:00.100Z +``` + + + + +```json + "progressiveRollout": { + "initial": { + "variation": "variationA", + # highlight-start + "percentage": 20, + # highlight-end + "date": "2024-01-01T05:00:00.100Z" + }, + "end": { + "variation": "variationB", + # highlight-start + "percentage": 80, + # highlight-end + "date": "2024-01-05T05:00:00.100Z" + } + } +``` + + + + +```toml +[progressive-flag.defaultRule.progressiveRollout.initial] +variation = "variationA" +# highlight-start +percentage = 20 +# highlight-end +date = 2024-01-01T05:00:00.100Z + +[progressive-flag.defaultRule.progressiveRollout.end] +variation = "variationB" +# highlight-start +percentage = 80 +# highlight-end +date = 2024-01-05T05:00:00.100Z +``` + + + + +- Before the `initial` date, the flag will return **variationB 20% of the time** and **variationA 80% of the time**. +- Between the `initial` and `end` date, the flag will gradually shift to return variationB more instead of variationA. +- After the `end` date, the flag will return **variationB 80% of the time** and **variationA 20% of the time**. +- This may not be intuitive. It starts with variationA as the dominant response and gradually shifts towards variationB. + +## Configuration fields + +:::info +The dates are in the format supported natively by your flag file format. +::: + +| Field | Description | +|------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------| +| **`initial`** | The initial state of this flag.
`variation` is the variation you intend to switch from.
`date` is the date to start the rollout.
| +| **`end`** | The end state of this flag.
`variation` is the variation you intend to switch to.
`date` is the date when rollout is completed.
| +| **`percentage`**
*(optional)* | It represents the ramp of progress, at which level the flag starts (`initial`) and ends (`end`).
**Default: `initial` = `0` and `end` = `100`**. | \ No newline at end of file diff --git a/website/versioned_docs/version-v1.40.0/configure_flag/rollout/scheduled.mdx b/website/versioned_docs/version-v1.40.0/configure_flag/rollout/scheduled.mdx new file mode 100644 index 00000000000..4df98a0879e --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/configure_flag/rollout/scheduled.mdx @@ -0,0 +1,138 @@ +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +# Scheduled rollout + +Scheduling introduces the ability for users to changes their flags for future points in time. +While this sounds deceptively straightforward, it unlocks the potential for users to create complex release strategies by scheduling the incremental steps in advance. + +For example, you may want to turn a feature ON for internal testing tomorrow and then enable it for your ‘beta’ user segment four days later. + +## Example + + + + +```yaml +scheduled-flag: + variations: + variationA: A + variationB: B + defaultRule: + name: defaultRule + percentage: + variationA: 100 + variationB: 0 +# highlight-start + scheduledRollout: + - date: 2020-04-10T00:00:00.1+02:00 + targeting: + - name: rule1 + query: beta eq "true" + percentage: + variationA: 0 + variationB: 100 + + - date: 2022-05-12T15:36:00.1+02:00 + targeting: + - name: rule1 + query: beta eq "false" +# highlight-end +``` + + + + +```json +{ + "scheduled-flag": { + "variations": { + "variationA": "A", + "variationB": "B" + }, + "defaultRule": { + "name": "defaultRule", + "percentage": { + "variationA": 100, + "variationB": 0 + } + }, + # highlight-start + "scheduledRollout": [ + { + "date": "2020-04-09T22:00:00.100Z", + "targeting": [ + { + "name": "rule1", + "query": "beta eq \"true\"", + "percentage": { + "variationA": 0, + "variationB": 100 + } + } + ] + }, + { + "date": "2022-05-12T13:36:00.100Z", + "targeting": [ + { + "name": "rule1", + "query": "beta eq \"false\"" + } + ] + } + ], + # highlight-end + } +} +``` + + + + +```toml +[scheduled-flag.variations] +variationA = "A" +variationB = "B" + +[scheduled-flag.defaultRule] +name = "defaultRule" + +[scheduled-flag.defaultRule.percentage] +variationA = 100 +variationB = 0 + +# highlight-start +[[scheduled-flag.scheduledRollout]] +date = 2020-04-09T22:00:00.100Z + +[[scheduled-flag.scheduledRollout.targeting]] +name = "rule1" +query = 'beta eq "true"' + +[scheduled-flag.scheduledRollout.targeting.percentage] +variationA = 0 +variationB = 100 + +[[scheduled-flag.scheduledRollout]] +date = 2022-05-12T13:36:00.100Z + +[[scheduled-flag.scheduledRollout.targeting]] +name = "rule1" +query = 'beta eq "false"' +# highlight-end +``` + + + + +## Configuration fields + +:::info +You can change any fields that are available on your flag. +Since your configuration has not been changed manually, it does not trigger any notifier. +::: + +| Field | Description | +|-------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| **`steps`** | The only mandatory field in a **step** is the `date`.
**If no date is provided the step will be skipped.**

The other attributes of your `step` are what you want to update your flag, so every field available in the [flag format](../flag_format) can be updated.
The new value in a field will override the existing one. | diff --git a/website/versioned_docs/version-v1.40.0/configure_flag/rule_format.md b/website/versioned_docs/version-v1.40.0/configure_flag/rule_format.md new file mode 100644 index 00000000000..0126a04fd65 --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/configure_flag/rule_format.md @@ -0,0 +1,183 @@ +--- +sidebar_position: 21 +description: How to create a rule to target specific users +--- + +# How to target specific users + +## Evaluation Context +An evaluation context in a feature flagging system is crucial for determining the output of a feature flag evaluation. +It's a collection of pertinent data about the conditions under which the evaluation is being made. +his data can be supplied through a mix of static information _(server name, IP, etc ...)_ and dynamic inputs +_(information about the user performing the action, etc ...)_, along with state information that is implicitly carried +through the execution of the program. + +When using GO Feature Flag, it's often necessary to personalize the experience for different users. +This is where the concept of a **targeting key** comes into play. +A targeting key is a unique identifier that represents the context of the evaluation _(email, session id, a fingerprint or anything that is consistent)_, +ensuring that they are consistently exposed to the same variation of a feature, even across multiple visits or sessions. + +For instance, **GO Feature Flag** ensures that in cases where a feature is being rolled out to a percentage of users, based on the targeting key, they will see the same variation each time they encounter the feature flag. + +The targeting key is a fundamental part of the evaluation context because it directly affects the determination of which feature variant is served to a particular user, and it maintains that continuity over time. + +### Reserved properties in the evaluation context +When you create an evaluation context some fields are reserved for GO Feature Flag. +Those fields are used by GO Feature Flag directly, you can use them as will but you should be aware that they are used by GO Feature Flag. + +| Field | Description | +|---------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `gofeatureflag.currentDateTime` | If this property is set, we will use this date as base for all the rollout strategies which implies dates _(experimentation, progressive and scheduled)_.
**Format:** Date following the RF3339 format. | +| `gofeatureflag.flagList` | If this property is set, in the bulk evaluation mode (for the client SDK) we will only evaluate the flags in this list.
If empty or not set the default behavior is too evaluate all the flags.
**Format:** []string | + +## Rule format + +A rule is a configuration that allows to serve a variation based on some conditions. + +### Format details + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldDescription
name
(optional)
Name of your rule.
This is needed when your are updating a rule using a scheduled rollout.
query +

+ Query represents an antlr query in the nikunjy/rules format. +
This field is mandatory in every rule used in the targeting field. +

+

See query format to have the syntax.

+

Note: if you use the field query in a defaultRule it will be ignored.

+
variation
(optional)
Name of the variation to return.
percentage
(optional)
+

Represents the percentage we should give to each variation.

+
+            percentage:
variationA: 10.59
variationB: 9.41
variationC: 80 +
+

The format is the name of the variation and the percentage for this one.

+
progressiveRollout
(optional)
+

+ Allows you to ramp up the percentage of your flag over time. +

+

+ You can decide at which percentage you start and end with in your release ramp. + Before the start date we will serve the initial percentage and afterwards, we will serve the end percentage. +

+

See progressive rollout to have more info on how to use it.

+
disable
(optional)
+

Set to true if you want to disable the rule.

+

Default: true.

+
+ + +:::info +`variation`, `percentage` and `progressiveRollout` are optional but you **must have at least one of the three**. + +If you have more than one field we will use the first one in the order +`progressiveRollout` > `percentage` > `variation`. +::: + +### Query format + +The rule format is based on the [`nikunjy/rules`](https://github.com/nikunjy/rules) library. + +All the operations can be written in capitalized or lowercase characters (ex: `eq` or `EQ` can be used). +Logical Operations supported are `AND` & `OR`. + +Compare Expression and their definitions (`a|b` means you can use one of either `a` or `b`): + +| Operator | Description | +|:----------:|-----------------------------| +| `eq`, `==` | equals to | +| `ne`, `!=` | not equals to | +| `lt`, `<` | less than | +| `gt`, `>` | greater than | +| `le`, `<=` | less than equal to | +| `ge`, `>=` | greater than equal to | +| `co` | contains | +| `sw` | starts with | +| `ew` | ends with | +| `in` | in a list | +| `pr` | present | +| `not` | not of a logical expression | + +#### Examples + +- Select a specific user: `key eq "example@example.com"` +- Select all identified users: `anonymous ne true` +- Select a user with a custom property: `userId eq "12345"` +- Select on multiple criteria: + *All users with ids finishing by `@test.com` that have the role `backend engineer` in the `pro` environment for the + company `go-feature-flag`* + + ```bash + (key ew "@test.com") and (role eq "backend engineer") and (env eq "pro") and (company eq "go-feature-flag") + ``` + +## Environments + +When you initialise `go-feature-flag` you can set an [environment](../go_module/configuration/#option_environment) for the instance of this SDK. + +```go linenums="1" +ffclient.Init(ffclient.Config{ + // ... + Environment: "prod", + // ... +}) +``` + +When an environment is set, it adds a new field in your user called **`env`** that you can use in your queries. +It means that you can decide to activate a flag only for some **environment**. + +**Example of flag configuration based on the environment:** + +```yaml +my-flag: + variations: + A: "A" + B: "B" + C: "C" + targeting: + - name: Target pre environment + query: env eq "pre" + variation: A + - name: Target pro environment + query: env eq "pro" + variation: B + defaultRule: + variation: C +``` + +## Get the rule name in the metadata + +When you use a rule in your targeting, you can get the name of the rule in the metadata of the variation. +The information on what rule has been used to serve the variation is available in the metadata of the variation in the field called `evaluatedRuleName`. + +If you are interested about this information, you have to name your rules by adding the field `name` in your rule. This name will be extract and added in the `evaluatedRuleName` field of the metadata. diff --git a/website/versioned_docs/version-v1.40.0/configure_flag/store_your_flags.mdx b/website/versioned_docs/version-v1.40.0/configure_flag/store_your_flags.mdx new file mode 100644 index 00000000000..759688907a0 --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/configure_flag/store_your_flags.mdx @@ -0,0 +1,158 @@ +--- +sidebar_position: 10 +description: Where to store your flags' configuration? +--- + + + +import {Cards} from '@site/src/components/doc/cardv2'; +import {ConfigCardContent} from "@site/src/components/doc/configCardContent"; +import httplogo from '@site/static/docs/retrievers/http.png'; +import customlogo from '@site/static/docs/retrievers/custom.png'; +import filelogo from '@site/static/docs/retrievers/file.png'; +import githublogo from '@site/static/docs/retrievers/github.png'; +import gitlablogo from '@site/static/docs/retrievers/gitlab.png'; +import googlelogo from '@site/static/docs/retrievers/google.png'; +import s3logo from '@site/static/docs/retrievers/s3.png'; +import k8slogo from '@site/static/docs/retrievers/k8s.png'; +import mongodblogo from '@site/static/docs/retrievers/mongodb.png'; +import redislogo from '@site/static/docs/retrievers/redis.png'; +import bitbucketlogo from '@site/static/docs/retrievers/bitbucket.png'; +import azBloblogo from '@site/static/docs/collectors/azblob.png'; + + +# Where to store your flags' configuration? +**GO Feature Flag** is a tool that makes it easy to implement feature flags in your application. One of the benefits of +using GO Feature Flag is that it is designed to be simple and lightweight. +To achieve this, the solution offers a variety of approach to store your flags' configuration. + +The easiest way to get started with GO Feature Flag is to store your flags' configuration in a file. +For this you can create one or more file(s) _(`YAML`, `TOML` or `JSON`)_ that contain(s) your feature +flags and their values. You can then upload this file(s) where you want, and GO Feature Flag will use it. +The way the solution achieves this is through the use of retrievers, which allow you to load your feature flag +configuration file from various sources. + +**Retrievers** are a core concept in GO Feature Flag. They are responsible for loading your feature flag configuration +from a specified location, such as a file on your local machine or a remote storage service. This allows you to +store your configuration in a location that works best for your workflow. + +**GO Feature Flag** supports a variety of retrievers out of the box, including `S3`, `Google Cloud Storage`, `Github`, +`HTTP`, `Kubernetes config maps`, `Local file` ... +But you can also implement your own custom retriever if needed. + +Using retrievers in **GO Feature Flag** is straightforward. You specify which retriever to use in your configuration +file, along with any required configuration options. GO Feature Flag will then use the specified retriever to load your +configuration and will evaluate your feature flags based on this configuration. + +## Available retrievers + + + }, + { + logoImg: s3logo, + title: "AWS S3", + content: + }, + { + logoImg: googlelogo, + title: "Google Storage", + content: + }, + { + logoImg: httplogo, + title: "HTTP/HTTPS", + content: + }, + { + logoImg: githublogo, + title: "GitHub", + content: + }, + { + logoImg: gitlablogo, + title: "Gitlab", + content: + }, + { + logoImg: filelogo, + title: "Local File", + content: + }, + { + logoImg: mongodblogo, + title: "MongoDB", + content: + }, + { + logoImg: redislogo, + title: "Redis", + content: + }, + { + logoImg: bitbucketlogo, + title: "Bitbucket", + content: + }, + { + logoImg: azBloblogo, + title: "Azure Blob Storage", + content: + }, + { + logoImg: customlogo, + title: "Custom ...", + content: + }, +]}/> + + + +## Using multiple retrievers +Sometimes, you might need to store your feature flags in different locations. +In such cases, you can configure multiple retrievers to retrieve the flags from different sources within your GO Feature +Flag instance. + +To set this up, you need to configure the [`Retrievers`](../go_module/configuration#configuration-fields) field to consume from different retrievers. +What this does is that it calls all the retrievers in parallel and applies them in the order you have provided. + +Keep in mind that if a flag is defined in multiple retrievers, it can be overridden by a later flag. For instance, +if you have a flag named _`my-feature-flag`_ in the first file and another flag with the same name in the second file, the second configuration will take precedence. diff --git a/website/versioned_docs/version-v1.40.0/experimental/_category_.json b/website/versioned_docs/version-v1.40.0/experimental/_category_.json new file mode 100644 index 00000000000..6d9f8d64449 --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/experimental/_category_.json @@ -0,0 +1,6 @@ +{ + "position": 200, + "label":"Experimental Features", + "collapsible": true, + "collapsed": true +} diff --git a/website/versioned_docs/version-v1.40.0/experimental/ofrep.md b/website/versioned_docs/version-v1.40.0/experimental/ofrep.md new file mode 100644 index 00000000000..4dadb11a049 --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/experimental/ofrep.md @@ -0,0 +1,30 @@ +# OpenFeature Remote Evaluation Protocol (OFREP) + +![Experimental](https://img.shields.io/badge/Status-Experimental-red.svg) +⚠️ Note that this a work in progress and the protocol is subject to change. ⚠️ + +OpenFeature Remote Flag Evaluation Protocol is an API specification for feature flagging that allows the use of generic +providers to connect to any feature flag management systems that supports the protocol. + +Currently, the protocol is in the early stages of development and is not yet ready for production use, but GO Feature Flag +is supporting the protocol and is the first implementation of the protocol. +We are part of the leading team in the protocol, and we try to follow the specification during the early stages of the protocol +to allow people to try it and be able to develop the providers. + +## How to test it? + +The OFREP implementation is part of the GO Feature Flag Relay Proxy. +We have a new API endpoints `/ofrep/v1/evaluate/flags/{key}` and `/ofrep/v1/evaluate/flags` that you can use to test the protocol. + +You just have to start the GO Feature Flag Relay Proxy (starting from version `v1.24.0`) and use the API to evaluate your flags. +For this, follow the instruction on how to use the relay-proxy [here](../relay_proxy/getting_started.md). + +### Want to start even faster? +```shell +curl https://raw.githubusercontent.com/thomaspoignant/go-feature-flag/main/cmd/relayproxy/testdata/dockerhub-example/goff-proxy.yaml -o goff-proxy.yaml +docker run -p 1031:1031 -v $(pwd)/goff-proxy.yaml:/goff/goff-proxy.yaml thomaspoignant/go-feature-flag:latest +``` + +This will launch a GO Feature Flag Relay Proxy with a configuration file that will retrieve the flags from the test server. + +Swagger is enabled, so you can directly go to http://localhost:1031/swagger/index.html to test the OFREP endpoints (the API Key to use is `apikey1`). diff --git a/website/versioned_docs/version-v1.40.0/faq.md b/website/versioned_docs/version-v1.40.0/faq.md new file mode 100644 index 00000000000..18da7977bca --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/faq.md @@ -0,0 +1,52 @@ +--- +sidebar_position: 100 +--- +# Frequently Asked Questions + +### Why Use Feature Flags? +This one of most common question I get. +Feature flags are a software development technique that allows the toggling of specific functionalities on and off at runtime without the need to deploy new code. + +It allows you to decouple **deploy** and **release**, giving you better control and more experimentation over the full lifecycle of features. + +--- + +### What is the lifecycle of a flag? +Managing the lifecycle of feature flags is crucial to prevent cluttering your codebase with obsolete elements. Here's a step-by-step guide: + +1. **Creation**: Initiate by adding the flag to your configuration file, setting it to 0% to avoid impacting users. +2. **Evaluation**: Implement the flag evaluation in your code (refer to [variation](./go_module/target_user.md#variation)). +3. **Deployment**: Deploy your application with the variation check in place. +4. **Rollout**: Gradually enable the flag for users. +5. **Completion**: Once the feature reaches 100% visibility, eliminate the variation call from your code. +6. **Clean-Up**: Deploy your application sans the variation check. +7. **Removal**: Finally, delete the flag from your configuration file. + +--- + +### What happens if my configuration file is not reachable/deleted? +If while you are on production and for some reason your flag file becomes unreachable, we will be able to serve the users based on the last version of the file we were able to read. We will continue to try reading the file based on the `pollingInterval` you have configured. + +If you start a new instance and the file is not reachable to module, it will fail to initialize except if you have set the option `StartWithRetrieverError` in the config. With this option, we will serve the SDK the default value *(the 3rd param in your variation)* until the flag becomes available again. + +--- + +### What is the best rollout strategy? +The lib offers numerous rollout strategies, with no single "best" approach as it heavily depends on the context of your feature release. +Some strategies include: + +- **Simple Cut-Off**: For non-critical releases, transitioning the flag from 0% to 100% immediately for all users might be suitable. +- **Progressive Rollout**: For releases that might impact infrastructure, a gradual rollout can mitigate risks by incrementally increasing user exposure. +- **Targeted Release**: To affect only a specific user segment, applying rules to your flag can be effective. + +You have an endless list of rollout strategies depending on what is your feature. + +--- + +### How do we ensure that users affected by the feature flags are not always the same? + +To avoid always have the same users getting affected by a flag, we compute the hash that allows us to determine if the user is part of the percentage that is not computed only based on the user key but a combination of the user key and the flag name. + +It guarantees that the user will be always in the same group but depending on the flag. + +--- diff --git a/website/versioned_docs/version-v1.40.0/getting_started/_category_.json b/website/versioned_docs/version-v1.40.0/getting_started/_category_.json new file mode 100644 index 00000000000..3bbc107bfb3 --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/getting_started/_category_.json @@ -0,0 +1,12 @@ +{ + "position": 10, + "label":"Getting Started", + "collapsible": true, + "collapsed": true, + "link": { + "type": "generated-index", + "title": "Getting Started", + "description": "GO Feature Flag can be used in 2 different ways. You can use it as a GO module directly inside your code or by using an Open-feature SDK. To use the Open-feature SDKs, you will need to deploy a relay proxy inside your architecture." + + } +} diff --git a/website/versioned_docs/version-v1.40.0/getting_started/using-go-module.md b/website/versioned_docs/version-v1.40.0/getting_started/using-go-module.md new file mode 100644 index 00000000000..736dc4db918 --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/getting_started/using-go-module.md @@ -0,0 +1,62 @@ +--- +sidebar_position: 20 +description: Use the module in your GO application with nothing to install. +--- +# Using the GO module + +## Installation +```bash +go get github.com/thomaspoignant/go-feature-flag +``` + +## Create a feature flag configuration + +Create a new `YAML` file containing your first flag configuration. + +```yaml title="flag-config.goff.yaml" +# 20% of the users will use the variation "my-new-feature" +test-flag: + variations: + my-new-feature: true + my-old-feature: false + defaultRule: + percentage: + my-new-feature: 20 + my-old-feature: 80 +``` + +This flag split the usage of this flag, 20% will use the variation `my-new-feature` and 80% the variation `my-old-feature`. + +## SDK Initialisation +First, you need to initialize the `ffclient` with the location of your backend file. +```go linenums="1" +err := ffclient.Init(ffclient.Config{ + PollingInterval: 3 * time.Second, + Retriever: &fileretriever.Retriever{ + Path: "flag-config.goff.yaml", + }, +}) +defer ffclient.Close() +``` +*This example will load a file from your local computer and will refresh the flags every 3 seconds (if you omit the +PollingInterval, the default value is 60 seconds).* + +:::tip +This is a basic configuration to test locally, in production it is better to use a remote place to store your feature flag configuration file. + +Look at the list of available options in the [**Store your feature flag file** page](../go_module/store_file/). +::: + +## Evaluate your flags +Now you can evaluate your flags anywhere in your code. + +```go linenums="1" +user := ffcontext.NewEvaluationContext("user-unique-key") +hasFlag, _ := ffclient.BoolVariation("test-flag", user, false) +if hasFlag { + // flag "test-flag" is true for the user +} else { + // flag "test-flag" is false for the user +} +``` +You can find more examples in the [examples/](https://github.com/thomaspoignant/go-feature-flag/tree/main/examples) directory. diff --git a/website/versioned_docs/version-v1.40.0/getting_started/using-openfeature.md b/website/versioned_docs/version-v1.40.0/getting_started/using-openfeature.md new file mode 100644 index 00000000000..776fb0c7304 --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/getting_started/using-openfeature.md @@ -0,0 +1,131 @@ +--- +sidebar_position: 10 +description: Deploy the relay proxy and use the OpenFeature SDKs +--- +# Using Open Feature SDKs + +:::note +OpenFeature provides a shared, standardized feature flagging client - _an SDK_ - which can be plugged into various 3rd-party feature flagging providers. +Whether you're using an open-source system or a commercial product, whether it's self-hosted or cloud-hosted, OpenFeature provides a consistent, unified API for developers to use feature flagging in their applications. +_[Documentation](https://docs.openfeature.dev)_ +::: +GO Feature Flag is committed to **opensource** principles and **standardization**. To achieve this, we've chosen to rely solely on [OpenFeature](https://docs.openfeature.dev) and avoid building custom SDKs. + +To seamlessly integrate with OpenFeature, we provide a lightweight, self-hosted API server called the **relay proxy**. This proxy leverages the core GO Feature Flag module internally. By deploying the relay proxy in your infrastructure, you can utilize Open Feature SDKs in conjunction with GO Feature Flag providers to evaluate your feature flags. + +For a comprehensive understanding of how Open Feature operates, please refer to the official **[Open Feature documentation](https://docs.openfeature.dev)**. + +## Integration pattern + + + +## Create a feature flag configuration + +Create a new `YAML` file containing your first flag configuration. + +```yaml title="flag-config.goff.yaml" +# only admins will eval to true +flag-only-for-admin: + variations: + is-admin: true + not-admin: false + defaultRule: + variation: not-admin + targeting: + - name: check admin + query: admin eq true + variation: is-admin +``` + +This flag split users in 2 groups. Those who were configured with `admin` attribute set to `true` will resolve to the variation `is-admin`. Otherwise, they will resolve to variation `not-admin`. + +## Create a relay proxy configuration file + +Create a new `YAML` file containing the configuration of your relay proxy. + +```yaml title="goff-proxy.yaml" +listen: 1031 +pollingInterval: 1000 +startWithRetrieverError: false +retriever: + kind: file + path: /goff/flag-config.goff.yaml +exporter: + kind: log +``` + +## Install the relay proxy + +We will run the **relay proxy** locally to make the API available. +The default port will be `1031`. + +```shell +# Launch the container +docker run \ + -p 1031:1031 \ + -v $(pwd)/flag-config.goff.yaml:/goff/flag-config.goff.yaml \ + -v $(pwd)/goff-proxy.yaml:/goff/goff-proxy.yaml \ + gofeatureflag/go-feature-flag:latest + +``` + +_If you don't want to use docker to install the **relay proxy** you can follow the [documentation](../relay_proxy/install_relay_proxy.md)_. + +## Use Open Feature SDK + +_In this example we are using the javascript SDK, but it is still relevant for all the languages_. + +### Install dependencies + +```shell +npm i @openfeature/server-sdk @openfeature/go-feature-flag-provider +``` + +### Init your Open Feature client + +In your app initialization you have to create a client using the Open Feature SDK and initialize it. + +```javascript +const {OpenFeature} = require("@openfeature/server-sdk"); +const {GoFeatureFlagProvider} = require("@openfeature/go-feature-flag-provider"); + + +// init Open Feature SDK with GO Feature Flag provider +const goFeatureFlagProvider = new GoFeatureFlagProvider({ + endpoint: 'http://localhost:1031/' // DNS of your instance of relay proxy +}); +// Sets the default feature flag provider +OpenFeature.setProvider(goFeatureFlagProvider); +// Gets the client that is bound to default provider +const featureFlagClient = OpenFeature.getClient(); +``` + +### Evaluate your flag + +Now you can evaluate your flags anywhere in your code using this client. + +```javascript +// Context of your flag evaluation. +// With GO Feature Flag you MUST have a targetingKey that is a unique identifier of the user. +const userContext = { + targetingKey: '1d1b9238-2591-4a47-94cf-d2bc080892f1', // user unique identifier (mandatory) + firstname: 'john', + lastname: 'doe', + email: 'john.doe@gofeatureflag.org', + admin: true, // this field is used in the targeting rule of the flag "flag-only-for-admin" + // ... +}; + +(async () => { + const adminFlag = await featureFlagClient.getBooleanValue('flag-only-for-admin', false, userContext); + if (adminFlag) { + // flag "flag-only-for-admin" is true for the user + console.log("new feature for admin!"); + } else { + // flag "flag-only-for-admin" is false for the user + console.log("not an admin, no feature"); + } +})(); +``` + +Try changing the `admin` attribute in the `userContext` from `true` to `false` to see different executions flows running. diff --git a/website/versioned_docs/version-v1.40.0/go_module/_category_.json b/website/versioned_docs/version-v1.40.0/go_module/_category_.json new file mode 100644 index 00000000000..1cfccab866c --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/go_module/_category_.json @@ -0,0 +1,10 @@ +{ + "position": 60, + "label":"Use as a GO module", + "collapsible": true, + "collapsed": true, + "link": { + "type": "generated-index", + "title": "Use as a GO module" + } +} diff --git a/website/versioned_docs/version-v1.40.0/go_module/configuration.md b/website/versioned_docs/version-v1.40.0/go_module/configuration.md new file mode 100644 index 00000000000..d5b42c34cd1 --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/go_module/configuration.md @@ -0,0 +1,102 @@ +--- +sidebar_position: 10 +description: How to configure the GO module to use it directly in your code. +--- + +# Configuration +`go-feature-flag` needs to be initialized to be used. +During the initialization you must give a [`ffclient.Config{}`](https://pkg.go.dev/github.com/thomaspoignant/go-feature-flag#Config) configuration object. + +[`ffclient.Config{}`](https://pkg.go.dev/github.com/thomaspoignant/go-feature-flag#Config) is the only location where you can put the configuration. + +## Configuration fields + +| Field | Description | +|-----------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `Retriever` | The configuration retriever you want to use to get your flag file
*See [Store your flag file](./store_file/index.mdx) for the configuration details*.

*This field is optional if `Retrievers`* is configured. | +| `Retrievers` | `Retrievers` is exactly the same thing as `Retriever` but you can configure more than 1 source for your flags.
All flags are retrieved in parallel, but we are applying them in the order you provided them _(it means that a flag can be overridden by another flag)_.
*See [Store your flag file](./store_file/index.mdx) for the configuration details*.

*This field is optional if `Retrievers`* is configured. | +| `Context` | *(optional)*
The context used by the retriever.
Default: **`context.Background()`** | +| `Environment` | *(optional)*
The environment the app is running under, can be checked in feature flag rules.
Default: `""`
*Check [**"environments"** section](../configure_flag/flag_format/#environments) to understand how to use this parameter.* | +| `DataExporter` | *(optional)*
DataExporter defines the method for exporting data on the usage of your flags.
*see [export data section](data_collection/index.md) for more details*. | +| `FileFormat` | *(optional)*
Format of your configuration file. Available formats are `yaml`, `toml` and `json`, if you omit the field it will try to unmarshal the file as a `yaml` file.
Default: **`YAML`** | +| `LeveledLogger` | *(optional)*
LeveledLogger is used to log what `go-feature-flag` is doing.
It should be a `slog` instance.
If no logger is provided the module will not log anything.
Default: **No log** | +| `Notifiers` | *(optional)*
List of notifiers to call when your flag file has been changed.
*See [notifiers section](./notifier/index.md) for more details*. | +| `PollingInterval` | (optional) Duration to wait before refreshing the flags.
The minimum polling interval is 1 second.
Default: **60 * time.Second** | +| `EnablePollingJitter` | (optional) Set to true if you want to avoid having true periodicity when retrieving your flags. It is useful to avoid having spike on your flag configuration storage in case your application is starting multiple instance at the same time.
We ensure a deviation that is maximum ±10% of your polling interval.
Default: **false** | +| `StartWithRetrieverError` | *(optional)* If **true**, the SDK will start even if we did not get any flags from the retriever. It will serve only default values until the retriever returns the flags.
The init method will not return any error if the flag file is unreachable.
Default: **false** | +| `DisableNotifierOnInit` | *(optional)* If **true**, the SDK will not call any notifier when the flags are loaded during initialization. This is useful if you do not want a Slack/Webhook notification saying that the flags have been added every time you start the application.
Default: **false** | +| `Offline` | *(optional)* If **true**, the SDK will not try to retrieve the flag file and will not export any data. No notifications will be sent either.
Default: **false** | +| `EvaluationContextEnrichment` |

*(optional)* It is a free `map[string]interface{}` field that will be merged with the evaluation context sent during the evaluations. It is useful to add common attributes to all the evaluation, such as a server version, environment, ...

All those fields will be included in the custom attributes of the evaluation context.

_If in the evaluation context you have a field with the same name, it will be overridden by the `evaluationContextEnrichment`._

_If you have a key `env` in your `EvaluationContextEnrichment` and you also have the `Environment` set in your configuration, the `env` key from `EvaluationContextEnrichment` will be ignored._

Default: **nil** | +| `PersistentFlagConfigurationFile` | *(optional)* If set GO Feature Flag will store the flags configuration in this file to be able to serve the flags even if none of the retrievers is available during starting time.
By default, the flag configuration is not persisted and stays on the retriever system. By setting a file here, you ensure that GO Feature Flag will always start with a configuration but which can be out-dated.

_(example: `/tmp/goff_persist_conf.yaml`)_ | + +## Example +```go +ffclient.Init(ffclient.Config{ + PollingInterval: 3 * time.Second, + LeveledLogger: slog.Default(), + Context: context.Background(), + Environment: os.Getenv("MYAPP_ENV"), + Retriever: &fileretriever.Retriever{Path: "testdata/flag-config.goff.yaml"}, + FileFormat: "yaml", + Notifiers: []notifier.Notifier{ + &webhooknotifier.Notifier{ + EndpointURL: "https://example.com/hook", + Secret: "Secret", + Meta: map[string]string{ + "app.name": "my app", + }, + }, + }, + DataExporter: ffclient.DataExporter{ + FlushInterval: 10 * time.Second, + MaxEventInMemory: 1000, + Exporter: &file.Exporter{ + OutputDir: "/output-data/", + }, + }, + StartWithRetrieverError: false, +}) +``` + +## Multiple configuration flag files +`go-feature-flag` comes ready to use out of the box by calling the `Init` function and, it will be available everywhere. +Since most applications will want to use a single central flag configuration, the package provides this. It is similar to a singleton. + +In all the examples above, they demonstrate using `go-feature-flag` in its singleton style approach. + +### Working with multiple go-feature-flag + +You can also create many `go-feature-flag` clients to use in your application. + +Each will have its own unique set of configurations and flags. Each can read from a different config file and from different places. +All the functions that `go-feature-flag` package supports are mirrored as methods on a [`GoFeatureFlag`](https://pkg.go.dev/github.com/thomaspoignant/go-feature-flag#GoFeatureFlag). + +#### Example + +```go showLineNumbers +x, err := ffclient.New(Config{ Retriever: &httpretriever.Retriever{{URL: "http://example.com/flag-config.goff.yaml",}}) +defer x.Close() + +y, err := ffclient.New(Config{ Retriever: &httpretriever.Retriever{{URL: "http://example.com/test2.goff.yaml",}}) +defer y.Close() + +user := ffcontext.NewEvaluationContext("user-key") +x.BoolVariation("test-flag", user, false) +y.BoolVariation("test-flag", user, false) + +// ... +``` + +When working with multiple [`GoFeatureFlag`](https://pkg.go.dev/github.com/thomaspoignant/go-feature-flag#GoFeatureFlag), it is up to the user to keep track of different [`GoFeatureFlag`](https://pkg.go.dev/github.com/thomaspoignant/go-feature-flag#GoFeatureFlag) instances. + +## Offline mode +In some situations, you might want to stop making remote calls and fall back to default values for your feature flags. +For example, if your software is both cloud-hosted and distributed to customers to run on-premise, it might make sense +to fall back to defaults when running on-premise. + +You can do this by setting `Offline` mode in the client's Config. + +## Advanced configuration + +- [Export data from your flag variations](./data_collection/index.md) +- [Be notified when your flags change](./notifier/index.md) diff --git a/website/versioned_docs/version-v1.40.0/go_module/data_collection/_category_.json b/website/versioned_docs/version-v1.40.0/go_module/data_collection/_category_.json new file mode 100644 index 00000000000..31a74b81af7 --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/go_module/data_collection/_category_.json @@ -0,0 +1,5 @@ +{ + "position": 40, + "collapsible": true, + "collapsed": true +} diff --git a/website/versioned_docs/version-v1.40.0/go_module/data_collection/azure_blob_storage.md b/website/versioned_docs/version-v1.40.0/go_module/data_collection/azure_blob_storage.md new file mode 100644 index 00000000000..ced4f64591e --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/go_module/data_collection/azure_blob_storage.md @@ -0,0 +1,47 @@ +--- +sidebar_position: 2 +--- + +# Azure Blob Storage Exporter + +The **Azure Blob Storage exporter** will collect the data and create a new file in a specific folder everytime we send the data. + +Everytime the `FlushInterval` or `MaxEventInMemory` is reached, a new file will be added to Google Cloud Storage. + +:::info +If for some reason the Azure Blob Storage upload failed, we will keep the data in memory and retry to add it the next time we reach `FlushInterval` or `MaxEventInMemory`. +::: + +## Configuration example +```go showLineNumbers +ffclient.Config{ + // ... + DataExporter: ffclient.DataExporter{ + // ... + Exporter: &azureexporter.Exporter{ + Container: "test-goff", + Format: "json", + Path: "yourPath", + Filename: "flag-variation-{{ .Timestamp}}.{{ .Format}}", + AccountName: "goff", + AccountKey: "XXXX", + }, + }, + // ... +} +``` + +## Configuration fields +| Field | Description | +|---------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `Container ` | Name of your Azure Blob Storage Container. | +| `AccountName ` | This is the name of your Azure Blob Storage account. | +| `AccountKey ` | *(optional)* This is the secret key of your Azure Blob Storage account. | +| `CsvTemplate` | *(optional)* CsvTemplate is used if your output format is CSV. This field will be ignored if you are using format other than CSV. You can decide which fields you want in your CSV line with a go-template syntax, please check [internal/exporter/feature_event.go](https://github.com/thomaspoignant/go-feature-flag/blob/main/internal/exporter/feature_event.go) to see what are the fields available.
**Default:** `{{ .Kind}};{{ .ContextKind}};{{ .UserKey}};{{ .CreationDate}};{{ .Key}};{{ .Variation}};{{ .Value}};{{ .Default}};{{ .Source}}\n` | +| `Filename` | *(optional)* Filename is the name of your output file. You can use a templated config to define the name of your exported files.
Available replacements are `{{ .Hostname}}`, `{{ .Timestamp}`} and `{{ .Format}}`
Default: `flag-variation-{{ .Hostname}}-{{ .Timestamp}}.{{ .Format}}` | +| `Format` | *(optional)* Format is the output format you want in your exported file. Available formats are **`JSON`**, **`CSV`**, **`Parquet`**. *(Default: `JSON`)* | +| `Options` | *(optional)* An instance of `option.ClientOption` that configures your access to Google Cloud.
Check [this documentation for more info](https://cloud.google.com/docs/authentication). | +| `Path ` | *(optional)* The location of the directory in your container. | +| `ParquetCompressionCodec` | *(optional)* ParquetCompressionCodec is the parquet compression codec for better space efficiency. [Available options](https://github.com/apache/parquet-format/blob/master/Compression.md) *(Default: `SNAPPY`)* |` + +Check the [godoc for full details](https://pkg.go.dev/github.com/thomaspoignant/go-feature-flag/exporter/azureexporter). diff --git a/website/versioned_docs/version-v1.40.0/go_module/data_collection/custom.md b/website/versioned_docs/version-v1.40.0/go_module/data_collection/custom.md new file mode 100644 index 00000000000..f5fe3d79d03 --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/go_module/data_collection/custom.md @@ -0,0 +1,24 @@ +--- +sidebar_position: 30 +--- + +# Custom exporter +To create a custom exporter you must have a `struct` that implements the [`exporter.Exporter`](https://pkg.go.dev/github.com/thomaspoignant/go-feature-flag/internal/exporter#Exporter) interface. + + +```go +type Exporter interface { + // Export will send the data to the exporter. + Export(context.Context, *fflog.FFLogger, []exporter.FeatureEvent) error + + // IsBulk return false if we should directly send the data as soon as it is produced + // and true if we collect the data to send them in bulk. + IsBulk() bool +} +``` +`Export` is called asynchronously with a list of `exporter.FeatureEvent` that have been collected. +It is your responsibility to store them where you want. + +`IsBulk` function should return `false` if the exporter can handle the results in stream mode. +If you decide to manage it in streaming mode, everytime we call a variation the `Export` function will be called +with only on event in the list. diff --git a/website/versioned_docs/version-v1.40.0/go_module/data_collection/file.md b/website/versioned_docs/version-v1.40.0/go_module/data_collection/file.md new file mode 100644 index 00000000000..c0b20d97092 --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/go_module/data_collection/file.md @@ -0,0 +1,38 @@ +--- +sidebar_position: 3 +--- + +# File Exporter +The file exporter will collect the data and create a new file in a specific folder everytime we send the data. +This file should be in the local instance. + +Check this [complete example](https://github.com/thomaspoignant/go-feature-flag/tree/main/examples/data_export_file) to see how to export the data in a file. + +## Configuration example +```go showLineNumbers +ffclient.Config{ + // ... + DataExporter: ffclient.DataExporter{ + // ... + Exporter: &fileexporter.Exporter{ + OutputDir: "/output-data/", + Format: "csv", + FileName: "flag-variation-{{ .Hostname}}-{{ .Timestamp}}.{{ .Format}}", + CsvTemplate: "{{ .Kind}};{{ .ContextKind}};{{ .UserKey}};{{ .CreationDate}};{{ .Key}};{{ .Variation}};{{ .Value}};{{ .Default}};{{ .Source}}\n" + }, + }, + // ... +} +``` + +## Configuration fields + +| Field | Description | +|---|---| +|`OutputDir` | OutputDir is the location of the directory to store the exported files. | +|`Format` | _(Optional)_ Format is the output format you want in your exported file.
Available format: **`JSON`**, **`CSV`**, **`Parquet`**.
**Default: `JSON`** | +|`Filename` | _(Optional)_ Filename is the name of your output file.
You can use a templated config to define the name of your exported files.
Available replacements are `{{ .Hostname}}`, `{{ .Timestamp}}` and `{{ .Format}}`
**Default: `flag-variation-{{ .Hostname}}-{{ .Timestamp}}.{{ .Format}}`**| +|`CsvTemplate` | _(Optional)_ CsvTemplate is used if your output format is CSV.
This field will be ignored if you are using format other than CSV.
You can decide which fields you want in your CSV line with a go-template syntax, please check [internal/exporter/feature_event.go](https://github.com/thomaspoignant/go-feature-flag/blob/main/internal/exporter/feature_event.go) to see the available fields.
**Default:** `{{ .Kind}};{{ .ContextKind}};{{ .UserKey}};{{ .CreationDate}};{{ .Key}};{{ .Variation}};{{ .Value}};{{ .Default}}\n` | +| `ParquetCompressionCodec` | _(Optional)_ ParquetCompressionCodec is the parquet compression codec for better space efficiency. [Available options](https://github.com/apache/parquet-format/blob/master/Compression.md)
**Default: `SNAPPY`** |` + +Check the [godoc for full details](https://pkg.go.dev/github.com/thomaspoignant/go-feature-flag/exporter/fileexporter). diff --git a/website/versioned_docs/version-v1.40.0/go_module/data_collection/google_cloud_storage.md b/website/versioned_docs/version-v1.40.0/go_module/data_collection/google_cloud_storage.md new file mode 100644 index 00000000000..6cf249ba2c0 --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/go_module/data_collection/google_cloud_storage.md @@ -0,0 +1,46 @@ +--- +sidebar_position: 2 +--- + +# Google Cloud Storage Exporter + +The **Google Cloud Storage exporter** will collect the data and create a new file in a specific folder everytime we send the data. + +Everytime the `FlushInterval` or `MaxEventInMemory` is reached, a new file will be added to Google Cloud Storage. + +:::info +If for some reason the Google Cloud Storage upload failed, we will keep the data in memory and retry to add it the next time we reach `FlushInterval` or `MaxEventInMemory`. +::: + +Check this [complete example](https://github.com/thomaspoignant/go-feature-flag/tree/main/examples/data_export_googlecloudstorage) to see how to export the data in S3. + +## Configuration example +```go showLineNumbers +ffclient.Config{ + // ... + DataExporter: ffclient.DataExporter{ + // ... + Exporter: &gcstorageexporter.Exporter{ + Bucket: "test-goff", + Format: "json", + Path: "yourPath", + Filename: "flag-variation-{{ .Timestamp}}.{{ .Format}}", + Options: []option.ClientOption{}, // Your google cloud SDK options + }, + }, + // ... +} +``` + +## Configuration fields +| Field | Description | +|---------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `Bucket ` | Name of your Google Cloud Storage Bucket. | +| `CsvTemplate` | *(optional)* CsvTemplate is used if your output format is CSV. This field will be ignored if you are using format other than CSV. You can decide which fields you want in your CSV line with a go-template syntax, please check [internal/exporter/feature_event.go](https://github.com/thomaspoignant/go-feature-flag/blob/main/internal/exporter/feature_event.go) to see what are the fields available.
**Default:** `{{ .Kind}};{{ .ContextKind}};{{ .UserKey}};{{ .CreationDate}};{{ .Key}};{{ .Variation}};{{ .Value}};{{ .Default}};{{ .Source}}\n` | +| `Filename` | *(optional)* Filename is the name of your output file. You can use a templated config to define the name of your exported files.
Available replacements are `{{ .Hostname}}`, `{{ .Timestamp}`} and `{{ .Format}}`
Default: `flag-variation-{{ .Hostname}}-{{ .Timestamp}}.{{ .Format}}` | +| `Format` | *(optional)* Format is the output format you want in your exported file. Available formats are **`JSON`**, **`CSV`**, **`Parquet`**. *(Default: `JSON`)* | +| `Options` | *(optional)* An instance of `option.ClientOption` that configures your access to Google Cloud.
Check [this documentation for more info](https://cloud.google.com/docs/authentication). | +| `Path ` | *(optional)* The location of the directory in your bucket. | +| `ParquetCompressionCodec` | *(optional)* ParquetCompressionCodec is the parquet compression codec for better space efficiency. [Available options](https://github.com/apache/parquet-format/blob/master/Compression.md) *(Default: `SNAPPY`)* |` + +Check the [godoc for full details](https://pkg.go.dev/github.com/thomaspoignant/go-feature-flag/exporter/gcstorageexporter). diff --git a/website/versioned_docs/version-v1.40.0/go_module/data_collection/google_pubsub.md b/website/versioned_docs/version-v1.40.0/go_module/data_collection/google_pubsub.md new file mode 100644 index 00000000000..fa09655d40e --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/go_module/data_collection/google_pubsub.md @@ -0,0 +1,38 @@ +--- +sidebar_position: 7 +--- + +# PubSub Exporter + +The **PubSub exporter** will collect the data and publish an event on the topic for each evaluation we receive. + +## Configuration example +```go +ffclient.Config{ + // ... + cfg, _ := config.LoadDefaultConfig(context.TODO()) + DataExporter: ffclient.DataExporter{ + // ... + Exporter: &pubsubexporter.Exporter{ + ProjectID: "project-id", // required + Topic: "topic", // required + Options: []option.ClientOption{...}, + PublishSettings: &pubsub.PublishSettings{...}, + EnableMessageOrdering: true, + }, + }, + // ... +} +``` + +## Configuration fields + +| Field | Description | +|-------------------------|------------------------------------------------------------------------------------------------------| +| `ProjectID ` | ID of GCP project.
_You can find it in your GCP console_ | +| `Topic ` | Name of topic on which messages will be published | +| `Options` | PubSub client options *(see [docs](https://pkg.go.dev/google.golang.org/api/option))* | +| `PublishSettings` | Topic related settings *(see [docs](https://pkg.go.dev/cloud.google.com/go/pubsub#PublishSettings))* | +| `EnableMessageOrdering` | Enables delivery of ordered keys | + +Check the [godoc for full details](https://pkg.go.dev/github.com/thomaspoignant/go-feature-flag/exporter/pubsubexporter). diff --git a/website/versioned_docs/version-v1.40.0/go_module/data_collection/index.md b/website/versioned_docs/version-v1.40.0/go_module/data_collection/index.md new file mode 100644 index 00000000000..8c27a7368ff --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/go_module/data_collection/index.md @@ -0,0 +1,141 @@ +--- +sidebar_position: 0 +--- + +# Export data + +If you want to export data about how your flags are used, you can use the **`DataExporter`**. +It collects all the variations events and can save these events on several locations: + +- [File](file.md) *- create local files with the variation usages.* +- [Log](log.md) *- use your logger to write the variation usages.* +- [S3](s3.md) *- export your variation usages to S3.* +- [Webhook](webhook.md) *- export your variation usages by calling a webhook.* +- [Google Cloud Storage](google_cloud_storage.md) *- export your variation usages by calling a webhook.* +- [Kafka](kafka.md) *- export your variation usages by producing messages to a Kafka topic.* + +If the existing exporter does not work with your system you can extend the system and use a [custom exporter](custom.md). + +## Data format + +Currently, we are supporting only feature events. +They represent individual flag evaluations and are considered "full fidelity" events. + +### Example + +```json showLineNumbers +{ + "kind": "feature", + "contextKind": "anonymousUser", + "userKey": "ABCD", + "creationDate": 1618228297, + "key": "test-flag", + "variation": "Default", + "value": false, + "default": false, + "source": "SERVER" +} +``` + +### Configuration fields + +| Field | Description | +|--------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| **`kind`** | The kind for a feature event is feature. A feature event will only be generated if the trackEvents attribute of the flag is set to true. | +| **`contextKind`** | The kind of context which generated an event. This will only be "**anonymousUser**" for events generated on behalf of an anonymous user or the reserved word "**user**" for events generated on behalf of a non-anonymous user. | +| **`userKey`** | The key of the user object used in a feature flag evaluation. | +| **`creationDate`** | When the feature flag was requested at Unix epoch time in milliseconds. | +| **`key`** | The key of the feature flag requested. | +| **`variation`** | The variation of the flag requested. Available values are:
**True**: if the flag was evaluated to True
**False**: if the flag was evaluated to False
**Default**: if the flag was evaluated to Default
**SdkDefault**: if something wrong happened and the SDK default value was used. | +| **`value`** | The value of the feature flag returned by feature flag evaluation. | +| **`source`** | Where the event is generated. This is set to SERVER when the event is evaluated from the relay-proxy and PROVIDER_CACHE when it is evaluated from the cache. +| **`default`** | (Optional) This value is set to true if feature flag evaluation failed, in which case, the value returned is the default value passed to variation. | + +Events are collected and send in bulk to avoid spamming your exporter *(see details in [how to configure data export](#how-to-configure-data-export)*) + +## How to configure data export? + +In your `ffclient.Config` add the `DataExporter` field and configure your export location. + +To avoid spamming your location everytime you have a variation called, `go-feature-flag` is storing in memory all the events and sends them in bulk to the exporter. +You can decide the threshold on when to send the data with the properties `FlushInterval` and `MaxEventInMemory`. The first threshold hit will export the data. + +If there are some flags that you don't want to export, you can use `trackEvents` fields on these specific flags to disable the data export *(see [flag file format](../../configure_flag/flag_format.mdx))*. + +### Example + +```go showLineNumbers +ffclient.Config{ + // ... + DataExporter: ffclient.DataExporter{ + FlushInterval: 10 * time.Second, + MaxEventInMemory: 1000, + Exporter: &fileexporter.Exporter{ + OutputDir: "/output-data/", + }, + }, + // ... +} +``` + +### Configuration fields + +| Field | Description | +|--------------------|-------------| +| `Exporter` | The configuration of the exporter you want to use. All the exporters are available in the `exporter` package. | +| `FlushInterval` | *(optional)*
Time to wait before exporting the data.
**Default: 60 seconds**. | +| `MaxEventInMemory` | *(optional)*
If `MaxEventInMemory` is reach before the `FlushInterval` a intermediary export will be done
**Default: 100000**. | + +## Don't track a flag + +By default, all flags are trackable, and their data is exported. + +If you want to exclude a specific flag from the data export, you can set the property `trackEvents` to `false` on your flag, and you will have no export for it. + +### YAML + +```yaml +test-flag: + percentage: 50 + true: "B" + false: "A" + default: "Default" + trackEvents: false +``` + +### JSON + +
+ JSON example + +```json +{ + "test-flag": { + "percentage": 50, + "true": "B", + "false": "A", + "default": "Default", + # highlight-next-line + "trackEvents": false + } +} +``` + +
+ +### TOML + +
+ TOML example + +```toml +[test-flag] +percentage = 50.0 +true = "B" +false = "A" +default = "Default" +# highlight-next-line +trackEvents = false +``` + +
diff --git a/website/versioned_docs/version-v1.40.0/go_module/data_collection/kafka.md b/website/versioned_docs/version-v1.40.0/go_module/data_collection/kafka.md new file mode 100644 index 00000000000..ace9e6ebd73 --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/go_module/data_collection/kafka.md @@ -0,0 +1,32 @@ +--- +sidebar_position: 6 +--- + +# Kafka Exporter +The **Kafka exporter** produces messages to a Kafka topic for each event generated. + +## Configuration example +```go +ffclient.Config{ + // ... + DataExporter: ffclient.DataExporter{ + // ... + Exporter: &kafkaexporter.Exporter{ + Settings: kafkaexporter.Settings{ + Topic: "go-feature-flag-events", + Addresses: []string{"cluster1", "cluster2"}, + }, + }, + }, + // ... +} +``` + +## Configuration fields +| Field | Description | +|--------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `Topic ` | Name of the topic to publish messages | +| `Addresses ` | The list of addresses for the Kafka boostrap servers | +| `Config ` | (Optional) An instance of `*sarama.Config` that holds additional settings for the producer, such as timeouts, TLS settings, etc. If not populated, a default will be used by calling `sarama.NewConfig()` | | | + +Check the [godoc for full details](https://pkg.go.dev/github.com/thomaspoignant/go-feature-flag/exporter/kafkaexporter). diff --git a/website/versioned_docs/version-v1.40.0/go_module/data_collection/kinesis.md b/website/versioned_docs/version-v1.40.0/go_module/data_collection/kinesis.md new file mode 100644 index 00000000000..539502c2424 --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/go_module/data_collection/kinesis.md @@ -0,0 +1,34 @@ +--- +sidebar_position: 6 +--- + +# Kinesis Exporter +The **Kinesis exporter** produces messages to a Kinesis stream for each event generated. Currently all messages are populated into single shard of your choice. + +## Configuration example +```go +ffclient.Config{ + // ... + DataExporter: ffclient.DataExporter{ + // ... + Exporter: &kinesisexporter.Exporter{ + Settings: kinesisexporter.NewSettings( + kinesisexporter.WithStreamName("test-stream"), + kinesisexporter.WithPartitionKey("0"), + kinesisexporter.WithExplicitHashKey("0"), + ), + AwsConfig: &config, // aws custom configuration + }, + + }, + // ... +} +``` + +## Configuration fields +| Field | Description | +|--------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `StreamName ` | (Required) Name of the Kinesis stream to publish messages to | +| `PartitionKey ` | (Optional) Function that takes 'FeatureEvent' as an input and returns calculated string 'Partition Key'. If not specified, then by default string 'default' will be used for all events. Effectively this will assign all events to a single Kinesis shard. There is nothing bad in using single shard, but for performance consideration this field might be utilised. | +| `ExplicitPartitionKey ` | (Optional) String key to identify which stream shard event data belongs to. Overrides PartitionKey setting | +| `AwsConfig ` | (Optional) An instance of `*aws.Config` that holds additional settings connect to AWS | | | diff --git a/website/versioned_docs/version-v1.40.0/go_module/data_collection/log.md b/website/versioned_docs/version-v1.40.0/go_module/data_collection/log.md new file mode 100644 index 00000000000..47a78ad5669 --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/go_module/data_collection/log.md @@ -0,0 +1,30 @@ +--- +sidebar_position: 5 +--- + +# Log Exporter +The log exporter is here mostly for backward compatibility *(originally every variation were logged, but it can be a lot of data for a default configuration)*. +It will use your logger `ffclient.Config.Logger` to log every variation changes. + +You can configure your output log with the `Format` field. +It uses a [go template](https://golang.org/pkg/text/template/) format. + +## Configuration example +```go showLineNumbers +ffclient.Config{ + // ... + DataExporter: ffclient.DataExporter{ + Exporter: &logsexporter.Exporter{ + LogFormat: "[{{ .FormattedDate}}] user=\"{{ .UserKey}}\", flag=\"{{ .Key}}\", value=\"{{ .Value}}\"", + }, + }, + // ... +} +``` + +## Configuration fields +| Field | Description | +|-------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `LogFormat` | *(optional)*
LogFormat is the [template](https://golang.org/pkg/text/template/) configuration of the output format of your log.
You can use all the key from the `exporter.FeatureEvent` + a key called `FormattedDate` that represents the date with the **RFC 3339** Format.

**Default: `[{{ .FormattedDate}}] user="{{ .UserKey}}", flag="{{ .Key}}", value="{{ .Value}}"`** | + +Check the [godoc for full details](https://pkg.go.dev/github.com/thomaspoignant/go-feature-flag/exporter/logsexporter). diff --git a/website/versioned_docs/version-v1.40.0/go_module/data_collection/s3.md b/website/versioned_docs/version-v1.40.0/go_module/data_collection/s3.md new file mode 100644 index 00000000000..cd086189312 --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/go_module/data_collection/s3.md @@ -0,0 +1,52 @@ +--- +sidebar_position: 1 +--- + +# S3 Exporter + +The **S3 exporter** will collect the data and create a new file in a specific folder everytime we send the data. + +Everytime the `FlushInterval` or `MaxEventInMemory` is reached a new file will be added to S3. + +:::info +If for some reason the S3 upload fails, we will keep the data in memory and retry to add the next time we reach `FlushInterval` or `MaxEventInMemory`. +::: + +![export in S3 screenshot](/docs/data_collection/s3-exporter.png) + + +Check this [complete example](https://github.com/thomaspoignant/go-feature-flag/tree/main/examples/data_export_s3) to see how to export the data in S3. + +## Configuration example +```go +awsConfig, _ := config.LoadDefaultConfig(context.Background()) +ffclient.Config{ + // ... + DataExporter: ffclient.DataExporter{ + // ... + Exporter: &s3exporterv2.Exporter{ + Format: "csv", + FileName: "flag-variation-{{ .Hostname}}-{{ .Timestamp}}.{{ .Format}}", + CsvTemplate: "{{ .Kind}};{{ .ContextKind}};{{ .UserKey}};{{ .CreationDate}};{{ .Key}};{{ .Variation}};{{ .Value}};{{ .Default}};{{ .Source}}\n", + Bucket: "my-bucket", + S3Path: "/go-feature-flag/variations/", + Filename: "flag-variation-{{ .Timestamp}}.{{ .Format}}", + AwsConfig: &awsConfig, + }, + }, + // ... +} +``` + +## Configuration fields +| Field | Description | +|---------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `Bucket ` | Name of your S3 Bucket. | +| `AwsConfig ` | An instance of `aws.Config` that configures your access to AWS *(see [this documentation for more info](https://aws.github.io/aws-sdk-go-v2/docs/configuring-sdk/))*. | +| `CsvTemplate` | *(optional)* CsvTemplate is used if your output format is CSV. This field will be ignored if you are using format other than CSV. You can decide which fields you want in your CSV line with a go-template syntax, please check [internal/exporter/feature_event.go](https://github.com/thomaspoignant/go-feature-flag/blob/main/internal/exporter/feature_event.go) to see what are the fields available.
**Default:** `{{ .Kind}};{{ .ContextKind}};{{ .UserKey}};{{ .CreationDate}};{{ .Key}};{{ .Variation}};{{ .Value}};{{ .Default}};{{ .Source}}\n` | +| `Filename` | *(optional)* Filename is the name of your output file. You can use a templated config to define the name of your exported files.
Available replacements are `{{ .Hostname}}`, `{{ .Timestamp}}` and `{{ .Format}}`
Default: `flag-variation-{{ .Hostname}}-{{ .Timestamp}}.{{ .Format}}` | +| `Format` | *(optional)* Format is the output format you want in your exported file. Available formats are **`JSON`**, **`CSV`**, **`Parquet`**. *(Default: `JSON`)* | +| `S3Path ` | *(optional)* The location of the directory in S3. | +| `ParquetCompressionCodec` | *(optional)* ParquetCompressionCodec is the parquet compression codec for better space efficiency. [Available options](https://github.com/apache/parquet-format/blob/master/Compression.md) *(Default: `SNAPPY`)* |` + +Check the [godoc for full details](https://pkg.go.dev/github.com/thomaspoignant/go-feature-flag/exporter/s3exporterv2). diff --git a/website/versioned_docs/version-v1.40.0/go_module/data_collection/sqs.md b/website/versioned_docs/version-v1.40.0/go_module/data_collection/sqs.md new file mode 100644 index 00000000000..56affe5ce4e --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/go_module/data_collection/sqs.md @@ -0,0 +1,31 @@ +--- +sidebar_position: 6 +--- + +# SQS Exporter + +The **SQS exporter** will collect the data and create an event in the queue for each evaluation we receive. + +## Configuration example +```go +ffclient.Config{ + // ... + cfg, _ := config.LoadDefaultConfig(context.TODO()) + DataExporter: ffclient.DataExporter{ + // ... + Exporter: &sqsexporter.Exporter{ + QueueURL: "https://sqs.eu-west-1.amazonaws.com/XXX/test-queue", + AwsConfig: &cfg, + }, + }, + // ... +} +``` + +## Configuration fields +| Field | Description | +|---------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `QueueURL ` | URL of your SQS queue.
_You can find it in your AWS console._ | +| `AwsConfig ` | An instance of `aws.Config` that configures your access to AWS *(see [this documentation for more info](https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html))*. | + +Check the [godoc for full details](https://pkg.go.dev/github.com/thomaspoignant/go-feature-flag/exporter/sqsexporter). diff --git a/website/versioned_docs/version-v1.40.0/go_module/data_collection/webhook.md b/website/versioned_docs/version-v1.40.0/go_module/data_collection/webhook.md new file mode 100644 index 00000000000..112000510e7 --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/go_module/data_collection/webhook.md @@ -0,0 +1,75 @@ +--- +sidebar_position: 4 +--- + +# Webhook Exporter + +The **Webhook exporter** will collect the data and send it via an HTTP POST request to the specified endpoint. +Everytime the `FlushInterval` or `MaxEventInMemory` is reached, a new call is performed. + +:::info +If for some reason the call failed, we will keep the data in memory and retry to add the next time we reach `FlushInterval` or `MaxEventInMemory`. +::: + +## Configuration example +```go showLineNumbers +ffclient.Config{ + // ... + DataExporter: ffclient.DataExporter{ + // ... + Exporter: &webhookexporter.Exporter{ + EndpointURL: " https://webhook.url/", + Secret: "secret-for-signing", + Meta: map[string]string{ + "extraInfo": "info", + }, + Headers: map[string][]string{ + "Authorization": {"Bearer auth_token"}, + }, + }, + }, + // ... +} +``` +## Configuration fields +| Field | Description | +|----------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `EndpointURL ` | EndpointURL of your webhook | +| `Secret ` | *(optional)*
Secret used to sign your request body and fill the `X-Hub-Signature-256` header.
See [signature section](#signature) for more details. | +| `Meta` | *(optional)*
Add all the information you want to see in your request. | +| `Headers` | *(optional)*
List of Headers to send to the endpoint | + + +## Webhook format +If you have configured a webhook, a `POST` request will be sent to the `EndpointURL` with a body in this format: + +```json +{ + "meta": { + "hostname": "server01", + // ... + }, + "events": [ + { + "kind": "feature", + "contextKind": "anonymousUser", + "userKey": "14613538188334553206", + "creationDate": 1618909178, + "key": "test-flag", + "variation": "Default", + "value": false, + "default": false, + "source": "SERVER" + }, + // ... + ] +} +``` + +## Signature +This header **`X-Hub-Signature-256`** is sent if the webhook is configured with a **`secret`**. +This is the **HMAC hex digest** of the request body, and is generated using the **SHA-256** hash function and the **secret as the HMAC key**. + +:::caution +The recommendation is to always use the `Secret` and on your API/webhook always verify the signature key to be sure that you don't get into a man-in-the-middle attack. +::: diff --git a/website/versioned_docs/version-v1.40.0/go_module/notifier/_category_.json b/website/versioned_docs/version-v1.40.0/go_module/notifier/_category_.json new file mode 100644 index 00000000000..69bc4e31aa2 --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/go_module/notifier/_category_.json @@ -0,0 +1,5 @@ +{ + "position": 50, + "collapsible": true, + "collapsed": true +} diff --git a/website/versioned_docs/version-v1.40.0/go_module/notifier/custom.md b/website/versioned_docs/version-v1.40.0/go_module/notifier/custom.md new file mode 100644 index 00000000000..f8e2ebcde23 --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/go_module/notifier/custom.md @@ -0,0 +1,24 @@ +--- +sidebar_position: 30 +--- + +# Custom Notifier + +To create a custom notifier you must have a `struct` that implements the +[`notifier.Notifier`](https://pkg.go.dev/github.com/thomaspoignant/go-feature-flag/notifier/notifier) interface. + +In parameter you will receive a `notifier.DiffCache` struct that will tell you what has changed in your flag configuration. + +```go +import ( + ffclient "github.com/thomaspoignant/go-feature-flag" + "github.com/thomaspoignant/go-feature-flag/notifier/notifier" + "sync" +) + +type Notifier struct{} +func (c *Notifier) Notify(diff notifier.DiffCache) error { + // ... + // do whatever you want here +} +``` diff --git a/website/versioned_docs/version-v1.40.0/go_module/notifier/discord.md b/website/versioned_docs/version-v1.40.0/go_module/notifier/discord.md new file mode 100644 index 00000000000..175e427a76f --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/go_module/notifier/discord.md @@ -0,0 +1,43 @@ +--- +sidebar_position: 10 +--- + +# Discord Notifier + +The discord notifier allows to get notified on your favorite discord channel when an instance of `go-feature-flag` is +detecting changes in the configuration of your flags. + +![Discord Notification](/docs/notifier/discord1.png) + +## Configure Discord Notifications + +1. Connect to your discord account and go on the channel where you want to send the notifications. +2. Go on the settings menu of your channel. + + ![Discord Config](/docs/notifier/discord2.png) + +3. Under your channel’s settings, go to the "Integrations" section and create a new webhook. To create it, please follow + the [official documentation](https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks). +4. Copy the webhook URL + + ![Discord WebHook](/docs/notifier/discord3.png) + +5. Now you can configure your notifier + +```go +err := ffclient.Init(ffclient.Config +{ + // ... + Notifiers: []notifier.Notifier{ + &discordnotifier.Notifier{ + DiscordWebhookURL: "https://discord.com/api/webhooks/yyyy/xxxxxxx", + }, + }, +}) +``` + +## **Configuration fields** + +| **Field** | **Description** | +|---------------------|-------------------------------------------| +| `DiscordWebhookURL` | The complete URL of your discord webhook. | diff --git a/website/versioned_docs/version-v1.40.0/go_module/notifier/index.md b/website/versioned_docs/version-v1.40.0/go_module/notifier/index.md new file mode 100644 index 00000000000..ac348ef9955 --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/go_module/notifier/index.md @@ -0,0 +1,19 @@ +--- +sidebar_position: 1 +--- + +# Notify flag changes +If you want to be informed when a flag has changed, you can configure a [**notifier**](https://pkg.go.dev/github.com/thomaspoignant/go-feature-flag#NotifierConfig). + +A notifier will send one notification to the targeted system to inform them that a new flag configuration has been loaded. + +:::info +`go-feature-flag` can handle more than one notifier at a time. +::: + +Available notifiers are: + +- [Slack](slack.md) - Get a slack message with the changes. +- [Webhook](webhook.md) - Call an API with the changes. +- [Discord](discord.md) - Get a discord message with the changes. +- [Microsoft Teams](microsoft-teams.md) - Get a Microsoft Teams message with the changes. diff --git a/website/versioned_docs/version-v1.40.0/go_module/notifier/microsoft-teams.md b/website/versioned_docs/version-v1.40.0/go_module/notifier/microsoft-teams.md new file mode 100644 index 00000000000..3757d394549 --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/go_module/notifier/microsoft-teams.md @@ -0,0 +1,34 @@ +--- +sidebar_position: 11 +--- + +# Microsoft Teams Notifier + +The microsoft teams notifier allows to get notified on your favorite microsoft teams channel when an instance of `go-feature-flag` is +detecting changes in the configuration of your flags. + + +## Configure Microsoft Teams Notifications + +1. First create a Webhook in the channel you want to send notifications to. + *Need a hand?*[Click here to see how it's done](https://support.microsoft.com/en-us/office/create-incoming-webhooks-with-workflows-for-microsoft-teams-8ae491c7-0394-4861-ba59-055e33f75498) +2. Copy the webhook URL +3. Now you can configure your notifier + +```go +err := ffclient.Init(ffclient.Config +{ + // ... + Notifiers: []notifier.Notifier{ + µsoftteamsnotifier.Notifier{ + MicrosoftTeamsWebhookURL: "https://xxx.xxx/..." + }, + }, +}) +``` + +## **Configuration fields** + +| **Field** | **Description** | +|----------------------------|---------------------------------------------------| +| `MicrosoftTeamsWebhookURL` | The complete URL of your microsoft teams webhook. | diff --git a/website/versioned_docs/version-v1.40.0/go_module/notifier/slack.md b/website/versioned_docs/version-v1.40.0/go_module/notifier/slack.md new file mode 100644 index 00000000000..4d2f9304f80 --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/go_module/notifier/slack.md @@ -0,0 +1,33 @@ +--- +sidebar_position: 1 +--- + +# Slack Notifier +The **Slack** notifier allows you to get notification on your favorite slack channel when an instance of `go-feature-flag` is detecting changes in the configuration file. + +
+ +## Configure Slack Notification +1. First, you need to create an incoming webhook on your slack instance. + *You can follow this [documentation to see how to do it](https://api.slack.com/messaging/webhooks#getting_started)* +2. Copy your webhook URL. + It should look like: `https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX`. +3. In your init method add a slack notifier + +```go {5}showLineNumbers +ffclient.Config{ + // ... + Notifiers: []notifier.Notifier{ + &slacknotifier.Notifier{ + SlackWebhookURL: "https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX", + }, + // ... + }, +} +``` + +### Configuration fields + +| Field | Description | +|---|---| +|`SlackWebhookURL` | The complete URL of your incoming webhook configured in Slack. | diff --git a/website/versioned_docs/version-v1.40.0/go_module/notifier/webhook.md b/website/versioned_docs/version-v1.40.0/go_module/notifier/webhook.md new file mode 100644 index 00000000000..4fa6cbb6180 --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/go_module/notifier/webhook.md @@ -0,0 +1,125 @@ +--- +sidebar_position: 20 +--- + +# Webhook Notifier + +The **Webhook notifier** will perform an HTTP POST request to the specified endpoint everytime a change in the flags is +detected. + +The format of the call is specified in the [format section](#format) and you can [sign the body](#signature) to trust +the data you are receiving. + +## Configure the webhook notifier + +```go +ffclient.Config{ + // ... + Notifiers: []notifier.Notifier{ + &webhooknotifier.Notifier{ + EndpointURL: " https://example.com/hook", + Secret: "Secret", + Meta: map[string]string{ + "app.name": "my app", + }, + Headers: map[string][]string{ + "Authorization": {"Bearer auth_token"}, + }, + }, + // ... + }, +} +``` + +## Configuration fields + +| Field | Description | +|---------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `EndpointURL` | The complete URL of your API *(we will send a POST request to this URL, [see format](#format))* | +| `Secret` | *(optional)*
A secret key you can share with your webhook. We will use this key to sign the request *(see [signature section](#signature) for more details)*. | +| `Meta` | *(optional)*
A list of key value that will be added in your request, this is super useful if you want to add information on the current running instance of your app.

**By default the hostname is always added in the meta information.** | +| `Headers` | *(optional)*
The list of Headers to send to the endpoint. | + +## Format + +If you have configured a webhook, a `POST` request will be sent to the `EndpointURL` with a body in this format: + +```json +{ + "meta": { + "hostname": "server01" + // ... + }, + "flags": { + "deleted": {}, + // map of your deleted flags + "added": {}, + // map of your added flags + "updated": { + "flag-name": { + // an object that contains old and new value + "old_value": {}, + "new_value": {} + } + } + } +} +``` + +### Example + +```json +{ + "meta": { + "hostname": "server01" + }, + "flags": { + "deleted": { + "test-flag": { + "rule": "key eq \"random-key\"", + "percentage": 100, + "true": true, + "false": false, + "default": false + } + }, + "added": { + "test-flag3": { + "percentage": 5, + "true": "test", + "false": "false", + "default": "default" + } + }, + "updated": { + "test-flag2": { + "old_value": { + "rule": "key eq \"not-a-key\"", + "percentage": 100, + "true": true, + "false": false, + "default": false + }, + "new_value": { + "disable": true, + "rule": "key eq \"not-a-key\"", + "percentage": 100, + "true": true, + "false": false, + "default": false + } + } + } + } +} +``` + +## Signature + +This header **`X-Hub-Signature-256`** is sent if the webhook is configured with a secret. This is the HMAC hex digest of +the request body, and is generated using the SHA-256 hash function and the secret as the HMAC key. + +:::caution +The recommendation is to always use the `Secret` and on your API/webook always verify the signature key to be sure that +you don't get into a man-in-the-middle attack. +::: diff --git a/website/versioned_docs/version-v1.40.0/go_module/store_file/_category_.json b/website/versioned_docs/version-v1.40.0/go_module/store_file/_category_.json new file mode 100644 index 00000000000..5a330427a1b --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/go_module/store_file/_category_.json @@ -0,0 +1,5 @@ +{ + "position": 30, + "collapsible": true, + "collapsed": true +} diff --git a/website/versioned_docs/version-v1.40.0/go_module/store_file/azureblobstorage.md b/website/versioned_docs/version-v1.40.0/go_module/store_file/azureblobstorage.md new file mode 100644 index 00000000000..ffe1f7ee38c --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/go_module/store_file/azureblobstorage.md @@ -0,0 +1,70 @@ +--- +sidebar_position: 8 +--- + +# Azure Blob Storage +This retriever is used to retrieve data from a Container on Azure Blob Storage. + +## Key Features + +- Supports both shared key and default Azure credential authentication +- Automatic retry mechanism for blob downloads +- Flexible configuration with optional custom service URL + +## Error Handling + +The `Retrieve` method returns an error if: +- `AccountName` is empty +- `Container` or `Object` is not specified +- There's an issue initializing the Azure client. +- There's a problem downloading or reading the file from Azure Blob Storage. + +## Example +```go showLineNumbers +awsConfig, _ := config.LoadDefaultConfig(context.Background()) +err := ffclient.Init(ffclient.Config{ + PollingInterval: 3 * time.Second, + Retriever: &azblobretriever.Retriever{ + Container: "your-container", + AccountName: "your-account-name", + AccountKey: "your-account-key", + Object: "your_location/feature-flags.json", + }, +}) +defer ffclient.Close() +``` +## Configuration fields +To configure your S3 file location: + +| Field | Description | +|-------------------|-------------------------------------------------| +| **`Container`** | Name of the Azure Blob Storage container | +| **`Object`** | Name of the feature flag file in the container | +| **`AccountName`** | Azure Storage Account Name | +| **`AccountKey`** | (Optional) Storage Account Key | +| **`ServiceURL`** | (Optional) Custom service URL | + +## Authentication Methods + +### 1. Shared Key Authentication + +```go +retriever := &azblobretriever.Retriever{ + Container: "your-container", + AccountName: "your-account-name", + AccountKey: "your-account-key", + Object: "feature-flags.json", +} +``` + +### 2. Microsoft Entra ID (Recommended) + +```go +retriever := &azblobretriever.Retriever{ + Container: "your-container", + AccountName: "your-account-name", + Object: "feature-flags.json", +} +``` + + diff --git a/website/versioned_docs/version-v1.40.0/go_module/store_file/bitbucket.md b/website/versioned_docs/version-v1.40.0/go_module/store_file/bitbucket.md new file mode 100644 index 00000000000..a62f367a226 --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/go_module/store_file/bitbucket.md @@ -0,0 +1,41 @@ +--- +sidebar_position: 7 +--- + +# Bitbucket + +The [**Bitbucket Retriever**](https://pkg.go.dev/github.com/thomaspoignant/go-feature-flag/retriever/bitbucketretriever/#Retriever) +will perform an HTTP Request to the Bitbucket API to get your flags. + +:::tip +Bitbucket has rate limits, be sure to correctly set your `PollingInterval` to avoid reaching the limit. +::: + +## Example + +```go showLineNumbers +err := ffclient.Init(ffclient.Config{ + PollingInterval: 3 * time.Second, + Retriever: &bitbucketretriever.Retriever{ + RepositorySlug: "thomaspoignant/go-feature-flag", + Branch: "main", + FilePath: "testdata/flag-config.goff.yaml", + BitBucketToken: "XXXX", + Timeout: 2 * time.Second, + }, +}) +defer ffclient.Close() +``` + +## Configuration fields + +To configure the access to your GitLab file: + +| Field | Description | +|----------------------|-------------------------------------------------------------------------------------------------------| +| **`BaseURL`** | *(optional)*
The domain name of your Bitbucket instance
Default: `https://api.bitbucket.org` | +| **`RepositorySlug`** | Your Gitlab slug `org/repo-name`. | +| **`FilePath`** | The path of your file. | +| **`Branch`** | *(optional)*
The branch where your file is.
Default: `main` | +| **`BitBucketToken`** | *(optional)*
Bitbucket token is used to access a private repository | +| **`Timeout`** | *(optional)*
Timeout for the HTTP call
Default: 10 seconds | diff --git a/website/versioned_docs/version-v1.40.0/go_module/store_file/custom.md b/website/versioned_docs/version-v1.40.0/go_module/store_file/custom.md new file mode 100644 index 00000000000..f930e7f4d9d --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/go_module/store_file/custom.md @@ -0,0 +1,38 @@ +--- +sidebar_position: 30 +--- + +# Custom Retriever + +## Simple retriever +To create a custom retriever you must have a `struct` that implements the [`Retriever`](https://pkg.go.dev/github.com/thomaspoignant/go-feature-flag/retriever/#Retriever) interface. + +```go showLineNumbers +type Retriever interface { + Retrieve(ctx context.Context) ([]byte, error) +} +``` + +The `Retrieve` function is supposed to load the file and to return a `[]byte` of your flag configuration file. + +You can check existing `Retriever` *([file](https://github.com/thomaspoignant/go-feature-flag/blob/main/retriever/fileretriever/retriever.go), +[s3](https://github.com/thomaspoignant/go-feature-flag/blob/main/retriever/s3retriever/retriever.go), ...)* to have an idea on how to do build your own. + +## Initializable retriever +Sometimes you need to initialize your retriever before using it. +For example, if you want to connect to a database, you need to initialize the connection before using it. + +To help you with that, you can use the [`InitializableRetriever`](https://pkg.go.dev/github.com/thomaspoignant/go-feature-flag/retriever/#InitializableRetriever) interface. + +The only difference with the `Retriever` interface is that the `Init` func of your retriever will be called at the start of the application and the `Shutdown` func will be called when closing GO Feature Flag. + +```go +type InitializableRetriever interface { + Retrieve(ctx context.Context) ([]byte, error) + Init(ctx context.Context, logger *fflog.FFLogger) error + Shutdown(ctx context.Context) error + Status() retriever.Status +} +``` +To avoid any issue to call the `Retrieve` function before the `Init` function, you have to manage the status of your retriever. +GO Feature Flag will try to call the `Retrieve` function only if the status is `RetrieverStatusReady`. diff --git a/website/versioned_docs/version-v1.40.0/go_module/store_file/file.md b/website/versioned_docs/version-v1.40.0/go_module/store_file/file.md new file mode 100644 index 00000000000..402609fdacc --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/go_module/store_file/file.md @@ -0,0 +1,31 @@ +--- +sidebar_position: 25 +--- + +# File +The [**File Retriever**](https://pkg.go.dev/github.com/thomaspoignant/go-feature-flag/retriever/fileretriever/#Retriever) will read a local file to get your flags. + +:::tip +Using a file to store your flags is not recommend, except if it is in a shared folder for all your services. +::: + +## Example +```go showLineNumbers +import "github.com/thomaspoignant/go-feature-flag/retriever/file" +// ... + +err := ffclient.Init(ffclient.Config{ + PollingInterval: 3 * time.Second, + Retriever: &fileretriever.Retriever{ + Path: "file-example.yaml", + }, +}) +defer ffclient.Close() +``` + +## Configuration fields +To configure your File retriever: + +| Field | Description | +|---|---| +|**`Path`**| location of your file on the file system.| diff --git a/website/versioned_docs/version-v1.40.0/go_module/store_file/github.md b/website/versioned_docs/version-v1.40.0/go_module/store_file/github.md new file mode 100644 index 00000000000..ca2167f7c75 --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/go_module/store_file/github.md @@ -0,0 +1,42 @@ +--- +sidebar_position: 6 +--- + +# GitHub + +The [**GitHub Retriever**](https://pkg.go.dev/github.com/thomaspoignant/go-feature-flag/retriever/githubretriever/#Retriever) +will perform an HTTP Request with your GitHub configuration to get your flags. + +:::tip +GitHub has rate limits, be sure to correctly set your `PollingInterval` to avoid reaching the limit. + +If the rate limit is reached, the retriever will return an error and will stop polling until GitHub allows it again. +::: + +## Example + +```go showLineNumbers +err := ffclient.Init(ffclient.Config{ + PollingInterval: 3 * time.Second, + Retriever: &githubretriever.Retriever{ + RepositorySlug: "thomaspoignant/go-feature-flag", + Branch: "main", + FilePath: "testdata/flag-config.goff.yaml", + GithubToken: "XXXX", + Timeout: 2 * time.Second, + }, +}) +defer ffclient.Close() +``` + +## Configuration fields + +To configure the access to your GitHub file: + +| Field | Description | +|----------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| **`RepositorySlug`** | Your GitHub slug `org/repo-name`. | +| **`FilePath`** | The path of your file. | +| **`Branch`** | *(optional)*
The branch where your file is.
Default: `main` | +| **`GithubToken`** | *(optional)*
Github token is used to access a private repository, you need the `repo` permission *([how to create a GitHub token](https://docs.github.com/en/free-pro-team@latest/github/authenticating-to-github/creating-a-personal-access-token))*. | +| **`Timeout`** | *(optional)*
Timeout for the HTTP call
Default: 10 seconds | diff --git a/website/versioned_docs/version-v1.40.0/go_module/store_file/gitlab.md b/website/versioned_docs/version-v1.40.0/go_module/store_file/gitlab.md new file mode 100644 index 00000000000..12230355af7 --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/go_module/store_file/gitlab.md @@ -0,0 +1,42 @@ +--- +sidebar_position: 6 +--- + +# GitLab + +The [**GitLab Retriever**](https://pkg.go.dev/github.com/thomaspoignant/go-feature-flag/retriever/gitlabretriever/#Retriever) +will perform an HTTP Request to the GitLab API to get your flags. + +:::tip +GitLab has rate limits, be sure to correctly set your `PollingInterval` to avoid reaching the limit. +::: + +## Example + +```go showLineNumbers +err := ffclient.Init(ffclient.Config{ + PollingInterval: 3 * time.Second, + Retriever: &gitlabretriever.Retriever{ + RepositorySlug: "thomaspoignant/go-feature-flag", + Branch: "main", + FilePath: "testdata/flag-config.goff.yaml", + GitlabToken: "XXXX", + Timeout: 2 * time.Second, + BaseURL: "https://gitlab.com", + }, +}) +defer ffclient.Close() +``` + +## Configuration fields + +To configure the access to your GitLab file: + +| Field | Description | +|----------------------|---------------------------------------------------------------------------------------------| +| **`BaseURL`** | *(optional)*
The domain name of your Gitlab instance
Default: `https://gitlab.com` | +| **`RepositorySlug`** | Your Gitlab slug `org/repo-name`. | +| **`FilePath`** | The path of your file. | +| **`Branch`** | *(optional)*
The branch where your file is.
Default: `main` | +| **`GitlabToken`** | *(optional)*
GitLab token is used to access a private repository | +| **`Timeout`** | *(optional)*
Timeout for the HTTP call
Default: 10 seconds | diff --git a/website/versioned_docs/version-v1.40.0/go_module/store_file/google_cloud_storage.md b/website/versioned_docs/version-v1.40.0/go_module/store_file/google_cloud_storage.md new file mode 100644 index 00000000000..23f8c8d5159 --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/go_module/store_file/google_cloud_storage.md @@ -0,0 +1,31 @@ +--- +sidebar_position: 5 +--- + +# Google Cloud Storage + +The [**Google Cloud Storage Retriever**](https://pkg.go.dev/github.com/thomaspoignant/go-feature-flag/retriever/gcstorageretriever/#Retriever) will use the [google-cloud-storage package](https://pkg.go.dev/cloud.google.com/go/storage) and [google-api-options package](https://pkg.go.dev/google.golang.org/api/option) to access your flag in Google Cloud Storage. + +## Example + +```go +err := ffclient.Init(ffclient.Config{ + PollingInterval: 3 * time.Second, + Retriever: &gcstorageretriever.Retriever{ + Options: []option.ClientOption{option.WithoutAuthentication()}, + Bucket: "2093u4pkasjc3", + Object: "flags.yaml", + } +}) +defer ffclient.Close() +``` + +## Configuration fields + +To configure your Google Cloud Storage file location: + +| Field | Description | +|--------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| **`Bucket`** | The name of your bucket. | +| **`Object`** | The name of your object in your bucket. | +| **`Option`** | An instance of `option.ClientOption` that configures your access to Google Cloud.
Check [this documentation for more info](https://cloud.google.com/docs/authentication). | diff --git a/website/versioned_docs/version-v1.40.0/go_module/store_file/http.md b/website/versioned_docs/version-v1.40.0/go_module/store_file/http.md new file mode 100644 index 00000000000..1b2da3679d0 --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/go_module/store_file/http.md @@ -0,0 +1,33 @@ +--- +sidebar_position: 2 +--- + +# HTTP endpoint + +The [__HTTP Retriever__](https://pkg.go.dev/github.com/thomaspoignant/go-feature-flag/retriever/httpretriever/#Retriever) +will perform an HTTP Request with your configuration to get your flags. + +## Example + +```go showLineNumbers +err := ffclient.Init(ffclient.Config{ + PollingInterval: 3 * time.Second, + Retriever: &httpretriever.Retriever{ + URL: "http://example.com/flag-config.goff.yaml", + Timeout: 2 * time.Second, + }, +}) +defer ffclient.Close() +``` + +## Configuration fields + +To configure your HTTP endpoint: + +| Field | Description | +|---------------|-----------------------------------------------------------------------------------------------------------------| +| __`URL`__ | Location to retrieve the file
_(ex: [http://mydomain.io/flag.yaml](http://mydomain.io/flag.yaml))_. | +| __`Method`__ | the HTTP method you want to use
_(default is GET)_. | +| __`Body`__ | _(optional)_
If you need a body to get the flags. | +| __`Header`__ | _(optional)_
Header you should pass while calling the endpoint _(useful for authorization)_. | +| __`Timeout`__ | _(optional)_
Timeout for the HTTP call
(default is 10 seconds). | diff --git a/website/versioned_docs/version-v1.40.0/go_module/store_file/index.mdx b/website/versioned_docs/version-v1.40.0/go_module/store_file/index.mdx new file mode 100644 index 00000000000..b969d23502a --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/go_module/store_file/index.mdx @@ -0,0 +1,37 @@ +--- +sidebar_position: 1 +--- +import DocCardList from '@theme/DocCardList'; + +# Retrieve your feature flags configuration +The module supports different ways of retrieving the flag file. + +## Available retrievers + + + +To retrieve a file you need to provide a [retriever](https://pkg.go.dev/github.com/thomaspoignant/go-feature-flag/retriever/#Retriever) in your `ffclient.Config{}` during the initialization. +If the existing retriever does not work with your system you can extend the system and use a [custom retriever](custom.md). + + +## Explicitly call the retrievers +By default, the retrievers are called regularly to refresh the configuration based on the polling interval. + +But there are use cases where you want to refresh the configuration explicitly _(for example, during the CI process +after you have changed your configuration file)_. + +To do that, you can call the `ForceRefresh` method on the client. + +```go +// Init ffclient with a file retriever. + goff, _ := ffclient.New(ffclient.Config{ + PollingInterval: 10 * time.Minute, + Retriever: &fileretriever.Retriever{ + Path: "xxxx.yaml", + }, + }) + + // ... + goff.ForceRefresh() + // ... +``` diff --git a/website/versioned_docs/version-v1.40.0/go_module/store_file/kubernetes_configmaps.md b/website/versioned_docs/version-v1.40.0/go_module/store_file/kubernetes_configmaps.md new file mode 100644 index 00000000000..75806bb6e5e --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/go_module/store_file/kubernetes_configmaps.md @@ -0,0 +1,47 @@ +--- +sidebar_position: 4 +--- + +# Kubernetes configmaps +A ConfigMap is an API object used to store non-confidential data in key-value pairs inside kubernetes. +GO Feature Flag can read directly in a configmap in your namespace. + +The [**Kubernetes Retriever**](https://pkg.go.dev/github.com/thomaspoignant/go-feature-flag/retriever/k8sretriever/#Retriever) +will access flags in a Kubernetes ConfigMap via the [Kubernetes Go Client](https://github.com/kubernetes/client-go). + + +## Add your config file as configmap + +```shell +kubectl create configmap goff --from-file=examples/retriever_configmap/flags.yaml +``` + +## Configuration Example +```go showLineNumbers +import ( + restclient "k8s.io/client-go/rest" +) + +config, _ := restclient.InClusterConfig() +err = ffclient.Init(ffclient.Config{ + PollingInterval: 3 * time.Second, + Retriever: &k8sretriever.Retriever{ + Path: "file-example.yaml", + Namespace: "default" + ConfigMapName: "my-configmap" + Key: "somekey.yml" + ClientConfig: &config + }, +}) +defer ffclient.Close() +``` + +## Configuration fields +To configure your retriever: + +| Field | Description | +|---------------------|----------------------------------------------------| +| **`Namespace`** | The namespace of the ConfigMap. | +| **`ConfigMapName`** | The name of the ConfigMap. | +| **`Key`** | The key within the ConfigMap storing the flags. | +| **`ClientConfig`** | The configuration object for the Kubernetes client | diff --git a/website/versioned_docs/version-v1.40.0/go_module/store_file/mongodb.md b/website/versioned_docs/version-v1.40.0/go_module/store_file/mongodb.md new file mode 100644 index 00000000000..20568bd5fed --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/go_module/store_file/mongodb.md @@ -0,0 +1,54 @@ +--- +sidebar_position: 7 +--- + +# MongoDB +The `mongodbRetriever` will use the mongoDB database to get your flags. + +## Example +```go linenums="1" +err := ffclient.Init(ffclient.Config{ + PollingInterval: 3 * time.Second, + Retriever: &mongodbretriever.Retriever{ + Collection: "featureFlags", + Database: "appConfig", + URI: "mongodb://root:example@127.0.0.1:27017/", + }, +}) +defer ffclient.Close() +``` + +## Expected format +If you use MongoDB to store your flags, you need a specific format to store your flags. + +We expect the flag to be stored in JSON format as defined in the [flag format](../../configure_flag/flag_format#format-details), +but you should also add a new field called `flag` containing the name of the flag. + +The retriever will read all the flags from the collection. + +### Example: +```json +{ + "flag": "new-admin-access", + "variations": { + "default_var": false, + "false_var": false, + "true_var": true + }, + "defaultRule": { + "percentage": { + "false_var": 70, + "true_var": 30 + } + } +} +``` + +## Configuration fields +To configure your mongodb retriever: + +| Field | Description | +|------------------|-------------------------------------------------------------| +| **`Collection`** | Name of the collection where your flags are stored | +| **`Database`** | Name of the mongo database where the collection is located. | +| **`URI`** | Connection URI of your mongoDB instance. | diff --git a/website/versioned_docs/version-v1.40.0/go_module/store_file/redis.md b/website/versioned_docs/version-v1.40.0/go_module/store_file/redis.md new file mode 100644 index 00000000000..1ac91ea5483 --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/go_module/store_file/redis.md @@ -0,0 +1,36 @@ +--- +sidebar_position: 7 +--- + +# Redis +The `redisRetriever` will use the redis database to get your flags. + +## Example +```go linenums="1" +err := ffclient.Init(ffclient.Config{ + PollingInterval: 3 * time.Second, + Retriever: &redisRetriver.Retriever{ + Prefix: "goff:", + Options: &redis.Options{ + Addr: "127.0.0.1:6379", + }, + }, +}) +defer ffclient.Close() +``` + +## Expected format +If you use Redis to store your flags, you need a specific format to store your flags. + +We expect the flag to be stored as a `string:string` format where the key if the flag key (with or without a prefix) +and the value is a string representing the flag in JSON. + +The retriever will `Scan` redis filtering with the `Prefix` and will parse the value as a JSON object. +` +## Configuration fields +To configure your redis retriever: + +| Field | Description | +|---------------|---------------------------------------------------------------------------------------| +| **`Options`** | A `redis.Options` object containing the connection information to the redis instance. | +| **`Prefix`** | (optional) Key prefix to filter on the key names. | diff --git a/website/versioned_docs/version-v1.40.0/go_module/store_file/s3.md b/website/versioned_docs/version-v1.40.0/go_module/store_file/s3.md new file mode 100644 index 00000000000..c339b0ddc11 --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/go_module/store_file/s3.md @@ -0,0 +1,33 @@ +--- +sidebar_position: 3 +--- + +# S3 Bucket +The [**S3 Retriever v2**](https://pkg.go.dev/github.com/thomaspoignant/go-feature-flag@v1.23.1/retriever/s3retrieverv2) will use the [aws-sdk-go-v2](https://github.com/aws/aws-sdk-go-v2) to access your flag in an S3 bucket. + +The [**S3 Retriever v1**](https://pkg.go.dev/github.com/thomaspoignant/go-feature-flag@v1.23.1/retriever/s3retriever) will use the deprecated [aws-sdk-go](https://github.com/aws/aws-sdk-go) to access your flag in an S3 bucket. + +[AWS has announce end-of-support for AWS SDK for Go v1](https://aws.amazon.com/blogs/developer/announcing-end-of-support-for-aws-sdk-for-go-v1-on-july-31-2025/), and it's recommended to migrate from S3 Retriever v1 to v2. + +## Example +```go showLineNumbers +awsConfig, _ := config.LoadDefaultConfig(context.Background()) +err := ffclient.Init(ffclient.Config{ + PollingInterval: 3 * time.Second, + Retriever: &s3retrieverv2.Retriever{ + Bucket: "tpoi-test", + Item: "flag-config.goff.yaml", + AwsConfig: &awsConfig, + }, +}) +defer ffclient.Close() +``` + +## Configuration fields +To configure your S3 file location: + +| Field | Description | +|-----------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| **`Bucket`** | The name of your bucket. | +| **`Item`** | The location of your file in the bucket. | +| **`AwsConfig`** | An instance of `aws.Config` that configure your access to AWS
*check [this documentation for more info](https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html)*. | diff --git a/website/versioned_docs/version-v1.40.0/go_module/target_user.md b/website/versioned_docs/version-v1.40.0/go_module/target_user.md new file mode 100644 index 00000000000..bd801cd43ec --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/go_module/target_user.md @@ -0,0 +1,186 @@ +--- +sidebar_position: 20 +description: How to select who should have the flag activated. +--- +# Performing flag evaluations + +## Users +Feature flag targeting and rollouts are all determined by the user you pass to your **Variation** calls. +The SDK defines a [`User`](https://pkg.go.dev/github.com/thomaspoignant/go-feature-flag/ffuser#User) struct and a [`UserBuilder`](https://pkg.go.dev/github.com/thomaspoignant/go-feature-flag/ffuser#UserBuilder) to make this easy. + +Here's an example: + +```go showLineNumbers +// User with only a key +user1 := ffcontext.NewEvaluationContext("user1-key") + +// User with a key plus other attributes +user2 = ffcontext.NewEvaluationContextBuilder("user2-key"). + AddCustom("firstname", "John"). + AddCustom("lastname", "Doe"). + AddCustom("email", "john.doe@example.com"). + Build() +``` + +The most common attribute is the user's key and **this is the only mandatory user attribute.** +The key should also uniquely identify each user. You can use a primary key, an e-mail address, or a hash, as long as the same user always has the same key. +**We recommend using a hash if possible.** +All the other attributes are optional. + +:::info +Custom attributes are one of the most powerful features. +They let you have rules on these attributes and target users according to any data that you want. +::: + +## Anonymous users +You can also distinguish logged-in users from anonymous users in the SDK, as follows: + +```go showLineNumbers +// User with only a key +user1 := ffcontext.NewAnonymousEvaluationContext("user1-key") + +// User with a key plus other attributes +user2 = ffcontext.NewEvaluationContextBuilder("user2-key"). + Anonymous(true). + AddCustom("firstname", "John"). + AddCustom("lastname", "Doe"). + AddCustom("email", "john.doe@example.com"). + Build() +``` +You will still need to generate a unique key for anonymous users. Session IDs or UUIDs work best for this. + +Anonymous users work just like regular users, this information just helps you to add a rule to target a specific population. + +## Variation +The Variation methods determine whether a flag is enabled or not for a specific user. +There is a Variation method for each type: +[`BoolVariation`](https://pkg.go.dev/github.com/thomaspoignant/go-feature-flag#BoolVariation) , [`IntVariation`](https://pkg.go.dev/github.com/thomaspoignant/go-feature-flag#IntVariation) +, [`Float64Variation`](https://pkg.go.dev/github.com/thomaspoignant/go-feature-flag#Float64Variation) +, [`StringVariation`](https://pkg.go.dev/github.com/thomaspoignant/go-feature-flag#StringVariation) +, [`JSONArrayVariation`](https://pkg.go.dev/github.com/thomaspoignant/go-feature-flag#JSONArrayVariation) +, [`JSONVariation`](https://pkg.go.dev/github.com/thomaspoignant/go-feature-flag#JSONVariation) + +```go showLineNumbers +result, _ := ffclient.BoolVariation("your.feature.key", user, false) + +// result is now true or false depending on the setting of +// this boolean feature flag +``` +Variation methods take the feature **flag key**, a **user**, and a **default value**. + +The default value is returned when an error is encountered _(`ffclient` not initialized, variation with wrong type, flag does not exist ...)._ + +In the example, if the flag `your.feature.key` does not exist, result will be `false`. +Not that you will always have a usable value in the result. + +## Variation details +If you want more information about your flag evaluation, you can use the variation details functions. +There is a Variation method for each type: +[`BoolVariationDetails`](https://pkg.go.dev/github.com/thomaspoignant/go-feature-flag#BoolVariationDetails) +, [`IntVariation`](https://pkg.go.dev/github.com/thomaspoignant/go-feature-flag#IntVariationDetails) +, [`Float64VariationDetails`](https://pkg.go.dev/github.com/thomaspoignant/go-feature-flag#Float64VariationDetails) +, [`StringVariationDetails`](https://pkg.go.dev/github.com/thomaspoignant/go-feature-flag#StringVariationDetails) +, [`JSONArrayVariationDetails`](https://pkg.go.dev/github.com/thomaspoignant/go-feature-flag#JSONArrayVariationDetails) +, [`JSONVariationDetails`](https://pkg.go.dev/github.com/thomaspoignant/go-feature-flag#JSONVariationDetails) + +You can use these functions the same way as the other variation functions BUT it will return a generic object `model.VariationResult[]` containing your result. +This object will contain these fields: + +| field | type | description | +|-----------------|-------------------------|--------------------------------------------------------------------------------| +| `TrackEvents` | `bool` | `true` if this evaluation was tracked. | +| `VariationType` | `string` | The name of the variation used to get this value. | +| `Failed` | `bool` | `true` if an error occurred during the evaluation. | +| `Version` | `string` | The **version** of the flag used to do the evaluation. | +| `Reason` | `flag.ResolutionReason` | The reason used for this evaluation. | +| `ErrorCode` | `flag.ErrorCode` | Error code in case we have an error. | +| `ErrorDetails` | `string` | A string providing more detail about the error. | +| `Value` | `` | Value of the flag in the expected type. | +| `Cacheable` | `bool` | `true` if it can be cached (by user or for everyone depending on the reason). | + + +### Reason +GO Feature Flag can furnish you with diverse reasons in the variation details, giving you insight into the evaluation process of your feature flag. +Here is the full list of reason available: + +| Reason | description | +|-------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `TARGETING_MATCH` | The resolved value was the result of a dynamic evaluation, such as a rule or specific user-targeting. _(ex: serve variation A if username is Thomas)_ | +| `TARGETING_MATCH_SPLIT` | The resolved value was the result of a dynamic evaluation, that is serving a percentage. _(ex: serve variation A to 10% of users with the username Thomas)_ | +| `SPLIT` | The resolved value was the result of pseudorandom assignment. _(ex: serve variation A to 10% of all the users.)_ | +| `DISABLED` | Indicates that the feature flag is disabled | +| `DEFAULT` | The resolved value was the result of the flag being disabled in the management system. | +| `STATIC` | Indicates that the feature flag evaluated to a static value, for example, the default value for the flag. _(Note: Typically means that no dynamic evaluation has been executed for the feature flag)_ | +| `UNKNOWN` | Indicates that an unknown issue occurred during evaluation | +| `ERROR` | Indicates that an error occurred during evaluation *(Note: The `errorCode` field contains the details of this error)* | +| `OFFLINE` | Indicates that GO Feature Flag is currently evaluating in offline mode. | + + +## Get all flags for a specific user +If you want to send the information about a specific user to the front-end, you will need a snapshot of all the flags of this user at a specific time. + +The method `ffclient.AllFlagsState` returns a snapshot of flag values and metadata. +The function is evaluating all available flags for the user and returns a `flagstate.AllFlagsState` object containing the information you need. + +```go showLineNumbers +user := ffcontext.NewEvaluationContext("example") +// AllFlagsState will give you the value for all the flags available. +allFlagsState := ffclient.AllFlagsState(u) + +// If you want to send it to the front-end you can Marshal it by calling MarshalJSON() +forFE, err := allFlagsState.MarshalJSON() +``` + +The `MarshalJSON()` function will return something like below, that can be directly used by your front-end application. +```json showLineNumbers +{ + "flags": { + "test-flag0": { + "value": true, + "timestamp": 1622209328, + "variationType": "True", + "trackEvents": true + }, + "test-flag1": { + "value": "true", + "timestamp": 1622209328, + "variationType": "True", + "trackEvents": true + }, + "test-flag2": { + "value": 1, + "timestamp": 1622209328, + "variationType": "True", + "trackEvents": true + }, + "test-flag3": { + "value": [ + "yo", + "ya" + ], + "timestamp": 1622209328, + "variationType": "True", + "trackEvents": true + }, + "test-flag4": { + "value": { + "test": "yo" + }, + "timestamp": 1622209328, + "variationType": "True", + "trackEvents": true + }, + "test-flag5": { + "value": 1.1, + "timestamp": 1622209328, + "variationType": "True", + "trackEvents": false + } + }, + "valid": true +} +``` + +:::caution +There is no tracking done when evaluating all the flag at once. +::: diff --git a/website/versioned_docs/version-v1.40.0/index.md b/website/versioned_docs/version-v1.40.0/index.md new file mode 100644 index 00000000000..a1f1e8f0a41 --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/index.md @@ -0,0 +1,36 @@ +--- +title: Home +description: go-feature-flag is a simple and complete feature flag solution, without any complex backend system to install. You need only a file as your backend. +sidebar_position: 1 +--- + +

+ go-feature-flag logo +

+ +## What is GO Feature Flag? +GO Feature Flag is a completely open-source, simple and lightweight feature flag solution. + +The solution has been built for application of feature flags in your code without the need of any vendor. + +**GO Feature Flag** was initially developed for the GO language, but with the new standardisation of feature flags by [Openfeature](https://openfeature.dev/) project, it now supports multiple languages _(`JAVA`, `typescript`, `javascript`, ...)_ with a simple server to host. + +:::info +If you are not familiar with feature flags, also called feature toggles, you can read this [article from Martin Fowler](https://www.martinfowler.com/articles/feature-toggles.html) +where he explains why this is a great pattern. + +I've also written an [article](https://medium.com/better-programming/feature-flags-and-how-to-iterate-quickly-7e3371b9986) which explains why feature flags can accelerate your iteration cycle. +::: + +## What can I do with GO Feature Flag? + +- Storing your configuration flags file on various locations (`HTTP`, `S3`, `Kubernetes`, [_see full list_](configure_flag/store_your_flags.mdx)). +- Configuring your flags in various [format](configure_flag/flag_format.mdx) (`JSON`, `TOML` and `YAML`). +- Adding complex [rules](configure_flag/flag_format.mdx#rule-format) to target your users. +- Use complex rollout strategy for your flags : + - [Run A/B testing experimentation](configure_flag/rollout/experimentation.mdx). + - [Progressively rollout a feature](configure_flag/rollout/progressive.mdx). + - [Schedule your flag updates](configure_flag/rollout/scheduled.mdx). +- Exporting your flags usage data ([`s3`](go_module/data_collection/s3.md), [`log`](go_module/data_collection/log.md), [`file`](go_module/data_collection/file.md), [_see full list_](configure_flag/export_flags_usage.mdx)). +- Getting notified when a flag has been changed ([`webhook`](go_module/notifier/webhook.md) and [`slack`](go_module/notifier/slack.md)). +- **Cross-Language Support:** Available for use across several programming languages. diff --git a/website/versioned_docs/version-v1.40.0/openfeature_sdk/_category_.json b/website/versioned_docs/version-v1.40.0/openfeature_sdk/_category_.json new file mode 100644 index 00000000000..fcfbd961686 --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/openfeature_sdk/_category_.json @@ -0,0 +1,6 @@ +{ + "position": 40, + "label":"Use with OpenFeature", + "collapsible": true, + "collapsed": true +} diff --git a/website/versioned_docs/version-v1.40.0/openfeature_sdk/client_providers/_category_.json b/website/versioned_docs/version-v1.40.0/openfeature_sdk/client_providers/_category_.json new file mode 100644 index 00000000000..0e8feb86373 --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/openfeature_sdk/client_providers/_category_.json @@ -0,0 +1,6 @@ +{ + "position": 20, + "label":"Client Providers", + "collapsible": true, + "collapsed": true +} diff --git a/website/versioned_docs/version-v1.40.0/openfeature_sdk/client_providers/openfeature_android.mdx b/website/versioned_docs/version-v1.40.0/openfeature_sdk/client_providers/openfeature_android.mdx new file mode 100644 index 00000000000..146bbfb61a2 --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/openfeature_sdk/client_providers/openfeature_android.mdx @@ -0,0 +1,209 @@ +--- +sidebar_position: 30 +title: Android / Kotlin +description: How to use the OpenFeature Kotlin SDK for your Android application +--- +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import versions from '@site/static/sdk-versions.json'; +import CodeBlock from '@theme/CodeBlock'; + +[![Maven Central Version](https://img.shields.io/maven-central/v/org.gofeatureflag.openfeature/gofeatureflag-kotlin-provider?color=blue&logo=android&style=flat-square)](https://search.maven.org/artifact/org.gofeatureflag.openfeature/gofeatureflag-kotlin-provider) + +This OpenFeature provider has a Kotlin implementation for Android to communicate with the GO Feature +Flag Server. + +In conjuction with the [OpenFeature SDK](https://openfeature.dev/docs/reference/technologies/client/kotlin) you will be able to evaluate your feature flags in your **Android** applications. + +For documentation related to flags management in GO Feature Flag, refer to the [GO Feature Flag documentation website](https://gofeatureflag.org/docs). + +### Functionalities: +- Manage the integration of the OpenFeature Android SDK and GO Feature Flag relay-proxy. +- Prefetch and cache flag evaluations in order to give the flag value in an efficient way. +- Automatic configuration changes polling, to be informed as soon as a flag configuration has changed. +- Automatic data collection about which flags have been accessed by the application + +## Dependency Setup + + + { +`api("dev.openfeature:android-sdk:${versions.maven.android}") +api("org.gofeatureflag.openfeature:gofeatureflag-kotlin-provider${versions.maven.providerKt}")` +} + + +## Getting started + +### Initialize the provider + +GO Feature Flag provider needs to be created and then set in the global OpenFeatureAPI. + +The only required option to create a `GoFeatureFlagProvider` is the URL to your GO Feature Flag relay-proxy instance. + +```kotlin +import org.gofeatureflag.openfeature.bean.GoFeatureFlagOptions +import org.gofeatureflag.openfeature.GoFeatureFlagProvider +// ... + +val evaluationContext: EvaluationContext = ImmutableContext( + targetingKey = UUID.randomUUID().toString(), + attributes = mapOf( "age" to Value.Integer(22), "email" to Value.String("contact@gofeatureflag.org")) + ) + +OpenFeatureAPI.setProvider( + GoFeatureFlagProvider( + options = GoFeatureFlagOptions( endpoint = "http://localhost:1031") + ), + evaluationContext +) + +``` + +The evaluation context is the way for the client to specify contextual data that GO Feature Flag uses to evaluate the feature flags, it allows defining rules on the flag. + +The `targetingKey` is mandatory for GO Feature Flag in order to evaluate the feature flag, it could be the id of a user, a session ID or anything you find relevent to use as identifier during the evaluation. + +The `setProvider()` function is synchronous and returns immediately however, this does not mean that the provider is ready to be used. +An asynchronous network request to the GO Feature Flag backend to fetch all the flags configured for your application must be completed by the provider first. The provider will then emit a `READY` event indicating you can start resolving flags. + +If you prefer to wait until the fetch is done you can use the `suspend` compatible API available for waiting the Provider to become ready: + +```kotlin +runBlocking{ + OpenFeatureAPI.shared.setProviderAndWait( + provider = provider, + dispatcher = Dispatchers.IO, + initialContext = evaluationContext + ) +} +``` + +### Available options + +When initializing the provider, you can pass some options to configure the provider and how it behaves with GO Feature Flag. + +| Option name | Type | Default | Description | +|---------------------------|--------|---------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `endpoint` | String | | endpoint is the URL where your GO Feature Flag server is located. | +| `timeout` | Long | 10000 | (optional) timeout is the time in millisecond we wait for an answer from the server. | +| `maxIdleConnections` | Int | 1000 | (optional) maxIdleConnections is the maximum number of connexions in the connexion pool. | +| `keepAliveDuration` | Long | 7200000 | (optional) keepAliveDuration is the time in millisecond we keep the connexion open. | +| `apiKey` | String | | (optional) If GO Feature Flag is configured to authenticate the requests, you should provide an API Key to the provider. Please ask the administrator of the relay proxy to provide an API Key. | +| `pollingIntervalInMillis` | Long | 300000 | (optional) Polling interval in millisecond to check with GO Feature Flag relay proxy if there is a flag configuration change. | +| `flushIntervalMs` | Long | 1000 | (optional) Time to wait before calling GO Feature Flag to store all the data about the evaluation in the relay proxy. | + +### Update the Evaluation Context + +During the usage of your application it may appear that the `EvaluationContext` should be updated. For example, if a not logged-in user authentify himself, you will probably have to update the evaluation context. + +```kotlin +val newContext: EvaluationContext = ImmutableContext( + targetingKey = userId, + attributes = mapOf( "age" to Value.Integer(32), "email" to Value.String("batman@gofeatureflag.org")) +) + +OpenFeatureAPI.setEvaluationContext(newEvalCtx) +``` + +`setEvaluationContext()` is a synchronous function similar to `setProvider()` and will fetch the new version of the feature flags based on this new `EvaluationContext`. + +### Limit the flags to evaluate + +By default, the provider will fetch all the flags configured in the GO Feature Flag server to be ready to evaluate them. +If you know in advance, what are the flags you will evaluate in your application, you can specify the list of flags to evaluate in the context. + +You need to add in the evaluation context the restricted key `gofeatureflag.flagList` with the list of flags you want to evaluate. + +```kotlin +val newContext: EvaluationContext = ImmutableContext( + targetingKey = "userId", + attributes = mapOf( + "gofeatureflag" to Value.Structure( + mapOf( + "flagList" to Value.List( + listOf( + // list of flags to evaluate + Value.String("flag1"), + Value.String("flag2"), + Value.String("flag3") + ) + ), + ) + ), + ) + ) + +OpenFeatureAPI.setEvaluationContext(newEvalCtx) +``` + +By setting the `gofeatureflag.flagList` key in the context, the provider will only fetch the flags specified in the list. + +:::warning +When limiting the flags to evaluate, if you try to evaluate a flag not in the list, the provider will return the default value with the error `FLAG_NOT_FOUND`. +::: + +### Evaluate a feature flag +The client is used to retrieve values for the current `EvaluationContext`. For example, retrieving a boolean value for the flag **"my-flag"**: + +```kotlin +val client = OpenFeatureAPI.getClient() +val result = client.getBooleanValue("my-flag", false) +``` + +GO Feature Flag supports different all OpenFeature supported types of feature flags, it means that you can use all the accessor directly +```kotlin +// Bool +client.getBooleanValue("my-flag", false) + +// String +client.getStringValue("my-flag", "default") + +// Integer +client.getIntegerValue("my-flag", 1) + +// Double +client.getDoubleValue("my-flag", 1.1) + +// Object +client.getObjectValue("my-flag", Value.structure(mapOf("email" to Value.String("contact@gofeatureflag.org")))) +``` + +> [!NOTE] +> If you add a new flag in GO Feature Flag, expect some delay before having it available for the provider. +> Refreshing the cache from remote happens when setting a new provider and/or evaluation context in the global OpenFeatureAPI, but also when a configuration change is detected during the polling. + +### Handling Provider Events + +When setting the provider or the context *(via `setEvaluationContext()` or `setProvider()`)* some events can be triggered to know the state of the provider. + +To listen to them, you can add an event handler via the `OpenFeatureAPI` shared instance: + +```kotlin +val coroutineScope = CoroutineScope(Dispatchers.IO) +coroutineScope.launch { + provider.observe().collect { + providerStaleEventReceived = true + } +} +``` + +#### Existing type of events are: +- `ProviderReady`: Provider is ready. +- `ProviderError`: Provider in error. +- `ProviderStale`: Provider has not the latest version of the feature flags. +- `ProviderNotReady`: Provider is not ready to evaluate the feature flags. + +## Features status + +| Status | Feature | Description | +|--------|--------------------|--------------------------------------------------------------------------------------| +| ✅ | Flag evaluation | It is possible to evaluate all the type of flags | +| ✅ | Cache invalidation | Websocket mechanism is in place to refresh the cache in case of configuration change | +| ❌ | Logging | Not supported by the SDK | +| ❌ | Flag Metadata | Not supported by the SDK | +| ✅ | Event Streaming | Not implemented | +| ✅ | Unit test | Not implemented | + +Implemented: ✅ | In-progress: ⚠️ | Not implemented yet: ❌ \ No newline at end of file diff --git a/website/versioned_docs/version-v1.40.0/openfeature_sdk/client_providers/openfeature_javascript.mdx b/website/versioned_docs/version-v1.40.0/openfeature_sdk/client_providers/openfeature_javascript.mdx new file mode 100644 index 00000000000..f3d899ce3a1 --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/openfeature_sdk/client_providers/openfeature_javascript.mdx @@ -0,0 +1,109 @@ +--- +sidebar_position: 10 +title: Javascript / Typescript +description: How to use the OpenFeature Javascript web SDK for your client application +--- +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +# Javascript / Typescript SDK usage +[![NPM Version](https://img.shields.io/npm/v/%40openfeature%2Fgo-feature-flag-web-provider?color=blue&style=flat-square&logo=npm)](https://www.npmjs.com/package/@openfeature/go-feature-flag-web-provider) +![NPM Downloads](https://img.shields.io/npm/d18m/%40openfeature%2Fgo-feature-flag-web-provider?style=flat-square) + + +This page describes how to use the OpenFeature Javascript web SDK for your client application. + +## About this provider +[GO Feature Flag](https://gofeatureflag.org) provider allows you to connect to your GO Feature Flag instance with the `@openfeature/web-sdk`. + +The main difference between this provider and [`@openfeature/go-feature-flag-provider`](https://www.npmjs.com/package/@openfeature/go-feature-flag-provider) is that it uses a **static evaluation context**. +This provider is more sustainable for client-side implementation. + +If you want to know more about this pattern, I encourage you to read this [blog post](https://openfeature.dev/blog/catering-to-the-client-side/). + +## Install the provider + +```shell +npm install @openfeature/go-feature-flag-web-provider @openfeature/web-sdk +``` + +## How to use the provider? +```typescript +const evaluationCtx: EvaluationContext = { + targetingKey: 'user-key', + email: 'john.doe@gofeatureflag.org', + name: 'John Doe', +}; + +const goFeatureFlagWebProvider = new GoFeatureFlagWebProvider({ + endpoint: endpoint, + // ... +}, logger); + +await OpenFeature.setContext(evaluationCtx); // Set the static context for OpenFeature +OpenFeature.setProvider(goFeatureFlagWebProvider); // Attach the provider to OpenFeature +const client = await OpenFeature.getClient(); + +// You can now use the client to use your flags +if(client.getBooleanValue('my-new-feature', false)){ + //... +} + +// You can add handlers to know what happen in the provider +client.addHandler(ProviderEvents.Ready, () => { ... }); +client.addHandler(ProviderEvents.Error, () => { //... }); +client.addHandler(ProviderEvents.Stale, () => { //... }); +client.addHandler(ProviderEvents.ConfigurationChanged, () => { //... }); +``` + +### Limit the flags to evaluate + +By default, the provider will fetch all the flags configured in the GO Feature Flag server to be ready to evaluate them. +If you know in advance, what are the flags you will evaluate in your application, you can specify the list of flags to evaluate in the context. + +You need to add in the evaluation context the restricted key `gofeatureflag.flagList` with the list of flags you want to evaluate. + +```typescript +OpenFeature.setContext({ + // ... + gofeatureflag: { + flagList: ['flag1', 'flag2'] + } +}); + +await OpenFeature.setContext(evaluationCtx); +``` + +By setting the `gofeatureflag.flagList` key in the context, the provider will only fetch the flags specified in the list. + +:::warning +When limiting the flags to evaluate, if you try to evaluate a flag not in the list, the provider will return the default value with the error `FLAG_NOT_FOUND`. +::: + +### Available options +| Option name | Type | Default | Description | +|-------------------------------|--------|----------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| endpoint | string | | endpoint is the URL where your GO Feature Flag server is located. | +| apiTimeout | number | 0 = no timeout | (optional) timeout is the time in millisecond we wait for an answer from the server. | +| apiKey | string | | (optional) If GO Feature Flag is configured to authenticate the requests, you should provide an API Key to the provider. Please ask the administrator of the relay proxy to provide an API Key. | +| websocketRetryInitialDelay | number | 100 | (optional) initial delay in millisecond to wait before retrying to connect the websocket | +| websocketRetryDelayMultiplier | number | 2 | (optional) multiplier of websocketRetryInitialDelay after each failure _(example: 1st connection retry will be after 100ms, second after 200ms, third after 400ms ...)_ | +| websocketMaxRetries | number | 10 | (optional) maximum number of retries before considering the websocket unreachable | + +### Reconnection +If the connection to the GO Feature Flag instance fails, the provider will attempt to reconnect with an exponential back-off. +The `websocketMaxRetries` can be specified to customize reconnect behavior. + +### Event streaming +The `GoFeatureFlagWebProvider` receives events from GO Feature Flag with changes. +Combined with the event API in the web SDK, this allows for subscription to flag value changes in clients. + +```typescript +client.addHandler(ProviderEvents.ConfigurationChanged, (ctx: EventDetails) => { + // do something when the configuration has changed. + // ctx.flagsChanged contains the list of changed flags. +}); +``` + +## Contribute to the provider +You can find the source of the provider in the [`open-feature/js-sdk-contrib`](https://github.com/open-feature/js-sdk-contrib/tree/main/libs/providers/go-feature-flag-web) repository. \ No newline at end of file diff --git a/website/versioned_docs/version-v1.40.0/openfeature_sdk/client_providers/openfeature_react.mdx b/website/versioned_docs/version-v1.40.0/openfeature_sdk/client_providers/openfeature_react.mdx new file mode 100644 index 00000000000..ee32c2906ee --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/openfeature_sdk/client_providers/openfeature_react.mdx @@ -0,0 +1,94 @@ +--- +sidebar_position: 11 +title: React +description: How to use the OpenFeature Javascript React SDK for your React application +--- +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +# React SDK usage +[![NPM Version](https://img.shields.io/npm/v/%40openfeature%2Fgo-feature-flag-web-provider?color=blue&style=flat-square&logo=npm)](https://www.npmjs.com/package/@openfeature/go-feature-flag-web-provider) +![NPM Downloads](https://img.shields.io/npm/d18m/%40openfeature%2Fgo-feature-flag-web-provider?style=flat-square) + + +This page describes how to use the OpenFeature React SDK for your client application. + +## About this provider +OpenFeature React SDK allows you to connect to your GO Feature Flag instance with the `@openfeature/react-sdk`. + +If you work with React the `@openfeature/react-sdk` will give you a better integration with your React application. +To integrate it with GO Feature Flag, you need to use the `@openfeature/go-feature-flag-web-provider` provider. + + + +## Install the provider + +```shell +npm install @openfeature/go-feature-flag-web-provider +npm install @openfeature/web-sdk +npm install @openfeature/react-sdk +npm install @openfeature/core +``` + +## How to use the provider? + +### OpenFeatureProvider context provider +The OpenFeatureProvider is a React context provider which represents a scope for feature flag evaluations within a React application. +It binds an OpenFeature client to all evaluations within child components, and allows the use of evaluation hooks. + +```typescript +import { EvaluationContext, OpenFeature, OpenFeatureProvider, useFlag } from "@openfeature/react-sdk"; +import { GoFeatureFlagWebProvider } from "@openfeature/go-feature-flag-web-provider"; + +const goFeatureFlagWebProvider = new GoFeatureFlagWebProvider({ + endpoint: "http://localhost:1031" +}); + +// Set the initial context for your evaluations +OpenFeature.setContext({ + targetingKey: "user-1", + admin: false +}); + +// Instantiate and set our provider (be sure this only happens once)! +// Note: there's no need to await its initialization, the React SDK handles re-rendering and suspense for you! +OpenFeature.setProvider(goFeatureFlagWebProvider); + +// Enclose your content in the configured provider +function App() { + return ( + + + + ); +} +``` + +### Evaluation hooks + +Within the provider, you can use the various evaluation hooks to evaluate flags. +```typescript +function Page() { + // Use the "query-style" flag evaluation hook, specifying a flag-key and a default value. + const { value: showNewMessage } = useFlag('new-message', true); + return ( +
+
+ {showNewMessage ?

Welcome to this OpenFeature-enabled React app!

:

Welcome to this React app.

} +
+
+ ) +} +``` + +### Advanced usage +You can check the [OpenFeature React SDK documentation](https://openfeature.dev/docs/reference/technologies/client/web/react) to see all the available hooks and how to use them. + +## Available options +Check the available options for the provider in the [web provider page](./openfeature_javascript). + +## Example +If you want to see some code, you can check the [example](https://github.com/thomaspoignant/go-feature-flag/tree/main/examples/openfeature_react) in the GO Feature Flag repository. + +## Contribute to the provider +You can find the source of the provider in the [`open-feature/js-sdk-contrib`](https://github.com/open-feature/js-sdk-contrib/tree/main/libs/providers/go-feature-flag-web) repository. \ No newline at end of file diff --git a/website/versioned_docs/version-v1.40.0/openfeature_sdk/client_providers/openfeature_swift.mdx b/website/versioned_docs/version-v1.40.0/openfeature_sdk/client_providers/openfeature_swift.mdx new file mode 100644 index 00000000000..d9579030396 --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/openfeature_sdk/client_providers/openfeature_swift.mdx @@ -0,0 +1,169 @@ +--- +sidebar_position: 20 +title: Swift +description: How to use the OpenFeature Swift SDK for your iOS/tvOS/macOS application +--- +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +# Swift SDK +[![version](https://img.shields.io/github/v/release/go-feature-flag/openfeature-swift-provider?label=Swift&display_name=tag&style=flat-square&logo=Swift)](https://github.com/go-feature-flag/openfeature-swift-provider) + +In conjuction with the [OpenFeature SDK](https://openfeature.dev/docs/reference/concepts/provider) you will be able to evaluate your feature flags in your **iOS**/**tvOS**/**macOS** applications. + +For documentation related to flags management in GO Feasture Flag, refer to the [GO Feature Flag documentation website](https://gofeatureflag.org/docs). + +### Functionalities: +- Managed the integration of the OpenFeature Swift SDK and GO Feature Flag relay-proxy. +- Prefetch and cache flag evaluations in order to give the flag value in a efficient way. +- Automatic configuration changes polling, to be informed as soon as a flag configuration has changed. +- Automatic data collection about which flags have been accessed by the application + +## Dependency Setup +### Swift Package Manager + +In the dependencies section of Package.swift add: +```swift +.package(url: "https://github.com/go-feature-flag/openfeature-swift-provider.git", from: "0.1.0") +``` + +and in the target dependencies section add: +```swift +.product(name: "GOFeatureFlag", package: "openfeature-swift-provider"), +``` + +### Xcode Dependencies + +You have two options, both start from File > Add Packages... in the code menu. + +First, ensure you have your GitHub account added as an option (`+ > Add Source Control Account...`). You will need to create a [Personal Access Token](https://github.com/settings/tokens) with the permissions defined in the Xcode interface. + +1. Add as a remote repository + * Search for `git@github.com:go-feature-flag/openfeature-swift-provider.git` and click "Add Package" +2. Clone the repository locally + * Clone locally using your preferred method + * Use the "Add Local..." button to select the local folder + +**Note:** Option 2 is only recommended if you are making changes to the SDK. You will also need to add the relevant OpenFeature SDK dependency manually. + +## Getting started + +### Initialize the provider + +GO Feature Flag provider needs to be created and then set in the global OpenFeatureAPI. + +The only required option to create a `GoFeatureFlagProvider` is the URL to your GO Feature Flag relay-proxy instance. + +```swift +import GOFeatureFlag +import OpenFeature + + +let options = GoFeatureFlagProviderOptions(endpoint: "https://your_domain.io") +let provider = GoFeatureFlagProvider(options: options) + +let evaluationContext = MutableContext(targetingKey: "myTargetingKey", structure: MutableStructure()) +OpenFeatureAPI.shared.setProvider(provider: provider, initialContext: evaluationContext) +``` + +The evaluation context is the way for the client to specify contextual data that GO Feature Flag uses to evaluate the feature flags, it allows to define rules on the flag. + +The `targetingKey` is mandatory for GO Feature Flag in order to evaluate the feature flag, it could be the id of a user, a session ID or anything you find relevent to use as identifier during the evaluation. + +The `setProvider()` function is synchronous and returns immediately, however this does not mean that the provider is ready to be used. An asynchronous network request to the GO Feature Flag backend to fetch all the flags configured for your application must be completed by the provider first. The provider will then emit a `READY` event indicating you can start resolving flags. + +If you prefer to wait until the fetch is done you can use the `async/await` compatible API available for waiting the Provider to become ready: + +```swift +await OpenFeatureAPI.shared.setProviderAndWait(provider: provider) +``` + +### Update the Evaluation Context + +During the usage of your application it may appears that the `EvaluationContext` should be updated. For example if a not logged in user, authentify himself you will probably have to update the evaluation context. + +```swift +let ctx = MutableContext(targetingKey: "myNewTargetingKey", structure: MutableStructure()) +OpenFeatureAPI.shared.setEvaluationContext(evaluationContext: ctx) +``` + +`setEvaluationContext()` is a synchronous function similar to `setProvider()` and will fetch the new version of the feature flags based on this new `EvaluationContext`. + +### Limit the flags to evaluate + +By default, the provider will fetch all the flags configured in the GO Feature Flag server to be ready to evaluate them. +If you know in advance, what are the flags you will evaluate in your application, you can specify the list of flags to evaluate in the context. + +You need to add in the evaluation context the restricted key `gofeatureflag.flagList` with the list of flags you want to evaluate. + +```swift +let ctx = MutableContext(targetingKey: "myNewTargetingKey") +ctx.add( + key: "gofeatureflag", + value: Value.list([ + Value.string("flag1"), + Value.string("flag2") + ]) +) +OpenFeatureAPI.shared.setEvaluationContext(evaluationContext: ctx) +``` + +By setting the `gofeatureflag.flagList` key in the context, the provider will only fetch the flags specified in the list. + +:::warning +When limiting the flags to evaluate, if you try to evaluate a flag not in the list, the provider will return the default value with the error `FLAG_NOT_FOUND`. +::: + +### Evaluate a feature flag +The client is used to retrieve values for the current `EvaluationContext`. For example, retrieving a boolean value for the flag **"my-flag"**: + +```swift +let client = OpenFeatureAPI.shared.getClient() +let result = client.getBooleanValue(key: "my-flag", defaultValue: false) +``` + +GO Feature Flag supports different all OpenFeature supported types of feature flags, it means that you can use all the accessor directly +```swift +// Bool +client.getBooleanValue(key: "my-flag", defaultValue: false) + +// String +client.getStringValue(key: "my-flag", defaultValue: "default") + +// Integer +client.getIntegerValue(key: "my-flag", defaultValue: 1) + +// Double +client.getDoubleValue(key: "my-flag", defaultValue: 1.1) + +// Object +client.getObjectValue(key: "my-flag", defaultValue: Value.structure(["key":Value.integer("1234")]) +``` + +:::note +If you add a new flag in GO Feature Flag, expect some delay before having it available for the provider. +Refreshing the cache from remote happens when setting a new provider and/or evaluation context in the global OpenFeatureAPI, but also when a configuration change is detected during the polling. +::: + +### Handling Provider Events +When setting the provider or the context *(via `setEvaluationContext()` or `setProvider()`)* some events can be triggered to know the state of the provider. + +To listen to them you can add an event handler via the `OpenFeatureAPI` shared instance: + +```swift +OpenFeatureAPI.shared.observe().sink { event in + if event == .error { + // An error has been emitted + } +} +``` + +#### Existing type of events are: +- `.ready`: Provider is ready. +- `.error`: Provider in error. +- `.configurationChanged`: Configuration has changed in GO Feature Flag. +- `.PROVIDER_STALE`: Provider has not the latest version of the feature flags. +- `.notReady`: Provider is not ready to evaluate the feature flags. + +## Contribute to the provider +You can find the source of the provider in the [`openfeature-swift-provider`](https://github.com/go-feature-flag/openfeature-swift-provider) repository. \ No newline at end of file diff --git a/website/versioned_docs/version-v1.40.0/openfeature_sdk/sdk.mdx b/website/versioned_docs/version-v1.40.0/openfeature_sdk/sdk.mdx new file mode 100644 index 00000000000..ca0a1856c15 --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/openfeature_sdk/sdk.mdx @@ -0,0 +1,138 @@ +--- +sidebar_position: 1 +description: Use GO Feature Flag with Openfeature SDKs +--- +import { Cards } from "@site/src/components/doc/cardv2"; +import { SdkCardContent} from "@site/src/components/doc/sdkCardContent"; + +# SDKs + +**GO Feature Flag** stands out as a feature flag solution, distinct from others, due to our decision to fully support the [`Openfeature`](https://openfeature.dev) CNCF project. + +The benefit of choosing OpenFeature lies in its framework-agnostic nature. When utilizing OpenFeature SDKs, you minimize the effort required for switching to a different feature flag provider. This flexibility empowers you to make changes without being tied down to a particular vendor. + +To show our commitment to this initiative, **GO Feature Flag has opted not to develop any custom SDKs and instead relies entirely on OpenFeature SDKs**. +In order to seamlessly integrate with our solution, we offer [`providers`](https://docs.openfeature.dev/docs/reference/concepts/provider) for GO Feature Flag in multiple programming languages. + +Rest assured, working with OpenFeature SDKs alongside GO Feature Flag providers is as straightforward as using any other feature flag solution. The added advantage is that you now adhere to a standard approach and avoid any vendor lock-in! + +## Server Providers + + }, + { + logoCss: "devicon-nodejs-plain colored", + title:"Node.JS", + badges:["Server"], + docLink: "./server_providers/openfeature_javascript", + content: + }, + { + logoCss: "devicon-java-plain colored", + title:"Java / Kotlin", + badges:["Server"], + docLink: "./server_providers/openfeature_java", + content: + }, + { + logoCss: "devicon-dot-net-plain-wordmark colored", + title:".Net", + badges:["Server"], + docLink: "./server_providers/openfeature_dotnet", + content: + }, + { + logoCss: "devicon-python-plain colored", + title:"Python", + badges:["Server"], + docLink: "./server_providers/openfeature_python", + content: + }, + { + logoCss: "devicon-php-plain colored", + title:"PHP", + badges:["Server"], + docLink: "./server_providers/openfeature_php", + content: + }, + { + logoCss: "devicon-ruby-plain colored", + title:"Ruby", + badges:["Server"], + docLink: "./server_providers/openfeature_ruby", + content: + } +]}/> + +## Client Providers + + }, + { + logoCss: "devicon-react-original-wordmark colored", + title:"React", + badges:["Client"], + docLink: "./client_providers/openfeature_react", + content: + }, + { + logoCss: "devicon-swift-plain colored", + title:"Swift (iOS/tvOS/macOS)", + badges:["Client"], + docLink: "./client_providers/openfeature_swift", + content: + }, + { + logoCss: "devicon-android-plain colored", + title:"Android / Kotlin", + badges:["Client"], + docLink: "./client_providers/openfeature_android", + content: + } +]}/> + +## How OpenFeature and GO Feature Flag are working together? + +To use the OpenFeature SDKs you need what we call a provider. +A **provider** is responsible for performing flag evaluations. It provides an abstraction between **GO Feature Flag** and the OpenFeature SDK. + +A provider needs a backend service to perform the flag evaluation. This is why we have introduced the [**relay proxy**](../relay_proxy). +This component retrieve your feature flag configuration file using the GO module and exposes APIs to get your flags variations. + +![](/docs/openfeature/concepts.png) + +With this simple architecture you have a central component _(the relay proxy)_ that is in charge of the flag evaluation, while the SDKs and providers are responsible to communicate with the relay proxy. + +This supports different languages the same way and facilitates you to use GO Feature Flag with all your favorite languages. diff --git a/website/versioned_docs/version-v1.40.0/openfeature_sdk/server_providers/_category_.json b/website/versioned_docs/version-v1.40.0/openfeature_sdk/server_providers/_category_.json new file mode 100644 index 00000000000..9fc0ead18b5 --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/openfeature_sdk/server_providers/_category_.json @@ -0,0 +1,6 @@ +{ + "position": 10, + "label":"Server Providers", + "collapsible": true, + "collapsed": true +} diff --git a/website/versioned_docs/version-v1.40.0/openfeature_sdk/server_providers/openfeature_dotnet.mdx b/website/versioned_docs/version-v1.40.0/openfeature_sdk/server_providers/openfeature_dotnet.mdx new file mode 100644 index 00000000000..b82697f0d3e --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/openfeature_sdk/server_providers/openfeature_dotnet.mdx @@ -0,0 +1,126 @@ +--- +sidebar_position: 60 +title: .NET +description: How to use the OpenFeature .Net SDK +--- +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + + + +# .Net SDK usage +[![NuGet Version](https://img.shields.io/nuget/v/OpenFeature.Contrib.GOFeatureFlag?color=blue&style=flat-square)](https://nuget.info/packages/OpenFeature.Contrib.GOFeatureFlag) +![NuGet Downloads](https://img.shields.io/nuget/dt/OpenFeature.Contrib.GOFeatureFlag?style=flat-square) + + +## Install dependencies + +The first things we will do is install the **Open Feature SDK** and the **GO Feature Flag provider**. + + + + +```shell +dotnet add package OpenFeature.Contrib.GOFeatureFlag +``` + + + + +```shell +NuGet\Install-Package OpenFeature.Contrib.GOFeatureFlag +``` + + + + +```xml + +``` + + + + +```shell +paket add OpenFeature.Contrib.GOFeatureFlag +``` + + + + +```shell +// Install OpenFeature.Contrib.GOFeatureFlag as a Cake Addin +#addin nuget:?package=OpenFeature.Contrib.GOFeatureFlag + +// Install OpenFeature.Contrib.GOFeatureFlag as a Cake Tool +#tool nuget:?package=OpenFeature.Contrib.GOFeatureFlag +``` + + + + + +## Initialize your Open Feature client + +To evaluate the flags you need to have an Open Feature configured in you app. +This code block shows you how you can create a client that you can use in your application. + + + + +```csharp +using OpenFeature; +using OpenFeature.Contrib.GOFeatureFlag; + +// ... + +var goFeatureFlagProvider = new GoFeatureFlagProvider(new GoFeatureFlagProviderOptions +{ + Endpoint = "http://localhost:1031/", + Timeout = new TimeSpan(1000 * TimeSpan.TicksPerMillisecond) +}); +Api.Instance.SetProvider(goFeatureFlagProvider); +var client = Api.Instance.GetClient("my-app"); +``` + + + + +## Evaluate your flag + +This code block explain how you can create an `EvaluationContext` and use it to evaluate your flag. + +:::note +In this example we are evaluating a `boolean` flag, but other types are also available. + +**Refer to the [Open Feature documentation](https://docs.openfeature.dev/docs/reference/concepts/evaluation-api#basic-evaluation) to know more about it.** +::: + + + + +```csharp +// Context of your flag evaluation. +// With GO Feature Flag you MUST have a targetingKey that is a unique identifier of the user. +var userContext = EvaluationContext.Builder() + .Set("targetingKey", "1d1b9238-2591-4a47-94cf-d2bc080892f1") // user unique identifier (mandatory) + .Set("firstname", "john") + .Set("lastname", "doe") + .Set("email", "john.doe@gofeatureflag.org") + .Set("admin", true) // this field is used in the targeting rule of the flag "flag-only-for-admin" + .Set("anonymous", false) + .Build(); + +var adminFlag = await client.GetBooleanValue("flag-only-for-admin", false, userContext); +if (adminFlag) { + // flag "flag-only-for-admin" is true for the user +} else { + // flag "flag-only-for-admin" is false for the user +} +``` + + + + +## Contribute to the provider +You can find the source of the provider in the [`open-feature/dotnet-sdk-contrib`](https://github.com/open-feature/dotnet-sdk-contrib/tree/main/src/OpenFeature.Contrib.Providers.GOFeatureFlag) repository. \ No newline at end of file diff --git a/website/versioned_docs/version-v1.40.0/openfeature_sdk/server_providers/openfeature_go.mdx b/website/versioned_docs/version-v1.40.0/openfeature_sdk/server_providers/openfeature_go.mdx new file mode 100644 index 00000000000..1d9f03724e6 --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/openfeature_sdk/server_providers/openfeature_go.mdx @@ -0,0 +1,92 @@ +--- +sidebar_position: 20 +title: GO +description: How to use the OpenFeature GO SDK +--- + +# GO SDK usage +[![GO Version](https://img.shields.io/badge/dynamic/json?color=blue&style=flat-square&url=https%3A%2F%2Fproxy.golang.org%2Fgithub.com%2Fopen-feature%2Fgo-sdk-contrib%2Fproviders%2Fgo-feature-flag%2F%40latest&query=%24.Version&label=GO)](https://github.com/open-feature/go-sdk-contrib/tree/main/providers/go-feature-flag) + + +## Install dependencies + +The first things we will do is install the **Open Feature SDK** and the **GO Feature Flag provider**. + +```shell +go get github.com/open-feature/go-sdk +go get github.com/open-feature/go-sdk-contrib/providers/go-feature-flag +``` + +## Initialize your Open Feature provider + +Despite other providers, this GO provider can be used with the **relay proxy** or standalone +using the **GO Feature Flag module**. + +### Using the relay proxy + +If you want to use the provider with the **relay proxy** you should set the field `Endpoint` in the options. +By default it will use a default `HTTPClient` with a **timeout** configured at **10000** milliseconds. You can change +this configuration by providing your own configuration of the `HTTPClient`. + +#### Example +```go +options := gofeatureflag.ProviderOptions{ + Endpoint: "http://localhost:1031", + HTTPClient: &http.Client{ + Timeout: 1 * time.Second, + }, +} +provider, _ := gofeatureflag.NewProvider(options) +``` + +## Initialize your Open Feature client + +To evaluate the flags you need to have an Open Feature configured in you app. +This code block shows you how you can create a client that you can use in your application. + +```go +import ( + // ... + gofeatureflag "github.com/open-feature/go-sdk-contrib/providers/go-feature-flag/pkg" + of "github.com/open-feature/go-sdk/pkg/openfeature" +) + +// ... + +options := gofeatureflag.ProviderOptions{ + Endpoint: "http://localhost:1031", +} +provider, err := gofeatureflag.NewProvider(options) +of.SetProvider(provider) +client := of.NewClient("my-app") +``` + +## Evaluate your flag + +This code block explains how you can create an `EvaluationContext` and use it to evaluate your flag. + + +> In this example we are evaluating a `boolean` flag, but other types are also available. +> +> **Refer to the [Open Feature documentation](https://docs.openfeature.dev/docs/reference/concepts/evaluation-api#basic-evaluation) to know more about it.** + +```go +evaluationCtx := of.NewEvaluationContext( + "1d1b9238-2591-4a47-94cf-d2bc080892f1", + map[string]interface{}{ + "firstname", "john", + "lastname", "doe", + "email", "john.doe@gofeatureflag.org", + "admin", true, + "anonymous", false, + }) +adminFlag, _ := client.BoolValue(context.TODO(), "flag-only-for-admin", false, evaluationCtx) +if adminFlag { + // flag "flag-only-for-admin" is true for the user +} else { + // flag "flag-only-for-admin" is false for the user +} +``` + +## Contribute to the provider +You can find the source of the provider in the [`open-feature/go-sdk-contrib`](https://github.com/open-feature/go-sdk-contrib/tree/main/providers/go-feature-flag) repository. \ No newline at end of file diff --git a/website/versioned_docs/version-v1.40.0/openfeature_sdk/server_providers/openfeature_java.mdx b/website/versioned_docs/version-v1.40.0/openfeature_sdk/server_providers/openfeature_java.mdx new file mode 100644 index 00000000000..1ad484be17f --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/openfeature_sdk/server_providers/openfeature_java.mdx @@ -0,0 +1,217 @@ +--- +sidebar_position: 40 +title: Java +description: How to use the OpenFeature JAVA SDK +--- + + + + + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import versions from '@site/static/sdk-versions.json'; +import CodeBlock from '@theme/CodeBlock'; + +# JAVA SDK usage +[![Maven Central Version](https://img.shields.io/maven-central/v/dev.openfeature.contrib.providers/go-feature-flag?color=blue&style=flat-square)](https://search.maven.org/artifact/dev.openfeature.contrib.providers/go-feature-flag) + +## Install dependencies + +The first thing we will do is install the **Open Feature SDK** and the **GO Feature Flag provider**. + + + + + + { + ` + dev.openfeature + sdk + ${versions.maven.sdk} + + + dev.openfeature.contrib.providers + go-feature-flag + ${versions.maven.providerJava} +` + } + + + + + + { + `implementation group: 'dev.openfeature', name: 'javasdk', version: '${versions.maven.sdk}' +implementation group: 'dev.openfeature.contrib.providers', name: 'go-feature-flag', version: '${versions.maven.providerJava}'`} + + + + + + +## Initialize your Open Feature client +To evaluate the flag you need to have an Open Feature configured in your app. +This code block shows you how you can create a client that you can use in your application. + + + + + ```java + import dev.openfeature.contrib.providers.gofeatureflag.GoFeatureFlagProvider; + import dev.openfeature.contrib.providers.gofeatureflag.GoFeatureFlagProviderOptions; + import dev.openfeature.sdk.EvaluationContext; + import dev.openfeature.sdk.MutableContext; + import dev.openfeature.sdk.OpenFeatureAPI; + + // ... + + GoFeatureFlagProviderOptions options = + GoFeatureFlagProviderOptions.builder().endpoint("http://localhost:1031/").build(); + GoFeatureFlagProvider provider = new GoFeatureFlagProvider(options); + + OpenFeatureAPI.getInstance().setProvider(provider); + OpenFeatureAPI api = OpenFeatureAPI.getInstance(); + Client featureFlagClient = api.getClient(); + ``` + + + + + ```kotlin + import dev.openfeature.contrib.providers.gofeatureflag.GoFeatureFlagProvider + import dev.openfeature.contrib.providers.gofeatureflag.GoFeatureFlagProviderOptions + import dev.openfeature.sdk.EvaluationContext + import dev.openfeature.sdk.MutableContext + import dev.openfeature.sdk.OpenFeatureAPI + + // ... + + val options = GoFeatureFlagProviderOptions.builder().endpoint("http://localhost:1031/").build() + val provider = GoFeatureFlagProvider(options) + + OpenFeatureAPI.getInstance().provider = provider + // wait for the provider to be ready + val api = OpenFeatureAPI.getInstance() + val featureFlagClient = api.client + ``` + + + + + +## Evaluate your flag + +This code block explain how you can create an `EvaluationContext` and use it to evaluate your flag. + +:::note +In this example we are evaluating a `boolean` flag, but other types are available. + +**Refer to the [Open Feature documentation](https://docs.openfeature.dev/docs/reference/concepts/evaluation-api#basic-evaluation) to know more about it.** +::: + + + + + ```java + // Context of your flag evaluation. + // With GO Feature Flag you MUST have a targetingKey that is a unique identifier of the user. + EvaluationContext userContext = new MutableContext("1d1b9238-2591-4a47-94cf-d2bc080892f1") + .add("firstname", "john") + .add("lastname", "doe") + .add("email","john.doe@gofeatureflag.org") + .add("admin", true) + .add("anonymous", false); + + Boolean adminFlag = featureFlagClient.getBooleanValue("flag-only-for-admin", false, userContext); + if (adminFlag) { + // flag "flag-only-for-admin" is true for the user + } else { + // flag "flag-only-for-admin" is false for the user + } + ``` + + + + + ```kotlin + // Context of your flag evaluation. + // With GO Feature Flag you MUST have a targetingKey that is a unique identifier of the user. + val userContext: EvaluationContext = MutableContext("1d1b9238-2591-4a47-94cf-d2bc080892f1") + .add("firstname", "john") + .add("lastname", "doe") + .add("email", "john.doe@gofeatureflag.org") + .add("admin", true) + .add("anonymous", false) + + val adminFlag = featureFlagClient.getBooleanValue("bool_targeting_match", false, userContext) + if (adminFlag) { + // flag "flag-only-for-admin" is true for the user + } else { + // flag "flag-only-for-admin" is false for the user + } + ``` + + + + +## Breaking changes + +### 0.4.0 - Cache Implementation Change: Guava to Caffeine + +In this release, we have updated the cache implementation from Guava to Caffeine. This change was made because Caffeine is now the recommended caching solution by the maintainers of Guava due to its performance improvements and enhanced features. + +Because of this, the cache configuration on `GoFeatureFlagProviderOptions` that used Guava's `CacheBuilder` is now handled by `Caffeine`. + +#### How to migrate + +Configuration cache with Guava used to be like this: + +```java +import com.google.common.cache.CacheBuilder; +// ... +CacheBuilder guavaCacheBuilder = CacheBuilder.newBuilder() + .initialCapacity(100) + .maximumSize(2000); + +FeatureProvider provider = new GoFeatureFlagProvider( + GoFeatureFlagProviderOptions + .builder() + .endpoint("https://my-gofeatureflag-instance.org") + .cacheBuilder(guavaCacheBuilder) + .build()); + +OpenFeatureAPI.getInstance().setProviderAndWait(provider); + +// ... +``` + +Now with Caffeine it should be like this: + +```java +import com.github.benmanes.caffeine.cache.Caffeine; +// ... +Caffeine caffeineCacheConfig = Caffeine.newBuilder() + .initialCapacity(100) + .maximumSize(2000); + +FeatureProvider provider = new GoFeatureFlagProvider( + GoFeatureFlagProviderOptions + .builder() + .endpoint("https://my-gofeatureflag-instance.org") + .cacheConfig(caffeineCacheConfig) + .build()); + +OpenFeatureAPI.getInstance().setProviderAndWait(provider); + +// ... +``` + +For a complete list of customizations options available in Caffeine, please refer to the [Caffeine documentation](https://github.com/ben-manes/caffeine/wiki) for more details. + +## Contribute to the provider +You can find the source of the provider in the [`open-feature/java-sdk-contrib`](https://github.com/open-feature/java-sdk-contrib/tree/main/providers/go-feature-flag) repository. \ No newline at end of file diff --git a/website/versioned_docs/version-v1.40.0/openfeature_sdk/server_providers/openfeature_javascript.mdx b/website/versioned_docs/version-v1.40.0/openfeature_sdk/server_providers/openfeature_javascript.mdx new file mode 100644 index 00000000000..61aa56c1947 --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/openfeature_sdk/server_providers/openfeature_javascript.mdx @@ -0,0 +1,137 @@ +--- +sidebar_position: 41 +title: Node.js +description: How to use the OpenFeature Javascript SDK +--- +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +# Node.js +[![NPM Version](https://img.shields.io/npm/v/%40openfeature%2Fgo-feature-flag-provider?color=blue&style=flat-square)](https://www.npmjs.com/package/@openfeature/go-feature-flag-provider) +![NPM Downloads](https://img.shields.io/npm/d18m/%40openfeature%2Fgo-feature-flag-provider?style=flat-square) + + +## Install dependencies + +The first things we will do is install the **Open Feature SDK** and the **GO Feature Flag provider**. + + + + +```shell +yarn add @openfeature/server-sdk @openfeature/go-feature-flag-provider +``` + + + + +```shell +npm i @openfeature/server-sdk @openfeature/go-feature-flag-provider +``` + + + + +## Initialize your Open Feature client + +To evaluate the flags you need to have an Open Feature configured in your app. +This code block shows you how you can create a client that you can use in your application. + + + + +```javascript +const {OpenFeature} = require("@openfeature/server-sdk"); +const {GoFeatureFlagProvider} = require("@openfeature/go-feature-flag-provider"); + + +// init Open Feature SDK with GO Feature Flag provider +const goFeatureFlagProvider = new GoFeatureFlagProvider({ + endpoint: 'http://localhost:1031/' // DNS of your instance of relay proxy +}); +OpenFeature.setProvider(goFeatureFlagProvider); +const featureFlagClient = OpenFeature.getClient('my-app') +``` + + + + + +```typescript +import {EvaluationContext, OpenFeature} from "@openfeature/server-sdk"; +import {GoFeatureFlagProvider} from "@openfeature/go-feature-flag-provider"; + + +// init Open Feature SDK with GO Feature Flag provider +const goFeatureFlagProvider: GoFeatureFlagProvider = new GoFeatureFlagProvider({ +endpoint: 'http://localhost:1031/' +}); +OpenFeature.setProvider(goFeatureFlagProvider); +const featureFlagClient = OpenFeature.getClient('my-app'); +``` + + + + +## Evaluate your flag + +This code block explains how you can create an `EvaluationContext` and use it to evaluate your flag. + +:::note +In this example, we are evaluating a `boolean` flag, but other types are also available. + +**Refer to the [Open Feature documentation](https://docs.openfeature.dev/docs/reference/concepts/evaluation-api#basic-evaluation) to know more about it.** +::: + + + + +```javascript +// Context of your flag evaluation. +// With GO Feature Flag you MUST have a targetingKey that is a unique identifier of the user. +const userContext = { + targetingKey: '1d1b9238-2591-4a47-94cf-d2bc080892f1', // user unique identifier (mandatory) + firstname: 'john', + lastname: 'doe', + email: 'john.doe@gofeatureflag.org', + admin: true, // this field is used in the targeting rule of the flag "flag-only-for-admin" + // ... +}; + +const adminFlag = await featureFlagClient.getBooleanValue('flag-only-for-admin', false, userContext); +if (adminFlag) { + // flag "flag-only-for-admin" is true for the user +} else { + // flag "flag-only-for-admin" is false for the user +} +``` + + + + + +```typescript +// Context of your flag evaluation. +// With GO Feature Flag you MUST have a targetingKey that is a unique identifier of the user. +const userContext: EvaluationContext = { + targetingKey: '1d1b9238-2591-4a47-94cf-d2bc080892f1', // user unique identifier + firstname: 'john', + lastname: 'doe', + email: 'john.doe@gofeatureflag.org', + admin: true, // this field is used in the targeting rule of the flag "flag-only-for-admin" + // ... +}; + +const adminFlag = await featureFlagClient.getBooleanValue('flag-only-for-admin', false, userContext); +if (adminFlag) { + // flag "flag-only-for-admin" is true for the user +} else { + // flag "flag-only-for-admin" is false for the user +} +``` + + + + +## Contribute to the provider +You can find the source of the provider in the [`open-feature/js-sdk-contrib`](https://github.com/open-feature/js-sdk-contrib/tree/main/libs/providers/go-feature-flag) repository. diff --git a/website/versioned_docs/version-v1.40.0/openfeature_sdk/server_providers/openfeature_php.md b/website/versioned_docs/version-v1.40.0/openfeature_sdk/server_providers/openfeature_php.md new file mode 100644 index 00000000000..cfd38318dc5 --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/openfeature_sdk/server_providers/openfeature_php.md @@ -0,0 +1,134 @@ +--- +sidebar_position: 50 +title: PHP +description: How to use the OpenFeature PHP SDK with GO Feature Flag +--- + +# PHP Provider +[![Packagist - Version](https://img.shields.io/packagist/v/open-feature/go-feature-flag-provider?logo=php&color=blue&style=flat-square)](https://packagist.org/packages/open-feature/go-feature-flag-provider) +[![Packagist - Downloads](https://img.shields.io/packagist/dt/open-feature/go-feature-flag-provider?logo=php&style=flat-square)](https://packagist.org/packages/open-feature/go-feature-flag-provider) + + +In conjunction with the [OpenFeature SDK](https://openfeature.dev/docs/reference/concepts/provider) you will be able +to evaluate your feature flags in your Ruby applications. + +### Functionalities: +- Manage the integration of the OpenFeature PHP SDK and GO Feature Flag relay-proxy. + +## Dependency Setup + +### Composer + +```shell +composer require open-feature/go-feature-flag-provider +``` +## Getting started + +### Initialize the provider + +The `GoFeatureFlagProvider` takes a config object as parameter to be initialized. + +The constructor of the config object has the following options: + +| **Option** | **Description** | +|-----------------|------------------------------------------------------------------------------------------------------------------| +| `endpoint` | **(mandatory)** The URL to access to the relay-proxy.
*(example: `https://relay.proxy.gofeatureflag.org/`)* | +| `apiKey` | The token used to call the relay proxy. | +| `customHeaders` | Any headers you want to add to call the relay-proxy. | +| `httpclient` | The HTTP Client to use (if you want to use a custom one). _It has to be a `PSR-7` compliant implementation._ | + +The only required option to create a `GoFeatureFlagProvider` is the URL _(`endpoint`)_ to your GO Feature Flag relay-proxy instance. + +```php +use OpenFeature\Providers\GoFeatureFlag\config\Config; +use OpenFeature\Providers\GoFeatureFlag\GoFeatureFlagProvider; +use OpenFeature\implementation\flags\MutableEvaluationContext; +use OpenFeature\implementation\flags\Attributes; +use OpenFeature\OpenFeatureAPI; + +$config = new Config('https://gofeatureflag.org', 'my-api-key'); +$provider = new GoFeatureFlagProvider($config); + +$api = OpenFeatureAPI::getInstance(); +$api->setProvider($provider); +$client = $api->getClient(); +$evaluationContext = new MutableEvaluationContext( + "214b796a-807b-4697-b3a3-42de0ec10a37", + new Attributes(["email" => 'contact@gofeatureflag.org']) + ); + +$value = $client->getBooleanDetails('integer_key', false, $evaluationContext); +if ($value) { + echo "The flag is enabled"; +} else { + echo "The flag is disabled"; +} +``` + +The evaluation context is the way for the client to specify contextual data that GO Feature Flag uses to evaluate the feature flags, it allows to define rules on the flag. + +The `targeting_key` is mandatory for GO Feature Flag to evaluate the feature flag, it could be the id of a user, a session ID or anything you find relevant to use as identifier during the evaluation. + + +### Evaluate a feature flag +The client is used to retrieve values for the current `EvaluationContext`. +For example, retrieving a boolean value for the flag **"my-flag"**: + +```php +$value = $client->getBooleanDetails('integer_key', false, $evaluationContext); +if ($value) { + echo "The flag is enabled"; +} else { + echo "The flag is disabled"; +} +``` + +GO Feature Flag supports different all OpenFeature supported types of feature flags, it means that you can use all the accessor directly +```php +// Bool +$client->getBooleanDetails('my-flag-key', false, new MutableEvaluationContext("214b796a-807b-4697-b3a3-42de0ec10a37")); +$client->getBooleanValue('my-flag-key', false, new MutableEvaluationContext("214b796a-807b-4697-b3a3-42de0ec10a37")); + +// String +$client->getStringDetails('my-flag-key', "default", new MutableEvaluationContext("214b796a-807b-4697-b3a3-42de0ec10a37")); +$client->getStringValue('my-flag-key', "default", new MutableEvaluationContext("214b796a-807b-4697-b3a3-42de0ec10a37")); + +// Integer +$client->getIntegerDetails('my-flag-key', 1, new MutableEvaluationContext("214b796a-807b-4697-b3a3-42de0ec10a37")); +$client->getIntegerValue('my-flag-key', 1, new MutableEvaluationContext("214b796a-807b-4697-b3a3-42de0ec10a37")); + +// Float +$client->getFloatDetails('my-flag-key', 1.1, new MutableEvaluationContext("214b796a-807b-4697-b3a3-42de0ec10a37")); +$client->getFloatValue('my-flag-key', 1.1, new MutableEvaluationContext("214b796a-807b-4697-b3a3-42de0ec10a37")); + +// Object +$client->getObjectDetails('my-flag-key', ["default" => true], new MutableEvaluationContext("214b796a-807b-4697-b3a3-42de0ec10a37")); +$client->getObjectValue('my-flag-key', ["default" => true], new MutableEvaluationContext("214b796a-807b-4697-b3a3-42de0ec10a37")); +``` + +## Features status + +| Status | Feature | Description | +|-------|-----------------|----------------------------------------------------------------------------| +| ✅ | Flag evaluation | It is possible to evaluate all the type of flags | +| ❌ | Caching | Mechanism is in place to refresh the cache in case of configuration change | +| ❌ | Event Streaming | Not supported by the SDK | +| ❌ | Logging | Not supported by the SDK | +| ❌ | Flag Metadata | Not supported by the SDK | + + +**Implemented**: ✅ | In-progress: ⚠️ | Not implemented yet: ❌ + +## Contributing +This project welcomes contributions from the community. +If you're interested in contributing, see the [contributors' guide](https://github.com/thomaspoignant/go-feature-flag/blob/main/CONTRIBUTING.md) for some helpful tips. + +### PHP Versioning +This library targets PHP version 8.0 and newer. As long as you have any compatible version of PHP on your system you should be able to utilize the OpenFeature SDK. + +This package also has a .tool-versions file for use with PHP version managers like asdf. + +### Installation and Dependencies +Install dependencies with `composer install`, it will update the `composer.lock` with the most recent compatible versions. + +We value having as few runtime dependencies as possible. The addition of any dependencies requires careful consideration and review. diff --git a/website/versioned_docs/version-v1.40.0/openfeature_sdk/server_providers/openfeature_python.md b/website/versioned_docs/version-v1.40.0/openfeature_sdk/server_providers/openfeature_python.md new file mode 100644 index 00000000000..96e13d4fecf --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/openfeature_sdk/server_providers/openfeature_python.md @@ -0,0 +1,80 @@ +--- +sidebar_position: 51 +title: Python +description: How to use the OpenFeature Python SDK with GO Feature Flag +--- + +# Python Provider +[![PyPI - Version](https://img.shields.io/pypi/v/gofeatureflag-python-provider?color=blue&style=flat-square)](https://pypi.org/project/gofeatureflag-python-provider/) +![PyPI - Downloads](https://img.shields.io/pypi/dm/gofeatureflag-python-provider?style=flat-square) + + +## Install dependencies + +The first thing we will do is install the **Open Feature SDK** and the **GO Feature Flag provider**. + +```shell +pip install gofeatureflag-python-provider +``` + +## Initialize your Open Feature client + +To evaluate the flag you need to have an Open Feature configured in you app. +This code block shows you how you can create a client that you can use in your application. + +```python +from gofeatureflag_python_provider.provider import GoFeatureFlagProvider +from gofeatureflag_python_provider.options import GoFeatureFlagOptions +from openfeature import api +from openfeature.evaluation_context import EvaluationContext + +// ... + +goff_provider = GoFeatureFlagProvider( + options=GoFeatureFlagOptions(endpoint="https://gofeatureflag.org/") +) +api.set_provider(goff_provider) +client = api.get_client(domain="test-client") +``` + +## Evaluate your flag + +This code block explains how you can create an `EvaluationContext` and use it to evaluate your flag. + + +> In this example, we are evaluating a `boolean` flag, but other types are also available. +> +> **Refer to the [Open Feature documentation](https://docs.openfeature.dev/docs/reference/concepts/evaluation-api#basic-evaluation) to know more about it.** + +```python +// Context of your flag evaluation. +// With GO Feature Flag you MUST have a targetingKey that is a unique identifier of the user. +evaluation_ctx = EvaluationContext( + targeting_key="d45e303a-38c2-11ed-a261-0242ac120002", + attributes={ + "email": "john.doe@gofeatureflag.org", + "firstname": "john", + "lastname": "doe", + "anonymous": False, + "professional": True, + "rate": 3.14, + "age": 30, + "company_info": {"name": "my_company", "size": 120}, + "labels": ["pro", "beta"], + }, +) + +admin_flag = client.get_boolean_value( + flag_key=flag_key, + default_value=default_value, + evaluation_context=ctx, + ) + +if admin_flag: + # flag "flag-only-for-admin" is true for the user +else: + # flag "flag-only-for-admin" is false for the user +``` + +## Contribute to the provider +You can find the source of the provider in the [`repository`](https://github.com/thomaspoignant/go-feature-flag/tree/main/openfeature/providers/python-provider). \ No newline at end of file diff --git a/website/versioned_docs/version-v1.40.0/openfeature_sdk/server_providers/openfeature_ruby.md b/website/versioned_docs/version-v1.40.0/openfeature_sdk/server_providers/openfeature_ruby.md new file mode 100644 index 00000000000..06dda6dbc86 --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/openfeature_sdk/server_providers/openfeature_ruby.md @@ -0,0 +1,129 @@ +--- +sidebar_position: 52 +title: Ruby +description: How to use the OpenFeature Ruby SDK with GO Feature Flag +--- + +# Ruby provider + gem + +This repository contains the official Ruby OpenFeature provider for accessing your feature flags with [GO Feature Flag](https://gofeatureflag.org). + +In conjuction with the [OpenFeature SDK](https://openfeature.dev/docs/reference/concepts/provider) you will be able +to evaluate your feature flags in your Ruby applications. + +For documentation related to flags management in GO Feature Flag, +refer to the [GO Feature Flag documentation website](https://gofeatureflag.org/docs). + +### Functionalities: +- Manage the integration of the OpenFeature Ruby SDK and GO Feature Flag relay-proxy. + +## Dependency Setup + +### Gem Package Manager + +Add this line to your application's Gemfile: +``` +gem 'openfeature-go-feature-flag-provider' +``` +And then execute: +``` +bundle install +``` +Or install it yourself as: +``` +gem install openfeature-go-feature-flag-provider +``` + +## Getting started + +### Initialize the provider + +The `OpenFeature::GoFeatureFlag::Provider` needs some options to be created and then set in the OpenFeature SDK. + +| **Option** | **Description** | +|------------|---------------------------------------------------------------------------------------------------------------------------------------------| +| `endpoint` | **(mandatory)** The URL to access to the relay-proxy.
*(example: `https://relay.proxy.gofeatureflag.org/`)* | +| `headers` | A `Hash` object containing the headers to send to the relay-proxy.
*(example to send APIKey: `{"Authorization" => "Bearer my-api-key"}` | + +The only required option to create a `GoFeatureFlagProvider` is the URL _(`endpoint`)_ to your GO Feature Flag relay-proxy instance. + +```ruby +import GOFeatureFlag +import OpenFeature + +# ... + +options = OpenFeature::GoFeatureFlag::Options.new(endpoint: "http://localhost:1031") +provider = OpenFeature::GoFeatureFlag::Provider.new(options: options) + +evaluation_context = OpenFeature::SDK::EvaluationContext.new(targeting_key: "9b9450f8-ab5c-4dcf-872f-feda3f6ccb16") + +OpenFeature::SDK.configure do |config| + config.set_provider(provider) +end +client = OpenFeature::SDK.build_client() + +bool_value = client.fetch_boolean_value( + flag_key: "my-boolean-flag", + default_value: false, + evaluation_context: evaluation_context +) + +if bool_value + puts "The flag is enabled" +else + puts "The flag is disabled" +end +``` + +The evaluation context is the way for the client to specify contextual data that GO Feature Flag uses to evaluate the feature flags, it allows to define rules on the flag. + +The `targeting_key` is mandatory for GO Feature Flag to evaluate the feature flag, it could be the id of a user, a session ID or anything you find relevant to use as identifier during the evaluation. + + +### Evaluate a feature flag +The client is used to retrieve values for the current `EvaluationContext`. +For example, retrieving a boolean value for the flag **"my-flag"**: + +```ruby +client = OpenFeature::SDK.build_client() + +bool_value = client.fetch_boolean_value( + flag_key: "my-boolean-flag", + default_value: false, + evaluation_context: evaluation_context +) +``` + +GO Feature Flag supports different all OpenFeature supported types of feature flags, it means that you can use all the accessor directly +```ruby +# Bool +client.fetch_boolean_value(flag_key: 'my-flag', default_value: false, evaluation_context: evaluation_context) + +# String +client.fetch_string_value(flag_key: 'my-flag', default_value: "default", evaluation_context: evaluation_context) + +# Number +client.fetch_number_value(flag_key: 'my-flag', default_value: 0, evaluation_context: evaluation_context) + +# Object +client.fetch_object_value(flag_key: 'my-flag', default_value: {"default" => true}, evaluation_context: evaluation_context) +``` + +## Features status + +| Status | Feature | Description | +|--------|-----------------|----------------------------------------------------------------------------| +| ✅ | Flag evaluation | It is possible to evaluate all the type of flags | +| ❌ | Caching | Mechanism is in place to refresh the cache in case of configuration change | +| ❌ | Event Streaming | Not supported by the SDK | +| ❌ | Logging | Not supported by the SDK | +| ✅ | Flag Metadata | Not supported by the SDK | + + +**Implemented**: ✅ | In-progress: ⚠️ | Not implemented yet: ❌ + +## Contributing +This project welcomes contributions from the community. +If you're interested in contributing, see the [contributors' guide](https://github.com/thomaspoignant/go-feature-flag/blob/main/CONTRIBUTING.md) for some helpful tips. diff --git a/website/versioned_docs/version-v1.40.0/relay_proxy/_category_.json b/website/versioned_docs/version-v1.40.0/relay_proxy/_category_.json new file mode 100644 index 00000000000..f4abdc44a43 --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/relay_proxy/_category_.json @@ -0,0 +1,6 @@ +{ + "position": 50, + "label":"Use the relay proxy", + "collapsible": true, + "collapsed": true +} diff --git a/website/versioned_docs/version-v1.40.0/relay_proxy/advanced_usage.md b/website/versioned_docs/version-v1.40.0/relay_proxy/advanced_usage.md new file mode 100644 index 00000000000..7e13f249e2f --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/relay_proxy/advanced_usage.md @@ -0,0 +1,28 @@ +--- +sidebar_position: 90 +title: Advanced usage +description: All the advanced usage of the relay proxy. +--- + +## Manually trigger retrievers and refresh the internal flag cache +By default, the retrievers are called regularly to refresh the configuration based on the polling interval. + +But there are use cases where you want to refresh the configuration explicitly _(for example, during the CI process +after you have changed your configuration file)_. + +To do so you can call the `/v1/admin/retriver/refresh` endpoint with a POST request. +It will force the retrievers to be called and update the internal cache. + +```shell +curl -X 'POST' \ + 'http://:1031/admin/v1/retriever/refresh' \ + -H 'accept: application/json' \ + -H 'Authorization: Bearer ' \ + -d '' +``` + +:::note +This endpoint must be called with an admin token. +Authorized keys should be configured in the relay-proxy configuration file under the key `authorizedKeys.admin`. +::: + diff --git a/website/versioned_docs/version-v1.40.0/relay_proxy/configure_relay_proxy.md b/website/versioned_docs/version-v1.40.0/relay_proxy/configure_relay_proxy.md new file mode 100644 index 00000000000..13960580a78 --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/relay_proxy/configure_relay_proxy.md @@ -0,0 +1,354 @@ +--- +sidebar_position: 30 +title: Configuration +description: How to configure the relay proxy to serve your feature flags. +--- + +# Configure the relay proxy + +## Getting Started + +The configuration of the **relay proxy** is based on a configuration file that you have to provide. + +The only mandatory information you need to start the server is to provide where to retrieve your feature flags +configuration. + +```yaml +retriever: + kind: file + path: /goff/flags.yaml # Location of your feature flag files +``` + +## Global configuration + +:::tip Use environment variables. +You can override file configurations using environment variables. + +Note that all environment variables should be uppercase. +If you want to replace a nested fields, please use `_` to separate each field _(ex: `RETRIEVER_KIND`)_. + +In case of an array of string, you can add multiple values separated by a comma _( +ex: `AUTHORIZEDKEYS_EVALUATION=my-first-key,my-second-key`)_. +::: + +| Field name | Type | Default | Description | +|-----------------------------------|----------------------------------------|--------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `retriever` | [retriever](#retriever) | **none** | **(mandatory)** This is the configuration on how to retrieve the configuration of the files.

_Note: this field is mandatory only if `retrievers` is not set._ | | +| `retrievers` | [[]retriever](#retriever) | **none** | **(mandatory)** Exactly the same things as `retriever` except that you can provide more than 1 retriever.

_Note: this field is mandatory only if `retriever` is not set._ | +| `listen` | int | `1031` | This is the port used by the relay proxy when starting the HTTP server. | +| `pollingInterval` | int | `60000` | This is the time interval **in millisecond** when the relay proxy is reloading the configuration file.
The minimum time accepted is 1000 millisecond. | +| `enablePollingJitter` | boolean | `false` | Set to true if you want to avoid having true periodicity when retrieving your flags. It is useful to avoid having spike on your flag configuration storage in case your application is starting multiple instance at the same time.
We ensure a deviation that is maximum ±10% of your polling interval.
Default: false | +| `DisableNotifierOnInit` | boolean | `false` | If **true**, the relay proxy will not call any notifier when the flags are loaded during initialization. This is useful if you do not want a Slack/Webhook notification saying that the flags have been added every time you start the proxy.
Default: **false** | +| `hideBanner` | boolean | `false` | Should we display the beautiful **go-feature-flag** banner when starting the relay proxy | +| `enableSwagger` | boolean | `false` | Enables Swagger for testing the APIs directly. If you are enabling Swagger you will have to provide the `host` configuration and the Swagger UI will be available at `http://:/swagger/`. | +| `host` | string | `localhost` | This is the DNS you will use to access the relay proxy. This field is used by Swagger to query the API at the right place. | +| `restApiTimeout` | int | `5000` | Timeout in milliseconds for API calls. | +| `logLevel` | string | `info` | The log level to use for the relay proxy.
Available values are `ERROR`, `WARN`, `INFO`, `DEBUG`. | +| `logFormat` | string | `json` | The format to use for structured logs from the relay proxy.
Valid values are `json` and `logfmt`. | +| `fileFormat` | string | `yaml` | This is the format of your `go-feature-flag` configuration file. Acceptable values are `yaml`, `json`, `toml`. | +| `startWithRetrieverError` | boolean | `false` | By default the **relay proxy** will crash if it is not able to retrieve the flags from the configuration.
If you don't want your relay proxy to crash, you can set `startWithRetrieverError` to true. Until the flag is retrievable the relay proxy will only answer with default values. | +| `exporter` | [exporter](#exporter) | **none** | Exporter is the configuration used to export data. | +| `notifier` | [notifier](#notifier) | **none** | Notifiers is the configuration on where to notify a flag change. | +| `authorizedKeys` | [authorizedKeys](#type-authorizedkeys) | **none** | List of authorized API keys. | +| `evaluationContextEnrichment` | object | **none** | It is a free field that will be merged with the evaluation context sent during the evaluation. It is useful to add common attributes to all the evaluations, such as a server version, environment, etc.

These fields will be included in the custom attributes of the evaluation context.

If in the evaluation context you have a field with the same name, it will be override by the `evaluationContextEnrichment`. | +| `openTelemetryOtlpEndpoint` | string | **none** | Endpoint of your OpenTelemetry OTLP collector, used to send traces to it and you will be able to forward them to your OpenTelemetry solution with the appropriate provider. | +| `kafka` | object | **none** | Settings for the Kafka exporter. Mandatory when using the 'kafka' exporter type, and ignored otherwise. | +| `projectID` | string | **none** | ID of GCP project. Mandatory when using PubSub exporter. | +| `topic` | string | **none** | Name of PubSub topic on which messages will be published. Mandatory when using PubSub exporter. | +| `persistentFlagConfigurationFile` | string | **none** | If set GO Feature Flag will store the flags configuration in this file to be able to serve the flags even if none of the retrievers is available during starting time.
By default, the flag configuration is not persisted and stays on the retriever system. By setting a file here, you ensure that GO Feature Flag will always start with a configuration but which can be out-dated.

_(example: `/tmp/goff_persist_conf.yaml`)_ | +| `startAsAwsLambda` | boolean | **`false`** | If set GO Feature Flag start the relay-proxy as a AWS Lambda, it means that it will start the server to receive request in the AWS format _(see `awsLambdaAdapter` to set the request/response format you are using)_. | +| `awsLambdaAdapter` | string | **`APIGatewayV2`** | This param is used only if `startAsAwsLambda` is `true`.
This parameter allow you to decide which type of AWS lambda handler you wan to use.
Accepted values are `APIGatewayV2`, `APIGatewayV1`, `ALB`. | +| `enablePprof` | boolean | **`false`** | This param is used to enable **pprof endpoints** if you wish to enable profiling. | + +## type `authorizedKeys` + +To be able to control who can access your relay proxy, you can set a list of authorized keys. + +| Field name | Type | Default | Description | +|--------------|----------|----------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `evaluation` | []string | **none** | If set, we will check for each evaluation if an authorized key is provided.
Each request will need to provide one of authorized key inside `Authorization` header with format `Bearer `.

_Note: there will be no authorization when this config is not set._ | +| `admin` | []string | **none** | You need to set API keys in this field if you want to access the `/v1/admin/*` endpoints.
If no api key is configured the endpoint will be unreachable.
Each request will need to provide one of authorized key inside `Authorization` header with format `Bearer `. | + + + +## type `retriever` + +`go-feature-flag` supports different kind of retriever types such as S3, Google store, etc ... +In this section we will present all the available retriever configurations available. + +### S3 + +If you are using the S3 provider, the easiest way to provide credentials is to set environment variables. +It will be used by GO Feature Flag to identify to your S3 bucket. + +```shell +export AWS_SECRET_ACCESS_KEY=xxxx +export AWS_ACCESS_KEY_ID=xxx +export AWS_DEFAULT_REGION=eu-west-1 +``` + +| Field name | Type | Default | Description | +|------------|--------|----------|----------------------------------------------------------------------------------------------------------------------| +| `kind` | string | **none** | **(mandatory)** Value should be **`s3`**.
_This field is mandatory and describes which retriever you are using._ | +| `bucket` | string | **none** | **(mandatory)** This is the name of your S3 bucket _(ex: `my-featureflag-bucket`)_. | +| `item` | string | **none** | **(mandatory)** Path to the file inside the bucket _(ex: `config/flag/my-flags.yaml`)_. | + +### GitHub + +:::tip +GitHub has rate limits, be sure to correctly set your `PollingInterval` to avoid reaching the limit. + +If the rate limit is reached, the retriever will return an error and will stop polling until GitHub allows it again. +::: + +| Field name | Type | Default | Description | +|------------------|--------|----------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `kind` | string | **none** | **(mandatory)** Value should be **`github`**.
_This field is mandatory and describes which retriever you are using._ | +| `repositorySlug` | string | **none** | **(mandatory)** The repository slug of the GitHub repository where your file is located _(ex: `thomaspoignant/go-feature-flag`)_. | +| `path` | string | **none** | **(mandatory)** Path to the file inside the repository _(ex: `config/flag/my-flags.yaml`)_. | +| `branch` | string | `main` | The branch we should check in the repository. | +| `token` | string | **none** | Github token used to access a private repository, you need the repo permission ([how to create a GitHub token](https://docs.github.com/en/free-pro-team@latest/github/authenticating-to-github/creating-a-personal-access-token)). | +| `timeout` | string | `10000` | Timeout in millisecond used when calling GitHub. | + +### GitLab + +| Field name | Type | Default | Description | +|------------------|--------|----------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `kind` | string | **none** | **(mandatory)** Value should be **`gitlab`**.
_This field is mandatory and describes which retriever you are using._ | +| `repositorySlug` | string | **none** | **(mandatory)** The repository slug of the GitLab repository where your file is located _(ex: `thomaspoignant/go-feature-flag`)_. | +| `path` | string | **none** | **(mandatory)** Path to the file inside the repository _(ex: `config/flag/my-flags.yaml`)_. | +| `baseUrl` | string | `https://gitlab.com` | The base URL of your GitLab instance. | +| `branch` | string | `main` | The branch we should check in the repository. | +| `token` | string | **none** | GitLab personal access token used to access a private repository ([Create a personal access token](https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html#create-a-personal-access-token)). | +| `timeout` | string | `10000` | Timeout in millisecond used when calling GitLab. | + +### File + +| Field name | Type | Default | Description | +|------------|--------|----------|------------------------------------------------------------------------------------------------------------------------| +| `kind` | string | **none** | **(mandatory)** Value should be **`file`**.
_This field is mandatory and describes which retriever you are using._ | +| `path` | string | **none** | **(mandatory)** Path to the file in your local computer _(ex: `/goff/my-flags.yaml`)_. | + +### HTTP + +| Field name | Type | Default | Description | +|------------|---------------------|----------|------------------------------------------------------------------------------------------------------------------------| +| `kind` | string | **none** | **(mandatory)** Value should be **`http`**.
_This field is mandatory and describes which retriever you are using._ | +| `url` | string | **none** | **(mandatory)** Location to retrieve the file. | +| `method` | string | `GET` | The HTTP Method you are using to call the HTTP endpoint. | +| `body` | string | **none** | The HTTP Body you are using to call the HTTP endpoint. | +| `headers` | map[string][]string | **none** | The HTTP headers used when calling the HTTP endpoint (useful for authorization). | +| `timeout` | string | `10000` | Timeout in millisecond when calling the HTTP endpoint. | + +### Google Storage + +| Field name | Type | Default | Description | +|------------|--------|----------|---------------------------------------------------------------------------------------------------------------------------------| +| `kind` | string | **none** | **(mandatory)** Value should be **`googleStorage`**.
_This field is mandatory and describes which retriever you are using._ | +| `bucket` | string | **none** | **(mandatory)** This is the name of your Google Storage bucket _(ex: `my-featureflag-bucket`)_. | +| `object` | string | **none** | **(mandatory)** Path to the file inside the bucket _(ex: `config/flag/my-flags.yaml`)_. | + +### Kubernetes ConfigMap + +_Note that relay proxy is only supporting this while running inside the kubernetes cluster._ + +| Field name | Type | Default | Description | +|-------------|--------|----------|-----------------------------------------------------------------------------------------------------------------------------| +| `kind` | string | **none** | **(mandatory)** Value should be **`configmap`**.
_This field is mandatory and describes which retriever you are using._ | +| `namespace` | string | **none** | **(mandatory)** This is the name of the namespace where your **configmap** is located _(ex: `default`)_. | +| `configmap` | string | **none** | **(mandatory)** Name of the **configmap** we should read _(ex: `feature-flag`)_. | +| `key` | string | **none** | **(mandatory)** Name of the `key` in the **configmap** which contains the flag. | + +### MongoDB + +_To understand the format in which a flag needs to be configured in MongoDB, check +the [example](https://github.com/thomaspoignant/go-feature-flag/examples/retriever_mongodb) available._ + +| Field name | Type | Default | Description | +|--------------|--------|----------|---------------------------------------------------------------------------------------------------------------------------| +| `kind` | string | **none** | **(mandatory)** Value should be **`mongodb`**.
_This field is mandatory and describes which retriever you are using._ | +| `uri` | string | **none** | **(mandatory)** This is the MongoDB URI used in order to connect to the MongoDB instance. | +| `database` | string | **none** | **(mandatory)** Name of the **database** where flags are stored. | +| `collection` | string | **none** | **(mandatory)** Name of the **collection** where flags are stored. | + +### Redis + +_To understand the format in which a flag needs to be configured in **Redis**, check +the [doc](../go_module/store_file/redis#expected-format) available._ + +| Field name | Type | Default | Description | +|------------|--------|----------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `kind` | string | **none** | **(mandatory)** Value should be **`redis`**.
_This field is mandatory and describes which retriever you are using._ | +| `options` | object | **none** | **(mandatory)** Options used to connect to your redis instance.
All the options from the `go-redis` SDK are available _([check `redis.Options`](https://github.com/redis/go-redis/blob/683f4fa6a6b0615344353a10478548969b09f89c/options.go#L31))_ | +| `prefix` | string | **none** | Prefix used before your flag name in the Redis DB. | + +### Bitbucket + +| Field name | Type | Default | Description | +|------------------|--------|----------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `kind` | string | **none** | **(mandatory)** Value should be **`bitbucket`**.
_This field is mandatory and describes which retriever you are using._ | +| `repositorySlug` | string | **none** | **(mandatory)** The repository slug of the Bitbucket repository where your file is located _(ex: `thomaspoignant/go-feature-flag`)_. | +| `path` | string | **none** | **(mandatory)** Path to the file inside the repository _(ex: `config/flag/my-flags.yaml`)_. | +| `baseUrl` | string | `https://gitlab.com` | The base URL of your Bitbucket instance
By default we are using the public API `https://api.bitbucket.org`. | +| `branch` | string | `main` | The branch we should check in the repository. | +| `token` | string | **none** | Bitbucket token used to access a private repository ([_Create a Repository Access Token_](https://support.atlassian.com/bitbucket-cloud/docs/create-a-repository-access-token/)). | +| `timeout` | string | `10000` | Timeout in millisecond used when calling GitLab. | + +### Azure Blob Storage + +| Field name | Type | Default | Description | +|---------------|--------|-----------|------------------------------------------------------------------------------------------------------------------------------------| +| `kind` | string | **none** | **(mandatory)** Value should be **`azureBlobStorage`**.
_This field is mandatory and describes which retriever you are using._ | +| `container` | string | **none** | **(mandatory)** This is the name of your Azure Blob Storage container _(ex: `my-featureflag-container`)_. | +| `accountName` | string | **none** | **(mandatory)** This is the name of your Azure Blob Storage account. | +| `accountKey` | string | **none** | This is the secret key of your Azure Blob Storage account. | +| `object` | string | **none** | **(mandatory)** Location of your configuration file. | + + + +## type `exporter` + +### Webhook + +| Field name | Type | Default | Description | +|--------------------|---------------------|----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `kind` | string | **none** | **(mandatory)** Value should be **`webhook`**.
_This field is mandatory and describes which retriever you are using._ | +| `endpointUrl` | string | **none** | **(mandatory)** EndpointURL of your webhook. | +| `flushInterval` | int | `60000` | The interval in millisecond between 2 calls to the webhook _(if the `maxEventInMemory` is reached before the flushInterval we will call the webhook before)_. | +| `maxEventInMemory` | int | `100000` | If we hit that limit we will call the webhook. | +| `secret` | string | **none** | Secret used to sign your request body and fill the `X-Hub-Signature-256` header.
See [signature section](https://thomaspoignant.github.io/go-feature-flag/latest/data_collection/webhook/#signature) for more details. | +| `meta` | map[string]string | **none** | Add all the information you want to see in your request. | +| `headers` | map[string][]string | **none** | Add all the headers you want to add while calling the endpoint | + +### File + +| Field name | Type | Default | Description | +|---------------------------|--------|------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `kind` | string | **none** | **(mandatory)** Value should be **`file`**.
_This field is mandatory and describes which retriever you are using._ | +| `outputDir` | string | **none** | **(mandatory)** OutputDir is the location of the directory where to store the exported files. | +| `flushInterval` | int | `60000` | The interval in millisecond between 2 calls to the webhook _(if the `maxEventInMemory` is reached before the flushInterval we will call the webhook before)_. | +| `maxEventInMemory` | int | `100000` | If we hit that limit we will call the webhook. | +| `format` | string | `JSON` | Format is the output format you want in your exported file. Available format: `JSON`, `CSV`, `Parquet`. | +| `filename` | string | `flag-variation-{{ .Hostname}}-{{ .Timestamp}}.{{ .Format}}` | You can use a templated config to define the name of your exported files. Available replacements are `{{ .Hostname}}`, `{{ .Timestamp}}` and `{{ .Format}` | +| `csvTemplate` | string | `{{ .Kind}};{{ .ContextKind}};{{ .UserKey}};{{ .CreationDate}};{{ .Key}};{{ .Variation}};{{ .Value}};{{ .Default}};{{ .Source}}\n` | CsvTemplate is used if your output format is CSV.
This field will be ignored if you are using format other than CSV.
You can decide which fields you want in your CSV line with a go-template syntax, please check [`internal/exporter/feature_event.go`](https://github.com/thomaspoignant/go-feature-flag/blob/main/internal/exporter/feature_event.go) to see the fields available. | +| `parquetCompressionCodec` | string | `SNAPPY` | ParquetCompressionCodec is the parquet compression codec for better space efficiency. [Available options](https://github.com/apache/parquet-format/blob/master/Compression.md) | + +### Log + +| Field name | Type | Default | Description | +|--------------------|--------|-------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `kind` | string | **none** | **(mandatory)** Value should be **`log`**.
_This field is mandatory and describes which retriever you are using._ | +| `flushInterval` | int | `60000` | The interval in millisecond between 2 calls to the webhook _(if the `maxEventInMemory` is reached before the flushInterval, we will call the webhook before)_. | +| `maxEventInMemory` | int | `100000` | If we hit that limit we will call the webhook. | +| `logFormat` | string | `[{{ .FormattedDate}}] user="{{ .UserKey}}", flag="{{ .Key}}", value="{{ .Value}}"` | LogFormat is the [template](https://golang.org/pkg/text/template/) configuration of the output format of your log.
You can use all the key from the exporter.FeatureEvent + a key called FormattedDate that represent the date with the RFC 3339 Format. | + +### S3 + +| Field name | Type | Default | Description | +|---------------------------|--------|------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `kind` | string | **none** | **(mandatory)** Value should be **`s3`**.
_This field is mandatory and describes which retriever you are using._ | +| `bucket` | string | **none** | **(mandatory)** Name of your S3 Bucket. | +| `flushInterval` | int | `60000` | The interval in millisecond between 2 calls to the webhook _(if the `maxEventInMemory` is reached before the flushInterval we will call the webhook before)_. | +| `maxEventInMemory` | int | `100000` | If we hit that limit we will call the webhook. | +| `format` | string | `JSON` | Format is the output format you want in your exported file. Available format: `JSON`, `CSV`, `Parquet`. | +| `filename` | string | `flag-variation-{{ .Hostname}}-{{ .Timestamp}}.{{ .Format}}` | You can use a config template to define the name of your exported files. Available replacements are `{{ .Hostname}}`, `{{ .Timestamp}}` and `{{ .Format}` | +| `csvTemplate` | string | `{{ .Kind}};{{ .ContextKind}};{{ .UserKey}};{{ .CreationDate}};{{ .Key}};{{ .Variation}};{{ .Value}};{{ .Default}};{{ .Source}}\n` | CsvTemplate is used if your output format is CSV.
This field will be ignored if you are using format other than CSV.
You can decide which fields you want in your CSV line with a go-template syntax, please check [`internal/exporter/feature_event.go`](https://github.com/thomaspoignant/go-feature-flag/blob/main/internal/exporter/feature_event.go) to see what are the fields available. |` +| `path` | string | **bucket root level** | The location of the directory in S3. | +| `parquetCompressionCodec` | string | `SNAPPY` | ParquetCompressionCodec is the parquet compression codec for better space efficiency. [Available options](https://github.com/apache/parquet-format/blob/master/Compression.md) |` + +### Google Storage + +| Field name | Type | Default | Description | +|---------------------------|--------|------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `kind` | string | **none** | **(mandatory)** Value should be **`s3`**.
_This field is mandatory and describes which retriever you are using._ | +| `bucket` | string | **none** | **(mandatory)** Name of your Google Cloud Storage Bucket. | +| `flushInterval` | int | `60000` | The interval in millisecond between 2 calls to the webhook _(if the `maxEventInMemory` is reached before the flushInterval we will call the webhook before)_. | +| `maxEventInMemory` | int | `100000` | If we hit that limit we will call the webhook. | +| `format` | string | `JSON` | Format is the output format you want in your exported file. Available format: `JSON`, `CSV`, `Parquet`. | +| `filename` | string | `flag-variation-{{ .Hostname}}-{{ .Timestamp}}.{{ .Format}}` | You can use a templated config to define the name of your exported files. Available replacement are `{{ .Hostname}}`, `{{ .Timestamp}}` and `{{ .Format}` | +| `csvTemplate` | string | `{{ .Kind}};{{ .ContextKind}};{{ .UserKey}};{{ .CreationDate}};{{ .Key}};{{ .Variation}};{{ .Value}};{{ .Default}};{{ .Source}}\n` | CsvTemplate is used if your output format is CSV.
This field will be ignored if you are using format other than CSV.
You can decide which fields you want in your CSV line with a go-template syntax, please check [`internal/exporter/feature_event.go`](https://github.com/thomaspoignant/go-feature-flag/blob/main/internal/exporter/feature_event.go) to see what are the fields available. |` +| `path` | string | **bucket root level** | The location of the directory in Google Cloud Storage. | +| `parquetCompressionCodec` | string | `SNAPPY` | ParquetCompressionCodec is the parquet compression codec for better space efficiency. [Available options](https://github.com/apache/parquet-format/blob/master/Compression.md) |` + +### Azure Blob Storage + +| Field name | Type | Default | Description | +|---------------------------|--------|------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `kind` | string | **none** | **(mandatory)** Value should be **`azureBlobStorage`**.
_This field is mandatory and describes which retriever you are using._ | +| `container` | string | **none** | **(mandatory)** This is the name of your Azure Blob Storage container _(ex: `my-featureflag-container`)_. | +| `accountName` | string | **none** | **(mandatory)** This is the name of your Azure Blob Storage account. | +| `accountKey` | string | **none** | This is the secret key of your Azure Blob Storage account. | +| `flushInterval` | int | `60000` | The interval in millisecond between 2 calls to the webhook _(if the `maxEventInMemory` is reached before the flushInterval we will call the webhook before)_. | +| `maxEventInMemory` | int | `100000` | If we hit that limit we will call the webhook. | +| `format` | string | `JSON` | Format is the output format you want in your exported file. Available format: `JSON`, `CSV`, `Parquet`. | +| `filename` | string | `flag-variation-{{ .Hostname}}-{{ .Timestamp}}.{{ .Format}}` | You can use a templated config to define the name of your exported files. Available replacement are `{{ .Hostname}}`, `{{ .Timestamp}}` and `{{ .Format}` | +| `csvTemplate` | string | `{{ .Kind}};{{ .ContextKind}};{{ .UserKey}};{{ .CreationDate}};{{ .Key}};{{ .Variation}};{{ .Value}};{{ .Default}};{{ .Source}}\n` | CsvTemplate is used if your output format is CSV.
This field will be ignored if you are using format other than CSV.
You can decide which fields you want in your CSV line with a go-template syntax, please check [`internal/exporter/feature_event.go`](https://github.com/thomaspoignant/go-feature-flag/blob/main/internal/exporter/feature_event.go) to see what are the fields available. |` +| `path` | string | **bucket root level** | The location of the directory in Azure Blob Storage. | +| `parquetCompressionCodec` | string | `SNAPPY` | ParquetCompressionCodec is the parquet compression codec for better space efficiency. [Available options](https://github.com/apache/parquet-format/blob/master/Compression.md) |` + +### SQS + +| Field name | Type | Default | Description | +|------------|--------|----------|-----------------------------------------------------------------------------------------------------------------------| +| `kind` | string | **none** | **(mandatory)** Value should be **`sqs`**.
_This field is mandatory and describes which retriever you are using._ | +| `queueUrl` | string | **none** | **(mandatory)** URL of your SQS queue.
_You can find it in your AWS console._ | + +### Kafka + +| Field name | Type | Default | Description | +|-------------------|----------|-------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `kind` | string | **none** | **(mandatory)** Value should be **`kafka`**.
_This field is mandatory and describes which retriever you are using._ | +| `kafka.topic` | string | **none** | **(mandatory)** Kafka topic to bind to. | +| `kafka.addresses` | []string | **none** | **(mandatory)** List of bootstrap addresses for the Kafka cluster. | +| `kafka.config` | object | _see description_ | This field allows fine tuning of the Kafka reader. This object should contain the [Sarama configuration](https://pkg.go.dev/github.com/IBM/sarama#Config) that the reader will use. On empty, a sensible default is created using [sarama.NewConfig()](https://pkg.go.dev/github.com/IBM/sarama#NewConfig) | + +### Google PubSub + +| Field name | Type | Default | Description | +|-------------|--------|----------|--------------------------------------------------------------------------------------------------------------------------| +| `kind` | string | **none** | **(mandatory)** Value should be **`pubsub`**.
_This field is mandatory and describes which retriever you are using._ | +| `projectID` | string | **none** | **(mandatory)** Value should be ID of GCP project you are using. | +| `topic` | string | **none** | **(mandatory)** Topic name on which messages will be published. | + +### AWS Kinesis + +| Field name | Type | Default | Description | +|--------------|--------|----------|---------------------------------------------------------------------------------------------------------------------------| +| `kind` | string | **none** | **(mandatory)** Value should be **`kinesis`**.
_This field is mandatory and describes which retriever you are using._ | +| `streamArn` | string | **none** | **(mandatory)** The ARN of your kinesis stream. | +| `streamName` | string | **none** | The name of your kinesis stream. | + + + +## type `notifier` + +### Slack + +| Field name | Type | Default | Description | +|--------------|--------|----------|------------------------------------------------------------------------------------------------------------------------| +| `kind` | string | **none** | **(mandatory)** Value should be **`slack`**.
_This field is mandatory and describe which retriever you are using._ | +| `webhookUrl` | string | **none** | **(mandatory)** The complete URL of your incoming webhook configured in Slack. | + +### Discord + +| Field name | Type | Default | Description | +|--------------|--------|----------|--------------------------------------------------------------------------------------------------------------------------| +| `kind` | string | **none** | **(mandatory)** Value should be **`discord`**.
_This field is mandatory and describe which retriever you are using._ | +| `webhookUrl` | string | **none** | **(mandatory)** The complete URL of your incoming webhook configured in Discord. | + +### Microsoft Teams + +| Field name | Type | Default | Description | +|--------------|--------|----------|---------------------------------------------------------------------------------------------------------------------------------| +| `kind` | string | **none** | **(mandatory)** Value should be **`microsoftteams`**.
_This field is mandatory and describe which retriever you are using._ | +| `webhookUrl` | string | **none** | **(mandatory)** The complete URL of your incoming webhook configured in Discord. | + +### Webhook + +| Field name | Type | Default | Description | +|---------------|---------------------|----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `kind` | string | **none** | **(mandatory)** Value should be **`webhook`**.
_This field is mandatory and describes which retriever you are using._ | +| `endpointUrl` | string | **none** | **(mandatory)** The complete URL of your API (we will send a POST request to this URL, see [format](https://thomaspoignant.github.io/go-feature-flag/latest/notifier/webhook/#format) | +| `secret` | string | **none** | Secret used to sign your request body and fill the `X-Hub-Signature-256` header.
See [signature section](https://thomaspoignant.github.io/go-feature-flag/latest/data_collection/webhook/#signature) for more details. | +| `meta` | map[string]string | **none** | Add all the information you want to see in your request. | +| `headers` | map[string][]string | **none** | Add all the headers you want to add while calling the endpoint | diff --git a/website/versioned_docs/version-v1.40.0/relay_proxy/deploy_relay_proxy.md b/website/versioned_docs/version-v1.40.0/relay_proxy/deploy_relay_proxy.md new file mode 100644 index 00000000000..de701e75b80 --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/relay_proxy/deploy_relay_proxy.md @@ -0,0 +1,66 @@ +--- +sidebar_position: 70 +title: Deployment +description: Deploy the relay proxy. +--- + +# Deploy the relay proxy + +## Deploy in Kubernetes using Helm +The relay proxy can be deployed in Kubernetes using a helm chart. +Helm is an invaluable tool for configuring and deploying applications to a Kubernetes environment. + +Below are the steps for installing a Helm Chart from a **GO Feature Flag** Helm repository. + +### Prerequisites + +- Access to a Kubernetes cluster +- Helm CLI installed on the client machine + +### Step 1: Prepare and Configure the Repository + +Add the repository to Helm with the Helm repository add command and provide a name and the repository URL. For example: + +```shell +helm repo add go-feature-flag https://charts.gofeatureflag.org/ +``` + +### Step 2: Install the Chart + +Install the Helm Chart with the Helm install command and provide the custom repository name, the chart name and any necessary values files. +You can look at the [helm doc](https://github.com/thomaspoignant/go-feature-flag/blob/main/cmd/relayproxy/helm-charts/relay-proxy/README.md) to know exactly what you can change in the values.yaml file. + +```shell +helm install go-feature-flag/relay-proxy -f values.yaml +``` + +### Step 3: Verify The Chart Installation + +Verify the Helm Chart installation with the Helm list command. For example: + +```shell +helm list +``` + +## Deploy as AWS Lambda +The GO Feature Flag relay proxy can easily be launched as an AWS Lambda function. +To do this, simply set the `startAsAwsLambda` option in your configuration file to `true`, like so: + +```yaml +# ... +startAsAwsLambda: true +``` + +Once you've updated your configuration file, you can deploy your function in AWS and configure it to be accessible +via HTTP. This can be achieved by creating an API Gateway or an Application Load Balancer (ALB) and linking it to +your Lambda function. + +By configuring your GO Feature Flag relay proxy to run as an AWS Lambda function, you can take advantage of many +benefits of serverless computing, including automatic scaling, reduced infrastructure costs, and simplified +deployment and management. + +:::info +As part of our release process, we are building an archive ready to be deployed as AWS lambda. +You can find it in the [GitHub release page](https://github.com/thomaspoignant/go-feature-flag/releases),and you can use the assets named `go-feature-flag-aws-lambda_.zip`. +::: + diff --git a/website/versioned_docs/version-v1.40.0/relay_proxy/getting_started.md b/website/versioned_docs/version-v1.40.0/relay_proxy/getting_started.md new file mode 100644 index 00000000000..b776b4079c4 --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/relay_proxy/getting_started.md @@ -0,0 +1,61 @@ +--- +sidebar_position: 19 +title: Getting started +description: Getting started with the relay proxy. +--- + +# Getting started + +Before starting your **relay proxy** you will need to create a minimal configuration file. + +```yaml +# this is a minimal config containing only where your flag file is located +retriever: + kind: http + url: https://raw.githubusercontent.com/thomaspoignant/go-feature-flag/main/cmd/relayproxy/testdata/dockerhub-example/flags.goff.yaml +``` + +After that you can launch the **relay proxy** by using this command: +```shell +go-feature-flag-relay-proxy --config=/path/to/your/configfile +``` + +The **relay proxy** will read the configuration file and retrieve all the flags. +After that you can use all the available endpoints _(see **Service endpoints** section)_ and get the variations for your users. + + +## Deployment options + +A common way to run **go-feature-flag relay proxy** is to use the Docker Container. +An image is available on Docker Hub [`gofeatureflag/go-feature-flag`](https://hub.docker.com/r/thomaspoignant/go-feature-flag). + +You can also run it as a service in your application following the [**Installation**](install_relay_proxy) page. + +```shell +docker run -v $(pwd)/goff-proxy.yaml:/goff/goff-proxy.yaml gofeatureflag/go-feature-flag:latest +``` + +## Specifying a configuration + +To configure the relay proxy you should provide a configuration file when launching the instance. + +The easiest way to provide the file is to use the option `--config=/path_to_your_file.yaml`. +But if you don't provide this option, the relay proxy will look in these folders if a file named `goff-proxy.yaml` is available. + +- **current folder** +- `/goff/` +- `/etc/opt/goff/` + +To learn how to configure the relay proxy, read [Configuration](./configure_relay_proxy). + +## Exporting metrics and traces + +To export the data you can use all the capabilities of `go-feature-flag` SDK. +To configure it please refer to the [type `exporter` section](./configure_relay_proxy#exporter) of the configuration documentation. + +## Service endpoints +The Relay Proxy defines many HTTP/HTTPS endpoints. +Most of these are proxies for GO Feature Flag services, to be used by SDKs that connect to the Relay Proxy. +Others are specific to the Relay Proxy, such as for monitoring its status. + +Please refer to [endpoints documentation](./relay_proxy_endpoints) to get the full details on available REST API endpoints. diff --git a/website/versioned_docs/version-v1.40.0/relay_proxy/index.mdx b/website/versioned_docs/version-v1.40.0/relay_proxy/index.mdx new file mode 100644 index 00000000000..10049d55642 --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/relay_proxy/index.mdx @@ -0,0 +1,20 @@ +--- +title: Use the relay proxy +description: GO Feature Flag Relay Proxy is a powerful tool that allows developers to easily integrate feature flagging functionality into their applications. It is a simple API service that can be called directly through the API or via the available OpenFeature providers. The service wraps the GO Feature Flag golang module to evaluate your flags and allows it to be used with all the available languages. +--- + +import DocCardList from '@theme/DocCardList'; + +# Use the relay proxy + +The GO Feature Flag Relay Proxy serves as the **backend** for your feature flag solution, housing all the necessary logic for feature flag management. + +It provides APIs to enable remote access to the GO Feature Flag system. While you have the option to interact directly with these APIs, it is recommended to use the **Openfeature SDK** and the appropriate **provider** for your programming language for a seamless experience. + +The relay proxy is designed to be **simple, lightweight, and stateless**. It operates without the need for any databases or complex systems, as it loads feature flag configuration files from a specified location and stores them in memory. Periodic polling ensures the proxy stays up-to-date with any changes in the configuration. This straightforward setup makes installation and usage incredibly easy and hassle-free. + +![](/docs/relay_proxy/arch.png) + +--- + + \ No newline at end of file diff --git a/website/versioned_docs/version-v1.40.0/relay_proxy/install_relay_proxy.md b/website/versioned_docs/version-v1.40.0/relay_proxy/install_relay_proxy.md new file mode 100644 index 00000000000..49e55ac5ca6 --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/relay_proxy/install_relay_proxy.md @@ -0,0 +1,31 @@ +--- +sidebar_position: 20 +title: Installation +description: Relay proxy is the component that will evaluate the flags, this page explain how to install it. +--- + +# Install the relay proxy + +## When should I use the GO Feature Flag Relay Proxy? +- If you want to access your feature flag using an API instead of the [`thomaspoignant/go-feature-flag`](https://github.com/thomaspoignant/go-feature-flag) SDK. +- If you are not using GOlang to build your application. +- If you want to reduce the number of accesses to your configuration flag by centralizing them. + + +## Install using Homebrew (mac and linux) +```shell +brew install go-feature-flag +``` + +## Install using Scoop (windows) +```shell +scoop install go-feature-flag +``` + +## Install using docker +```shell +docker pull gofeatureflag/go-feature-flag:latest +``` +:::info +More info in the [dockerhub page](https://hub.docker.com/r/thomaspoignant/go-feature-flag). +::: diff --git a/website/versioned_docs/version-v1.40.0/relay_proxy/monitor_relay_proxy.md b/website/versioned_docs/version-v1.40.0/relay_proxy/monitor_relay_proxy.md new file mode 100644 index 00000000000..e4debfbe510 --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/relay_proxy/monitor_relay_proxy.md @@ -0,0 +1,59 @@ +--- +sidebar_position: 80 +title: Monitoring / Tracing +description: Monitoring and Tracing of the relay proxy. +--- + +## Tracing + +The **relay proxy** is able to trace the requests it is handling. This is done by using OpenTelemetry. + +### Configuration + +The relay proxy will attempt to send traces to an OpenTelemetry collector or +compatible agent if an OpenTelemetry exporter endpoint is configured. + +To set the endpoint, set the `OTEL_EXPORTER_OTLP_ENDPOINT` environment variable +or set `otel.exporter.otlp.endpoint` in the configuration file. + +To override the protocol, set the `OTEL_EXPORTER_OTLP_PROTOCOL` environment variable (`http/protobuf` is the default) or set `otel.exporter.otlp.protocol` in the configuration file. + +See [the OpenTelemetry documentation](https://opentelemetry.io/docs/specs/otel/configuration/sdk-environment-variables/) for more information. + +All your requests will be traced and sent to the collector with the service name **`go-feature-flag`**. + +:::note +If you want to try the OpenTelemetry integration locally, follow this [README](https://github.com/thomaspoignant/go-feature-flag/tree/main/cmd/relayproxy/testdata/opentelemetry) +to setup Jaeger and see your traces. +::: + +## Monitoring + +The **relay proxy** offers some endpoints for you to be able to see how it behaves. + +### `/health` +Making a **GET** request to the URL path `/health` will tell you if the relay proxy is ready to +serve traffic. + +This is useful especially for loadbalancer to know that they can send traffic to the service. + +### `/info` +Making a **GET** request to the URL path `/info` will give you information about the actual state +of the relay proxy. + +### `/metrics` +This endpoint is providing metrics about the relay proxy in the prometheus format. + +## Use specific port for the monitoring +You can configure a different port for the monitoring endpoints. +This is useful if you want to expose the monitoring endpoints on a different port than the main service. + +```yaml +# ... +monitoringPort: 1032 +# ... +``` + +:::note +By default the monitoring endpoints are exposed on the same port as the main service. +::: diff --git a/website/versioned_docs/version-v1.40.0/relay_proxy/profiling.md b/website/versioned_docs/version-v1.40.0/relay_proxy/profiling.md new file mode 100644 index 00000000000..be0442eb3f3 --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/relay_proxy/profiling.md @@ -0,0 +1,33 @@ +--- +sidebar_position: 81 +title: Profiling +description: Profiling of the relay proxy. +--- + +## Profiling + +The **relay proxy** is able to expose profiling information. +This is useful to understand the performance of the service and solve potential issues. + +The information are exposed on the `/debug/pprof` endpoint, and we are using the default `net/http/pprof` package +to expose the information. + +:::warning +By default the profiling endpoints are disabled, and must be enabled in the configuration file. +::: + +List of endpoints exposed is available http://localhost:1031/debug/pprof/ + +### Enable profiling + +In your relay proxy configuration file you need to set the `enablePprof` field to `true`. + +```yaml {5} +retriever: + kind: file + path: /goff/flags.yaml # Location of your feature flag files +# ... +enablePprof: true +``` + +_Note: the `debug` field also enables profiling, but it is deprecated._ diff --git a/website/versioned_docs/version-v1.40.0/relay_proxy/relay_proxy_endpoints.md b/website/versioned_docs/version-v1.40.0/relay_proxy/relay_proxy_endpoints.md new file mode 100644 index 00000000000..192c186e1d0 --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/relay_proxy/relay_proxy_endpoints.md @@ -0,0 +1,26 @@ +--- +sidebar_position: 60 +title: API endpoints +description: Description of the available endpoints in the relay proxy. +--- + +# Relay proxy endpoints + +The most updated documentation about the relay proxy endpoints is the Swagger docs _(see [Swagger section](#swagger) to see how to access to the documentation)_. + +### Swagger +Swagger endpoint is serving a [swagger UI](https://swagger.io/tools/swagger-ui/) to test your REST endpoints. +By default, this endpoint is not exposed, you need to have this configuration in your **relay proxy** configuration file: + +```yaml +# ... + +enableSwagger: true +host: my-proxy-domain.com # the DNS to access the proxy +``` + +When enabled, you can go to the `/swagger/` endpoint with your browser, and you will have access to the Swagger UI for the relay proxy. + +## [OpenAPI documentation](/API_relayproxy) + +If you don't want to install the relay proxy to check the endpoints, you can go to this [**OpenAPI documentation**](/API_relayproxy) directly. diff --git a/website/versioned_docs/version-v1.40.0/tooling/_category_.json b/website/versioned_docs/version-v1.40.0/tooling/_category_.json new file mode 100644 index 00000000000..f7b6b1dd4b9 --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/tooling/_category_.json @@ -0,0 +1,6 @@ +{ + "position": 60, + "label":"Tools", + "collapsible": true, + "collapsed": true +} diff --git a/website/versioned_docs/version-v1.40.0/tooling/autocomplete.md b/website/versioned_docs/version-v1.40.0/tooling/autocomplete.md new file mode 100644 index 00000000000..8455c0aba31 --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/tooling/autocomplete.md @@ -0,0 +1,13 @@ +--- +sidebar_position: 20 +title: Auto-complete +description: Flag configuration auto-complete +--- + +# Flag configuration auto-complete + +GO Feature Flag offers a way to have auto-completion while creating a flag file. + +To achieve this we publish a `jsonschema` on [schemastore](https://www.schemastore.org). The store is used by all major IDEs such as `vscode`, `intelliJ`, and others. + +To enable it, you just have to use the extension `.goff.yaml` for your configuration file, and it will be automatically available for you _(example: `flag.goff.yaml`)_. diff --git a/website/versioned_docs/version-v1.40.0/tooling/cli.mdx b/website/versioned_docs/version-v1.40.0/tooling/cli.mdx new file mode 100644 index 00000000000..b70e4fc1798 --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/tooling/cli.mdx @@ -0,0 +1,32 @@ +--- +sidebar_position: 1 +title: Command Line +description: The GO Feature Flag Command Line is a CLI tool to interact with GO Feature Flag in your terminal. +--- + +# Command Line Tool + +The GO Feature Flag Command Line is a CLI tool to interact with GO Feature Flag in your terminal. + +It offers a variety of commands to interact with GO Feature Flag, for now the supported commands are: +- [`evaluate`](./evaluate) to evaluate feature flags directly in your terminal +- [`lint`](./linter) to validate a configuration file format. + +## Install the GO Feature Flag Command Line + +### Install using Homebrew (mac and linux) +```shell +brew tap thomaspoignant/homebrew-tap +brew install go-feature-flag-cli +``` + +### Install using Scoop (windows) +```shell +scoop bucket add org https://github.com/go-feature-flag/scoop.git +scoop install go-feature-flag-cli +``` + +### Install using Docker +```shell +docker pull thomaspoignant/go-feature-flag-cli:latest +``` \ No newline at end of file diff --git a/website/versioned_docs/version-v1.40.0/tooling/evaluate.md b/website/versioned_docs/version-v1.40.0/tooling/evaluate.md new file mode 100644 index 00000000000..6e5fd3ce4d1 --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/tooling/evaluate.md @@ -0,0 +1,36 @@ +--- +sidebar_position: 11 +title: Evaluate command line +description: Evaluate feature flags directly in your terminal +--- + +# Evaluate feature flags directly in your terminal + +Sometimes for debug or testing purposes, you may want to be able to know what will be the variant used during the evaluation of your feature flag. +With the GO Feature Flag Command Line, you can evaluate a feature flag directly in your terminal. + +:::tip +You can also use the `evaluate` command to use feature flags in your CI/CD pipelines. +::: + +## Install the Command Line + +Check the [installation guide](./cli) to install the `go-feature-flag-cli`. + +## Use the evaluate command in your terminal + +```shell +./go-feature-flag-cli evaluate \ + --config="" \ + --format="yaml" \ + --flag="" \ + --ctx='' +``` + +| param | description | +|------------|------------------------------------------------------------------------------------------------------------------------| +| `--config` | **(mandatory)** The location of your configuration file. | +| `--ctx` | **(mandatory)** The evaluation context used to evaluate the flag in json format (ex: `{"targetingKey":"123"}`). | +| `--format` | The format of your configuration flag _(acceptable values:`yaml`, `json`, `toml`)_.
Default: **`yaml`** | +| `--flag` | The name of the flag you want to evaluate, if omitted all flags will be evaluated | + diff --git a/website/versioned_docs/version-v1.40.0/tooling/linter.mdx b/website/versioned_docs/version-v1.40.0/tooling/linter.mdx new file mode 100644 index 00000000000..a0a788c15ee --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/tooling/linter.mdx @@ -0,0 +1,96 @@ +--- +sidebar_position: 10 +title: Linter +description: Lint your configuration file +--- + + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +# Lint your configuration file + +Ensuring the accuracy of your configuration is vital for the **GO Feature Flag** to function as expected. +This is why you can lint your configuration file using the [`go-feature-flag-cli`](./cli), a command line tool that allow to interact with GO Feature Flag in your terminal. The `lint` command validates whether a flag file can be parsed by **GO Feature Flag**. + +:::tip +We recommend you to use this command line in your CI/CD pipelines to avoid any unforeseen issues. +::: + +## Install the Command Line + +Check the [installation guide](./cli) to install the `go-feature-flag-cli`. + +## Use the linter + +```shell +./go-feature-flag-cli lint /input/my-go-feature-flag-config.goff.yaml --format=yaml +``` + +You have to pass the location of your configuration file and the format of your current configuration file _(available formats are `yaml`, `json`, `toml`)_. + +## Use the linter in your CI (continuous integration) + +You can run `go-feature-flag-cli` directly in your CI: + + + + +```yaml +name: "Build" +on: + push: + branches: + - main + pull_request: + types: [ opened, synchronize, reopened ] + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Lint the config file + uses: go-feature-flag/gofeatureflag-lint-action@v1 + with: + flag-file: ./path/to/your/config.yaml + format: yaml + + ``` + + + + +```yaml +version: 2.1 +jobs: + build: + docker: + - image: cimg/base:2022.05 + + steps: + - checkout + - run: curl -L $(curl -s https://api.github.com/repos/thomaspoignant/go-feature-flag/releases/latest | jq -r '.assets[] | select(.name|match("Linux_x86_64.tar.gz$")) | .browser_download_url' | grep 'go-feature-flag-cli') --output release.tar.gz && tar -zxvf release.tar.gz + - run: ./go-feature-flag-cli lint flag-config.goff.yaml --format=yaml # please put the right file name +``` + + + + +```yaml +image: ubuntu +lint-job: + stage: build + + before_script: + - apt-get -qq update + - apt-get install -y jq curl + + script: + - curl -L $(curl -s https://api.github.com/repos/thomaspoignant/go-feature-flag/releases/latest | jq -r '.assets[] | select(.name|match("Linux_x86_64.tar.gz$")) | .browser_download_url' | grep 'go-feature-flag-cli') --output release.tar.gz && tar -zxvf release.tar.gz + - ./go-feature-flag-cli lint flag-config.goff.yaml --format=yaml # please put the right file name +``` + + + + diff --git a/website/versioned_docs/version-v1.40.0/tooling/migrate_v0_v1.md b/website/versioned_docs/version-v1.40.0/tooling/migrate_v0_v1.md new file mode 100644 index 00000000000..ec1bf6c774c --- /dev/null +++ b/website/versioned_docs/version-v1.40.0/tooling/migrate_v0_v1.md @@ -0,0 +1,64 @@ +--- +sidebar_position: 90 +description: How to migrate from v0.x.x to v1.x.x +--- + +--- +**⚠️ Version `v1.35.0` will be the last version of the cli.**. +**Why? Because it is feature complete and because it has been decided to stop supporting `v0.x.x` format.** +--- + +# Migrate from v0.x.x to v1.x.x + +:::info +Version `v1.0.0` has introduced a new flag format that push the limits of **GO Feature Flag** even further. +**NOTE:** The flag format from all the versions `v0.x.x` are still compatible and supported by the `v1.0.0`. +::: + +A command line is available to help you to convert your actual configuration file to the version `v1.x.x`. + + +## Install the migration command line + +### Install using Homebrew (mac and linux) +```shell +brew tap thomaspoignant/homebrew-tap +brew install go-feature-flag-migration-cli +``` + +### Install using Scoop (windows) +```shell +scoop bucket add org https://github.com/go-feature-flag/scoop.git +scoop install go-feature-flag-migration-cli +``` + +### Install using Docker +```shell +docker pull thomaspoignant/go-feature-flag-migration-cli:latest +``` + +## Use the migration command line + +```shell +./go-feature-flag-migration-cli \ + --input-format=yaml \ + --input-file=/config/my-go-feature-flag-config-v0.x.x.yaml \ + --output-format=yaml \ + --output-file=/config/my-go-feature-flag-config-v1.x.x.yaml +``` + +The command line has 4 arguments you should specify. + +- `input-format`: Format of your input file (`YAML`, `JSON` or `TOML`). +- `input-file`: Location of the flag file you want to convert. +- `output-format`: Format of your output file (`YAML`, `JSON` or `TOML`). +- `output-file`: Location of the converted flag file. + + +## Update your flag file + +When your file is ready, you just have to replace your file in the location where GO Feature Flag is retrieves it. + +:::tip +If for any reason your file is not readable by GO Feature Flag, it will not break anything, we will keep the latest version we have in memory. +::: diff --git a/website/versioned_sidebars/version-v1.40.0-sidebars.json b/website/versioned_sidebars/version-v1.40.0-sidebars.json new file mode 100644 index 00000000000..caea0c03ba6 --- /dev/null +++ b/website/versioned_sidebars/version-v1.40.0-sidebars.json @@ -0,0 +1,8 @@ +{ + "tutorialSidebar": [ + { + "type": "autogenerated", + "dirName": "." + } + ] +} diff --git a/website/versions.json b/website/versions.json index 2dd9a91d918..df0014fb96d 100644 --- a/website/versions.json +++ b/website/versions.json @@ -1 +1 @@ -["v1.39.1","v1.39.0","v1.38.0","v1.37.1","v1.37.0","v1.36.1","v1.35.0","v1.34.3"] \ No newline at end of file +["v1.40.0","v1.39.1","v1.39.0","v1.38.0","v1.37.1","v1.37.0","v1.36.1","v1.35.0"] \ No newline at end of file diff --git a/website/versions.json.bak b/website/versions.json.bak index 64cb1754ff2..2ca68a5b150 100644 --- a/website/versions.json.bak +++ b/website/versions.json.bak @@ -1,4 +1,5 @@ [ + "v1.40.0", "v1.39.1", "v1.39.0", "v1.38.0", @@ -6,6 +7,5 @@ "v1.37.0", "v1.36.1", "v1.35.0", - "v1.34.3", - "v1.34.2" + "v1.34.3" ]