├── .husky ├── _ │ └── .gitignore └── pre-commit ├── docs ├── placeholder ├── CNAME ├── _config.yml ├── Gemfile ├── assets │ ├── tweek.png │ ├── architecture.png │ ├── inverted-logo.png │ ├── logo-with-background.png │ ├── openapi.html │ └── folder-icon-closed.svg ├── _layouts │ ├── home.html │ ├── openapi.html │ ├── play-with-tweek.html │ └── default.html ├── pages │ ├── 2.concepts │ │ ├── 03.jpad │ │ │ ├── 01.intro.md │ │ │ └── 02.rules.md │ │ └── 01.keys │ │ │ └── 02.dependent-keys.md │ ├── 5.deployment │ │ └── 04.configuration │ │ │ └── 02.gateway.md │ ├── 3.usage │ │ └── 02.editor │ │ │ ├── 02.dependent-keys.md │ │ │ ├── 01.create-new-key.md │ │ │ ├── 04.override-context.md │ │ │ └── 03.define-identity-schema.md │ ├── play-with-tweek.md │ ├── 6.reference │ │ └── redoc.md │ └── 1.getting-started │ │ └── 01.first-setup.md ├── docker-compose.yml └── _includes │ └── header.html ├── e2e ├── ui │ ├── .gitignore │ ├── .dockerignore │ ├── Dockerfile │ ├── utils │ │ ├── selector-utils.js │ │ ├── assertion-utils.js │ │ ├── constants.js │ │ └── location-utils.js │ ├── pages │ │ ├── Keys │ │ │ ├── TypedInput │ │ │ │ ├── index.js │ │ │ │ ├── ObjectInput.js │ │ │ │ └── TagInput.js │ │ │ ├── ConstValue.js │ │ │ ├── Rules │ │ │ │ └── NewRule.js │ │ │ └── ObjectEditor.js │ │ ├── Context │ │ │ ├── Property.js │ │ │ └── FixedKey.js │ │ ├── Settings │ │ │ └── Identity.js │ │ └── Alert.js │ ├── clients │ │ ├── api-client.js │ │ └── tweek-clients.js │ └── spec │ │ ├── keys │ │ └── readonly-key.js │ │ └── login │ │ ├── redirect-to-login-page.js │ │ └── authorization.js └── integration │ ├── .dockerignore │ ├── spec │ ├── authoring-api │ │ └── test-data │ │ │ ├── bulk1.zip │ │ │ ├── invalidRules.zip │ │ │ ├── invalidStructure.zip │ │ │ └── subject_extraction_rules.rego │ ├── setup.js │ ├── tweek-api │ │ ├── key-aliases.test.js │ │ └── value-distribution.test.js │ └── gateway │ │ └── v2_cors.spec.js │ ├── Dockerfile │ └── utils │ └── manifest.js ├── services ├── publishing │ ├── .gitignore │ ├── base-repo │ │ ├── hooks.json │ │ ├── tags.json │ │ ├── implementations │ │ │ └── jpad │ │ │ │ └── @tweek │ │ │ │ └── auth │ │ │ │ └── @global │ │ │ │ ├── write_context.jpad │ │ │ │ └── read_configuration.jpad │ │ ├── security │ │ │ ├── subject_extraction_rules.rego │ │ │ └── policy.json │ │ └── manifests │ │ │ └── @tweek │ │ │ ├── editor │ │ │ ├── google_tag_manager │ │ │ │ ├── id.json │ │ │ │ └── enabled.json │ │ │ ├── search │ │ │ │ └── max_results.json │ │ │ ├── history │ │ │ │ └── since.json │ │ │ ├── show_internal_keys.json │ │ │ └── service_worker │ │ │ │ └── is_enabled.json │ │ │ ├── auth │ │ │ ├── @global │ │ │ │ ├── write_context.json │ │ │ │ └── read_configuration.json │ │ │ └── tweek_editor_user │ │ │ │ └── write_context.json │ │ │ └── custom_types │ │ │ └── version.json │ ├── .dockerignore │ ├── scripts │ │ └── ssh-helper.sh │ ├── hooks │ │ ├── post-receive │ │ └── pre-receive │ └── Tweek.Publishing.Service │ │ ├── appsettings.json │ │ ├── Validation │ │ ├── IValidator.cs │ │ ├── CircularValidationException.cs │ │ ├── PolicyValidationException.cs │ │ ├── SubjectExtractionRulesValidationException.cs │ │ ├── Patterns.cs │ │ └── ManifestStructureException.cs │ │ ├── Model │ │ ├── VersionsBlob.cs │ │ ├── Rules │ │ │ └── KeyDef.cs │ │ └── ExternalApps │ │ │ ├── SecretKey.cs │ │ │ └── ExternalApp.cs │ │ ├── Sync │ │ ├── Converters │ │ │ └── IConverter.cs │ │ └── RevisionException.cs │ │ ├── Storage │ │ └── IObjectStorage.cs │ │ └── Utils │ │ └── ConfigurationHelper.cs ├── authoring │ ├── .prettierignore │ ├── .gitignore │ ├── ssh-helper.cmd │ ├── ssh-helper │ ├── ssh-helper.sh │ ├── src │ │ ├── utils │ │ │ ├── author.ts │ │ │ ├── jsonValue.ts │ │ │ ├── hook.ts │ │ │ ├── logger.ts │ │ │ ├── response-utils.ts │ │ │ ├── error-utils.ts │ │ │ └── requestLogger.ts │ │ ├── security │ │ │ ├── types.d.ts │ │ │ ├── configure-passport.ts │ │ │ ├── permissions │ │ │ │ └── consts.ts │ │ │ └── strategies │ │ │ │ └── tweek-internal.ts │ │ ├── routes │ │ │ └── config.ts │ │ └── search-index │ │ │ └── get-manifests.ts │ ├── .prettierrc │ ├── .eslintignore │ ├── .dockerignore │ ├── tsconfig.json │ └── .eslintrc ├── git-service │ └── BareRepository │ │ ├── source │ │ ├── hooks.json │ │ ├── tags.json │ │ ├── implementations │ │ │ └── jpad │ │ │ │ └── @tweek │ │ │ │ └── auth │ │ │ │ └── @global │ │ │ │ ├── write_context.jpad │ │ │ │ └── read_configuration.jpad │ │ ├── security │ │ │ ├── subject_extraction_rules.rego │ │ │ └── policy.json │ │ └── manifests │ │ │ └── @tweek │ │ │ ├── editor │ │ │ ├── google_tag_manager │ │ │ │ ├── id.json │ │ │ │ └── enabled.json │ │ │ ├── search │ │ │ │ └── max_results.json │ │ │ ├── history │ │ │ │ └── since.json │ │ │ ├── show_internal_keys.json │ │ │ └── service_worker │ │ │ │ └── is_enabled.json │ │ │ ├── auth │ │ │ ├── @global │ │ │ │ ├── write_context.json │ │ │ │ └── read_configuration.json │ │ │ └── tweek_editor_user │ │ │ │ └── write_context.json │ │ │ └── custom_types │ │ │ └── version.json │ │ └── tests-source │ │ ├── hooks.json │ │ ├── tags.json │ │ ├── implementations │ │ └── jpad │ │ │ ├── behavior_tests │ │ │ ├── revision_history.jpad │ │ │ ├── edit_key │ │ │ │ ├── text │ │ │ │ │ └── edit_test.jpad │ │ │ │ └── visual │ │ │ │ │ ├── edit_test.jpad │ │ │ │ │ ├── edit_array_test.jpad │ │ │ │ │ └── edit_object_test.jpad │ │ │ ├── dependent_keys │ │ │ │ ├── fail │ │ │ │ │ ├── third.jpad │ │ │ │ │ ├── first.jpad │ │ │ │ │ └── second.jpad │ │ │ │ ├── pass │ │ │ │ │ ├── used_by.jpad │ │ │ │ │ └── depends_on.jpad │ │ │ │ └── display │ │ │ │ │ ├── used_by.jpad │ │ │ │ │ ├── depends_on.jpad │ │ │ │ │ └── depends_on_alias.jpad │ │ │ ├── partitions │ │ │ │ ├── empty_partition.jpad │ │ │ │ └── add_partition_group.jpad │ │ │ └── read_only.jpad │ │ │ ├── @tweek │ │ │ ├── auth │ │ │ │ └── @global │ │ │ │ │ ├── write_context.jpad │ │ │ │ │ └── read_configuration.jpad │ │ │ └── editor │ │ │ │ └── google_tag_manager │ │ │ │ ├── id.jpad │ │ │ │ └── enabled.jpad │ │ │ ├── smoke_tests │ │ │ ├── value_distribution │ │ │ │ ├── bernoulli.jpad │ │ │ │ └── weighted_normalized.jpad │ │ │ └── rule_based_keys │ │ │ │ └── simple.jpad │ │ │ ├── integration_tests │ │ │ ├── include_errors.jpad │ │ │ └── value_distribution │ │ │ │ └── object_format.jpad │ │ │ └── @tweek_clients_tests │ │ │ ├── test_category │ │ │ ├── test_key1.jpad │ │ │ └── test_key2.jpad │ │ │ └── test_category2 │ │ │ └── user_fruit.jpad │ │ ├── manifests │ │ ├── integration_tests │ │ │ ├── alias_key.json │ │ │ ├── some_key.json │ │ │ ├── include_errors.json │ │ │ └── value_distribution │ │ │ │ ├── array_format.json │ │ │ │ └── object_format.json │ │ ├── behavior_tests │ │ │ ├── delete_key │ │ │ │ ├── delete_alias.json │ │ │ │ ├── delete.json │ │ │ │ └── archive.json │ │ │ ├── key_aliases │ │ │ │ ├── alias_key.json │ │ │ │ ├── delete_alias.json │ │ │ │ ├── alias_to_alias.json │ │ │ │ └── regular_key.json │ │ │ ├── dependent_keys │ │ │ │ ├── display │ │ │ │ │ ├── alias_key.json │ │ │ │ │ ├── used_by.json │ │ │ │ │ ├── depends_on.json │ │ │ │ │ └── depends_on_alias.json │ │ │ │ ├── fail │ │ │ │ │ ├── third.json │ │ │ │ │ ├── first.json │ │ │ │ │ └── second.json │ │ │ │ └── pass │ │ │ │ │ ├── used_by.json │ │ │ │ │ └── depends_on.json │ │ │ ├── tags.json │ │ │ ├── routing.json │ │ │ ├── context │ │ │ │ └── override_key.json │ │ │ ├── read_only.json │ │ │ ├── keys_list │ │ │ │ ├── banana.json │ │ │ │ ├── red_apple.json │ │ │ │ └── green_apple.json │ │ │ ├── revision_history.json │ │ │ ├── edit_key │ │ │ │ ├── text │ │ │ │ │ └── edit_test.json │ │ │ │ └── visual │ │ │ │ │ ├── const │ │ │ │ │ ├── number_type.json │ │ │ │ │ ├── string_type.json │ │ │ │ │ ├── date_type.json │ │ │ │ │ └── object_type.json │ │ │ │ │ ├── edit_test.json │ │ │ │ │ ├── edit_array_test.json │ │ │ │ │ └── edit_object_test.json │ │ │ └── partitions │ │ │ │ ├── add_partition.json │ │ │ │ ├── empty_partition.json │ │ │ │ ├── delete_partition.json │ │ │ │ ├── partition_groups.json │ │ │ │ └── add_partition_group.json │ │ ├── smoke_tests │ │ │ ├── key_path │ │ │ │ ├── key1.json │ │ │ │ └── key2.json │ │ │ ├── key_path_.json │ │ │ ├── rule_based_keys │ │ │ │ ├── in.json │ │ │ │ ├── simple.json │ │ │ │ ├── comparison.json │ │ │ │ ├── array_contains.json │ │ │ │ └── multi_conditions.json │ │ │ ├── identity_context │ │ │ │ └── color.json │ │ │ ├── key_path_suffix │ │ │ │ └── key.json │ │ │ ├── not_hidden │ │ │ │ ├── some_key.json │ │ │ │ ├── @hidden │ │ │ │ │ ├── @hidden_key.json │ │ │ │ │ └── visible_key.json │ │ │ │ └── @some_hidden_key.json │ │ │ ├── ignore_key_types │ │ │ │ ├── number_type.json │ │ │ │ ├── boolean_type.json │ │ │ │ ├── string_type.json │ │ │ │ ├── array_type.json │ │ │ │ └── object_type.json │ │ │ └── value_distribution │ │ │ │ ├── bernoulli.json │ │ │ │ └── weighted_normalized.json │ │ ├── @tweek │ │ │ ├── schema │ │ │ │ ├── other.json │ │ │ │ ├── delete_property_test.json │ │ │ │ ├── edit_properties_test.json │ │ │ │ └── test.json │ │ │ ├── editor │ │ │ │ ├── google_tag_manager │ │ │ │ │ ├── id.json │ │ │ │ │ └── enabled.json │ │ │ │ ├── search │ │ │ │ │ └── max_results.json │ │ │ │ ├── history │ │ │ │ │ └── since.json │ │ │ │ ├── show_internal_keys.json │ │ │ │ └── service_worker │ │ │ │ │ └── is_enabled.json │ │ │ ├── auth │ │ │ │ ├── @global │ │ │ │ │ ├── write_context.json │ │ │ │ │ └── read_configuration.json │ │ │ │ └── tweek_editor_user │ │ │ │ │ └── write_context.json │ │ │ └── custom_types │ │ │ │ └── version.json │ │ └── @tweek_clients_tests │ │ │ ├── test_category │ │ │ ├── test_key2.json │ │ │ └── test_key1.json │ │ │ └── test_category2 │ │ │ └── user_fruit.json │ │ └── security │ │ └── subject_extraction_rules.rego ├── editor │ ├── .eslintrc │ ├── src │ │ ├── react-app-env.d.ts │ │ ├── styles │ │ │ ├── core │ │ │ │ ├── accents.less │ │ │ │ ├── fonts │ │ │ │ │ ├── resources │ │ │ │ │ │ ├── tweekIcons.eot │ │ │ │ │ │ ├── tweekIcons.ttf │ │ │ │ │ │ └── tweekIcons.woff │ │ │ │ │ └── fonts.less │ │ │ │ └── accents │ │ │ │ │ └── lightBlue.less │ │ │ └── styles.less │ │ ├── utils │ │ │ ├── index.ts │ │ │ ├── hooks │ │ │ │ ├── index.ts │ │ │ │ ├── useDebounceValue.ts │ │ │ │ └── useSearchConfig.ts │ │ │ └── tweekClients.ts │ │ ├── components │ │ │ ├── NoMatch.tsx │ │ │ ├── common │ │ │ │ ├── Input │ │ │ │ │ ├── Input.less │ │ │ │ │ └── DateInput.less │ │ │ │ ├── ComboBox │ │ │ │ │ ├── ComboBox.less │ │ │ │ │ └── MultiSourceComboBox.less │ │ │ │ ├── Label │ │ │ │ │ ├── Label.less │ │ │ │ │ └── Label.tsx │ │ │ │ ├── SaveButton │ │ │ │ │ └── SaveButton.less │ │ │ │ ├── EditableText │ │ │ │ │ └── EditableText.less │ │ │ │ ├── EditableTextArea │ │ │ │ │ └── EditableTextArea.less │ │ │ │ └── __snapshots__ │ │ │ │ │ └── ErrorHandler.spec.js.snap │ │ │ ├── JPadFullEditor │ │ │ │ ├── JPadTextEditor │ │ │ │ │ └── JPadTextEditor.less │ │ │ │ └── JPadVisualEditor │ │ │ │ │ ├── Rule │ │ │ │ │ ├── DefaultValue.less │ │ │ │ │ ├── DefaultValue.tsx │ │ │ │ │ └── Rule.less │ │ │ │ │ ├── Matcher │ │ │ │ │ └── Properties │ │ │ │ │ │ └── styles.less │ │ │ │ │ ├── Partition │ │ │ │ │ └── PartitionsSelector.less │ │ │ │ │ └── JPadVisualEditor.less │ │ │ ├── AppPage.tsx │ │ │ ├── GoogleTagManager.ts │ │ │ └── ConstEditor.tsx │ │ ├── contexts │ │ │ ├── SelectedKey │ │ │ │ ├── index.ts │ │ │ │ ├── useSelectedKey.ts │ │ │ │ ├── localKeyStorage.ts │ │ │ │ └── SelectedKey.ts │ │ │ └── TypesService.ts │ │ ├── pages │ │ │ ├── keys │ │ │ │ ├── components │ │ │ │ │ ├── KeyPage │ │ │ │ │ │ ├── MessageKeyPage │ │ │ │ │ │ │ ├── MessageKeyPage.less │ │ │ │ │ │ │ └── MessageKeyPage.tsx │ │ │ │ │ │ ├── KeyPage.less │ │ │ │ │ │ ├── KeyAddPage │ │ │ │ │ │ │ └── NewKeyInput.less │ │ │ │ │ │ └── KeyEditPage │ │ │ │ │ │ │ └── KeyTags │ │ │ │ │ │ │ └── KeyTags.less │ │ │ │ │ ├── KeysList │ │ │ │ │ │ ├── TreeView │ │ │ │ │ │ │ └── utils.ts │ │ │ │ │ │ └── resources │ │ │ │ │ │ │ └── Folder-icon-closed.svg │ │ │ │ │ └── utils │ │ │ │ │ │ └── keyFormatHelpers.ts │ │ │ │ └── utils │ │ │ │ │ └── search.ts │ │ │ ├── context │ │ │ │ └── components │ │ │ │ │ ├── FixedKeys │ │ │ │ │ ├── FixedKeysList │ │ │ │ │ │ └── FixedKeysList.less │ │ │ │ │ └── FixedKeys.less │ │ │ │ │ └── ContextPage │ │ │ │ │ ├── ContextPage.less │ │ │ │ │ └── SearchBox │ │ │ │ │ └── SearchBox.less │ │ │ └── settings │ │ │ │ └── components │ │ │ │ ├── HooksPage │ │ │ │ ├── HookTypes.js │ │ │ │ └── WebHookFormats.js │ │ │ │ ├── PoliciesPage │ │ │ │ └── JWTExtraction.js │ │ │ │ └── ExternalAppsPage │ │ │ │ └── CreateExternalAppSecret.less │ │ ├── index.js │ │ ├── services │ │ │ └── auth │ │ │ │ └── clients │ │ │ │ └── storage.ts │ │ └── resources │ │ │ ├── archive-icon.svg │ │ │ └── key-icon.svg │ ├── public │ │ ├── tweek.png │ │ ├── favicon.ico │ │ └── manifest.json │ ├── debug.Dockerfile │ ├── .babelrc │ ├── .editorconfig │ ├── .gitignore │ ├── .dockerignore │ ├── tsconfig.json │ └── Dockerfile ├── gateway │ ├── version.go │ ├── .dockerignore │ ├── utils │ │ └── arrayUtils.go │ ├── healthcheck │ │ └── healthcheck.go │ ├── handlers │ │ └── healthHandler.go │ ├── security │ │ └── testdata │ │ │ └── subject_extraction_rules.rego │ ├── Makefile │ ├── .gitignore │ ├── audit │ │ └── auditor.go │ ├── corsSupport │ │ └── corsSupport.go │ ├── debug.Dockerfile │ └── .vscode │ │ └── launch.json └── api │ ├── Tweek.ApiService │ ├── runtimeconfig.template.json │ ├── app.config │ ├── appsettings.Development.json │ ├── .vscode │ │ └── tasks.json │ ├── Security │ │ └── ClaimsPrincipalExtentions.cs │ ├── Utils │ │ └── TimerOptionsExtentions.cs │ └── Controllers │ │ ├── RepoVersionController.cs │ │ └── DiagnosticsController.cs │ ├── Tweek.ApiService.SmokeTests │ ├── test.sh │ ├── GetConfigurations │ │ └── Models │ │ │ └── TestContext.cs │ └── RepositoryVersionTests.cs │ └── Tweek.ApiService.Tests │ └── InMemoryContextServiceAddon.cs ├── deployments ├── dev │ ├── minio │ │ ├── access_key │ │ └── secret_key │ ├── ssh │ │ ├── tweekgit_public.pfx │ │ ├── Dockerfile │ │ └── tweekgit.pub │ └── gateway │ │ └── config │ │ └── gateway.test.json └── kubernetes │ ├── infra │ ├── nats.yaml │ └── redis.yaml │ └── editor.yaml ├── .prettierrc ├── lint-staged.config.js ├── .dockerignore ├── core ├── Engine │ ├── Tweek.Engine.Core │ │ ├── Rules │ │ │ ├── IRuleParser.cs │ │ │ ├── IRule.cs │ │ │ └── RuleSet.cs │ │ ├── Tweek.Engine.Core.csproj │ │ ├── app.config │ │ ├── Context │ │ │ └── Context.cs │ │ └── Utils │ │ │ ├── OptionHelpers.cs │ │ │ └── OptionIEnumrableHelpers.cs │ ├── Tweek.Engine.Drivers │ │ ├── Context │ │ │ ├── IContextDriver.cs │ │ │ ├── IContextReader.cs │ │ │ └── IContextWriter.cs │ │ ├── Rules │ │ │ ├── IRulesetVersionProvider.cs │ │ │ └── IRulesDriver.cs │ │ ├── Utils │ │ │ └── JsonValueExtensions.cs │ │ └── Tweek.Engine.Drivers.csproj │ ├── Tweek.Engine │ │ ├── Collections │ │ │ └── LeafNode.cs │ │ ├── Rules │ │ │ └── Schema │ │ │ │ └── ContextValidation.cs │ │ ├── Tweek.cs │ │ ├── app.config │ │ ├── ITweek.cs │ │ └── Tweek.Engine.csproj │ ├── Tweek.Engine.DataTypes │ │ ├── Tweek.Engine.DataTypes.csproj │ │ ├── app.config │ │ └── Identity.cs │ └── Tweek.Engine.Tests │ │ └── TestDrivers │ │ └── ITestDriver.cs ├── Utils │ └── JsonValueConverter │ │ ├── JsonValueConverter.csproj │ │ └── app.config ├── JPad │ └── Tweek.JPad.Utils │ │ ├── app.config │ │ └── Tweek.JPad.Utils.csproj └── Tweek.ApiService.Addons │ ├── ITweekAddon.cs │ └── TweekContractResolver.cs ├── .github └── workflows │ └── push-tag.sh ├── .vscode ├── cSpell.json └── settings.json ├── addons ├── Rules │ ├── Tweek.Drivers.Rules.Minio │ │ ├── MinioSettings.cs │ │ ├── Tweek.Drivers.Rules.Minio.csproj │ │ └── ConfigurationExtention.cs │ └── Tweek.Drivers.Rules.FileSystem │ │ └── Tweek.Drivers.Rules.FileSystem.csproj ├── Context │ ├── Tweek.Drivers.Context.Couchbase.IntegrationTests │ │ ├── setup-couchbase.sh │ │ └── docker-compose.yml │ ├── Tweek.Drivers.Context.Redis.IntegrationTests │ │ └── RedisIntegrationTests.cs │ ├── Tweek.Drivers.Context.MongoDb.IntegrationTests │ │ └── MongoDbIntegrationTests.cs │ ├── Tweek.Drivers.Context.InMemory │ │ └── InMemoryServiceAddon.cs │ ├── Tweek.Drivers.Context.Couchbase │ │ └── app.config │ ├── Tweek.Drivers.Context.Redis │ │ └── RedisServiceAddon.cs │ └── Tweek.Drivers.Context.MongoDb │ │ └── MongoDbServiceAddon.cs └── ApplicationInsights │ └── Tweek.Addons.ApplicationInsights │ └── Tweek.Addons.ApplicationInsights.csproj ├── TweekApiSmokeTest.Dockerfile ├── tweek.code-workspace ├── .devcontainer └── install-all-deps └── utils └── generate_keys.sh /.husky/_/.gitignore: -------------------------------------------------------------------------------- 1 | * -------------------------------------------------------------------------------- /docs/placeholder: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /docs/CNAME: -------------------------------------------------------------------------------- 1 | tweek.soluto.io -------------------------------------------------------------------------------- /e2e/ui/.gitignore: -------------------------------------------------------------------------------- 1 | screenshots 2 | -------------------------------------------------------------------------------- /e2e/integration/.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules/ -------------------------------------------------------------------------------- /services/publishing/.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | -------------------------------------------------------------------------------- /services/publishing/base-repo/hooks.json: -------------------------------------------------------------------------------- 1 | [] 2 | -------------------------------------------------------------------------------- /services/publishing/base-repo/tags.json: -------------------------------------------------------------------------------- 1 | [] 2 | -------------------------------------------------------------------------------- /deployments/dev/minio/access_key: -------------------------------------------------------------------------------- 1 | AKIAIOSFODNN7EXAMPLE -------------------------------------------------------------------------------- /services/authoring/.prettierignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist -------------------------------------------------------------------------------- /services/git-service/BareRepository/source/hooks.json: -------------------------------------------------------------------------------- 1 | [] 2 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/source/tags.json: -------------------------------------------------------------------------------- 1 | [] 2 | -------------------------------------------------------------------------------- /services/editor/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["react-app"] 3 | } 4 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/hooks.json: -------------------------------------------------------------------------------- 1 | [] 2 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/tags.json: -------------------------------------------------------------------------------- 1 | [] 2 | -------------------------------------------------------------------------------- /services/publishing/.dockerignore: -------------------------------------------------------------------------------- 1 | Dockerfile 2 | **/bin 3 | **/obj -------------------------------------------------------------------------------- /deployments/dev/minio/secret_key: -------------------------------------------------------------------------------- 1 | wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY -------------------------------------------------------------------------------- /docs/_config.yml: -------------------------------------------------------------------------------- 1 | repository: soluto/tweek 2 | 3 | theme: "minima" 4 | -------------------------------------------------------------------------------- /services/editor/src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /services/gateway/version.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | const Version = "1.0.0-rc22" 4 | -------------------------------------------------------------------------------- /docs/Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | gem "github-pages", group: :jekyll_plugins -------------------------------------------------------------------------------- /docs/assets/tweek.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Soluto/tweek/HEAD/docs/assets/tweek.png -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | yarn lint-staged -------------------------------------------------------------------------------- /services/authoring/.gitignore: -------------------------------------------------------------------------------- 1 | /rulesRepository 2 | searchIndex.json 3 | dist/ 4 | uploads/ 5 | -------------------------------------------------------------------------------- /services/authoring/ssh-helper.cmd: -------------------------------------------------------------------------------- 1 | ssh -o StrictHostKeyChecking=no -i %GIT_CLI_SSH_PRIVATE_KEY% %* -------------------------------------------------------------------------------- /services/editor/src/styles/core/accents.less: -------------------------------------------------------------------------------- 1 | @import (reference) './accents/lightBlue.less'; 2 | -------------------------------------------------------------------------------- /services/editor/src/utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from './hooks'; 2 | export * from './tweekClients'; 3 | -------------------------------------------------------------------------------- /docs/assets/architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Soluto/tweek/HEAD/docs/assets/architecture.png -------------------------------------------------------------------------------- /docs/assets/inverted-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Soluto/tweek/HEAD/docs/assets/inverted-logo.png -------------------------------------------------------------------------------- /services/authoring/ssh-helper: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ssh -o StrictHostKeyChecking=no -i $GIT_CLI_SSH_PRIVATE_KEY "$@" -------------------------------------------------------------------------------- /docs/_layouts/home.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | --- 4 |
5 | {{ content }} 6 |
7 | -------------------------------------------------------------------------------- /services/authoring/ssh-helper.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ssh -o StrictHostKeyChecking=no -i $GIT_CLI_SSH_PRIVATE_KEY "$@" -------------------------------------------------------------------------------- /services/editor/public/tweek.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Soluto/tweek/HEAD/services/editor/public/tweek.png -------------------------------------------------------------------------------- /e2e/ui/.dockerignore: -------------------------------------------------------------------------------- 1 | .git/ 2 | node_modules/ 3 | screenshots/ 4 | 5 | .gitignore 6 | .dockerignore 7 | Dockerfile 8 | -------------------------------------------------------------------------------- /services/editor/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Soluto/tweek/HEAD/services/editor/public/favicon.ico -------------------------------------------------------------------------------- /docs/assets/logo-with-background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Soluto/tweek/HEAD/docs/assets/logo-with-background.png -------------------------------------------------------------------------------- /services/authoring/src/utils/author.ts: -------------------------------------------------------------------------------- 1 | export default interface Author { 2 | name: string; 3 | email: string; 4 | } 5 | -------------------------------------------------------------------------------- /deployments/dev/ssh/tweekgit_public.pfx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Soluto/tweek/HEAD/deployments/dev/ssh/tweekgit_public.pfx -------------------------------------------------------------------------------- /docs/_layouts/openapi.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | --- 4 |
5 | {{ content }} 6 |
7 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 100, 3 | "singleQuote": true, 4 | "trailingComma": "all", 5 | "arrowParens": "always" 6 | } 7 | -------------------------------------------------------------------------------- /services/api/Tweek.ApiService/runtimeconfig.template.json: -------------------------------------------------------------------------------- 1 | { 2 | "configProperties": { 3 | "System.GC.Server": true 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /docs/pages/2.concepts/03.jpad/01.intro.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: page 3 | title: Intro 4 | permalink: /concepts/jpad/intro 5 | --- 6 | 7 | TBD 8 | -------------------------------------------------------------------------------- /docs/pages/2.concepts/03.jpad/02.rules.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: page 3 | title: Rules 4 | permalink: /concepts/jpad/rules 5 | --- 6 | 7 | TBD 8 | -------------------------------------------------------------------------------- /docs/docker-compose.yml: -------------------------------------------------------------------------------- 1 | tweek-docs: 2 | image: starefossen/github-pages 3 | volumes: 4 | - ./:/usr/src/app 5 | ports: 6 | - 5000:4000 -------------------------------------------------------------------------------- /lint-staged.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | '*.{js,jsx,ts,tsx,json,css,less,md}': 'prettier --write', 3 | '*.go': 'yarn format:go', 4 | }; 5 | -------------------------------------------------------------------------------- /services/api/Tweek.ApiService/app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /services/editor/src/components/NoMatch.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const NoMatch = () =>
404
; 4 | 5 | export default NoMatch; 6 | -------------------------------------------------------------------------------- /services/publishing/scripts/ssh-helper.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | exec ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /tmp/ssh_server "$@" 3 | -------------------------------------------------------------------------------- /docs/pages/5.deployment/04.configuration/02.gateway.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: page 3 | title: Gateway 4 | permalink: /deployment/configuration/gateway 5 | --- 6 | -------------------------------------------------------------------------------- /e2e/integration/spec/authoring-api/test-data/bulk1.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Soluto/tweek/HEAD/e2e/integration/spec/authoring-api/test-data/bulk1.zip -------------------------------------------------------------------------------- /services/authoring/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 120, 3 | "singleQuote": true, 4 | "trailingComma": "all", 5 | "arrowParens": "always" 6 | } 7 | -------------------------------------------------------------------------------- /services/editor/src/components/common/Input/Input.less: -------------------------------------------------------------------------------- 1 | @import (reference) '../../../styles/core/core.less'; 2 | 3 | .text-input { 4 | .metro-textbox; 5 | } 6 | -------------------------------------------------------------------------------- /docs/pages/2.concepts/01.keys/02.dependent-keys.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: page 3 | title: Dependent Keys 4 | permalink: /concepts/keys/dependent-keys 5 | --- 6 | 7 | TBD 8 | -------------------------------------------------------------------------------- /services/authoring/src/utils/jsonValue.ts: -------------------------------------------------------------------------------- 1 | // types below are necessary for typescript-rest-swagger 2 | export type JsonValue = { 3 | [key: string]: any; 4 | }; 5 | -------------------------------------------------------------------------------- /e2e/integration/spec/authoring-api/test-data/invalidRules.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Soluto/tweek/HEAD/e2e/integration/spec/authoring-api/test-data/invalidRules.zip -------------------------------------------------------------------------------- /services/editor/src/styles/core/fonts/resources/tweekIcons.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Soluto/tweek/HEAD/services/editor/src/styles/core/fonts/resources/tweekIcons.eot -------------------------------------------------------------------------------- /services/editor/src/styles/core/fonts/resources/tweekIcons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Soluto/tweek/HEAD/services/editor/src/styles/core/fonts/resources/tweekIcons.ttf -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | **/* 2 | !addons/ 3 | !core/ 4 | !services/api 5 | !Tweek.sln 6 | **/.vs/ 7 | **/.vscode/ 8 | **/.idea/ 9 | **/obj/ 10 | **/bin/ 11 | **/node_modules/ 12 | -------------------------------------------------------------------------------- /e2e/integration/spec/authoring-api/test-data/invalidStructure.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Soluto/tweek/HEAD/e2e/integration/spec/authoring-api/test-data/invalidStructure.zip -------------------------------------------------------------------------------- /services/editor/src/styles/core/fonts/resources/tweekIcons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Soluto/tweek/HEAD/services/editor/src/styles/core/fonts/resources/tweekIcons.woff -------------------------------------------------------------------------------- /docs/pages/3.usage/02.editor/02.dependent-keys.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: page 3 | title: Dependency between keys 4 | permalink: /usage/editor/dependency-between-keys 5 | --- 6 | 7 | TBD 8 | -------------------------------------------------------------------------------- /e2e/ui/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM testcafe/testcafe:1.9.4 2 | USER root 3 | 4 | COPY package.json yarn.lock /src/ 5 | RUN cd /src && npm i --production 6 | COPY . /src 7 | 8 | USER user 9 | -------------------------------------------------------------------------------- /services/authoring/src/security/types.d.ts: -------------------------------------------------------------------------------- 1 | declare namespace Express { 2 | export interface User { 3 | isTweekService: boolean; 4 | permissions: string[]; 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/implementations/jpad/behavior_tests/revision_history.jpad: -------------------------------------------------------------------------------- 1 | { 2 | "partitions": [], 3 | "valueType": "string", 4 | "rules": [] 5 | } -------------------------------------------------------------------------------- /e2e/integration/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:16.0.0-alpine 2 | 3 | WORKDIR /opt/app 4 | COPY package.json yarn.lock ./ 5 | RUN yarn install 6 | 7 | COPY . ./ 8 | 9 | CMD ["yarn", "test"] 10 | -------------------------------------------------------------------------------- /services/editor/src/components/common/ComboBox/ComboBox.less: -------------------------------------------------------------------------------- 1 | @import (reference) '../../../styles/core/comboBox.less'; 2 | .combo-box-default-wrapper-theme-class { 3 | .combo-box-basic; 4 | } 5 | -------------------------------------------------------------------------------- /services/editor/src/contexts/SelectedKey/index.ts: -------------------------------------------------------------------------------- 1 | export * from './useKeyActions'; 2 | export * from './useLoadKey'; 3 | export * from './useSelectedKey'; 4 | export * from './useUpdateKey'; 5 | -------------------------------------------------------------------------------- /services/editor/src/utils/hooks/index.ts: -------------------------------------------------------------------------------- 1 | export * from './useDebounceValue'; 2 | export * from './useErrorNotifier'; 3 | export * from './useRemoteState'; 4 | export * from './useSearchConfig'; 5 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/implementations/jpad/behavior_tests/edit_key/text/edit_test.jpad: -------------------------------------------------------------------------------- 1 | { 2 | "partitions": [], 3 | "valueType": "string", 4 | "rules": [] 5 | } -------------------------------------------------------------------------------- /core/Engine/Tweek.Engine.Core/Rules/IRuleParser.cs: -------------------------------------------------------------------------------- 1 | namespace Tweek.Engine.Core.Rules 2 | { 3 | public interface IRuleParser 4 | { 5 | IRule Parse(string sourceCode); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /services/editor/debug.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:16.0.0-alpine 2 | ENV CI=true 3 | WORKDIR /app 4 | COPY package.json yarn.lock ./ 5 | RUN yarn 6 | COPY . /app 7 | RUN yarn test 8 | CMD [ "yarn", "start" ] 9 | -------------------------------------------------------------------------------- /services/editor/src/pages/keys/components/KeyPage/MessageKeyPage/MessageKeyPage.less: -------------------------------------------------------------------------------- 1 | @import (reference) '../../../../../styles/core/core.less'; 2 | .key-page-message { 3 | .centered-message; 4 | } 5 | -------------------------------------------------------------------------------- /services/gateway/.dockerignore: -------------------------------------------------------------------------------- 1 | Dockerfile* 2 | debug.Dockerfile 3 | docker-compose* 4 | .dockerignore 5 | .git 6 | .gitignore 7 | README.md 8 | LICENSE 9 | .vscode 10 | .idea 11 | 12 | vendor -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/implementations/jpad/behavior_tests/dependent_keys/fail/third.jpad: -------------------------------------------------------------------------------- 1 | { 2 | "partitions": [], 3 | "valueType": "string", 4 | "rules": [] 5 | } -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/implementations/jpad/behavior_tests/dependent_keys/pass/used_by.jpad: -------------------------------------------------------------------------------- 1 | { 2 | "partitions": [], 3 | "valueType": "string", 4 | "rules": [] 5 | } -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/implementations/jpad/behavior_tests/edit_key/visual/edit_test.jpad: -------------------------------------------------------------------------------- 1 | { 2 | "partitions": [], 3 | "valueType": "string", 4 | "rules": [] 5 | } -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/implementations/jpad/behavior_tests/partitions/empty_partition.jpad: -------------------------------------------------------------------------------- 1 | { 2 | "partitions": [], 3 | "valueType": "string", 4 | "rules": [] 5 | } -------------------------------------------------------------------------------- /core/Engine/Tweek.Engine.Drivers/Context/IContextDriver.cs: -------------------------------------------------------------------------------- 1 | namespace Tweek.Engine.Drivers.Context 2 | { 3 | public interface IContextDriver : IContextReader, IContextWriter 4 | { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /services/authoring/src/utils/hook.ts: -------------------------------------------------------------------------------- 1 | export default interface Hook { 2 | id?: string; 3 | keyPath: string; 4 | type: string; 5 | url: string; 6 | tags?: string[]; 7 | format?: string; 8 | } 9 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/implementations/jpad/behavior_tests/dependent_keys/display/used_by.jpad: -------------------------------------------------------------------------------- 1 | { 2 | "partitions": [], 3 | "valueType": "string", 4 | "rules": [] 5 | } -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/implementations/jpad/behavior_tests/dependent_keys/pass/depends_on.jpad: -------------------------------------------------------------------------------- 1 | { 2 | "partitions": [], 3 | "valueType": "string", 4 | "rules": [] 5 | } -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/implementations/jpad/behavior_tests/edit_key/visual/edit_array_test.jpad: -------------------------------------------------------------------------------- 1 | { 2 | "partitions": [], 3 | "valueType": "array", 4 | "rules": [] 5 | } -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/implementations/jpad/behavior_tests/edit_key/visual/edit_object_test.jpad: -------------------------------------------------------------------------------- 1 | { 2 | "partitions": [], 3 | "valueType": "object", 4 | "rules": [] 5 | } -------------------------------------------------------------------------------- /services/publishing/base-repo/implementations/jpad/@tweek/auth/@global/write_context.jpad: -------------------------------------------------------------------------------- 1 | { 2 | "partitions": [], 3 | "valueType": "string", 4 | "rules": [], 5 | "defaultValue": "deny" 6 | } -------------------------------------------------------------------------------- /services/publishing/base-repo/implementations/jpad/@tweek/auth/@global/read_configuration.jpad: -------------------------------------------------------------------------------- 1 | { 2 | "partitions": [], 3 | "valueType": "string", 4 | "rules": [], 5 | "defaultValue": "deny" 6 | } -------------------------------------------------------------------------------- /services/git-service/BareRepository/source/implementations/jpad/@tweek/auth/@global/write_context.jpad: -------------------------------------------------------------------------------- 1 | { 2 | "partitions": [], 3 | "valueType": "string", 4 | "rules": [], 5 | "defaultValue": "deny" 6 | } -------------------------------------------------------------------------------- /services/git-service/BareRepository/source/implementations/jpad/@tweek/auth/@global/read_configuration.jpad: -------------------------------------------------------------------------------- 1 | { 2 | "partitions": [], 3 | "valueType": "string", 4 | "rules": [], 5 | "defaultValue": "deny" 6 | } -------------------------------------------------------------------------------- /services/authoring/.eslintignore: -------------------------------------------------------------------------------- 1 | # don't ever lint node_modules 2 | node_modules 3 | # don't lint build output (make sure it's set to your correct build folder name) 4 | dist 5 | # don't lint nyc coverage output 6 | coverage 7 | -------------------------------------------------------------------------------- /services/authoring/src/utils/logger.ts: -------------------------------------------------------------------------------- 1 | import { createLogger, stdSerializers } from 'bunyan'; 2 | 3 | export default createLogger({ 4 | name: 'tweek-authoring', 5 | serializers: stdSerializers, 6 | level: 0, 7 | }); 8 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/implementations/jpad/@tweek/auth/@global/write_context.jpad: -------------------------------------------------------------------------------- 1 | { 2 | "partitions": [], 3 | "valueType": "string", 4 | "rules": [], 5 | "defaultValue": "allow" 6 | } -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/implementations/jpad/@tweek/editor/google_tag_manager/id.jpad: -------------------------------------------------------------------------------- 1 | { 2 | "partitions": [], 3 | "valueType": "string", 4 | "rules": [], 5 | "defaultValue": "" 6 | } 7 | -------------------------------------------------------------------------------- /deployments/dev/ssh/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:3.4 2 | COPY tweekgit /gitkeys/tweekgit 3 | COPY tweekgit.pub /gitkeys/tweekgit.pub 4 | COPY tweekgit_public.pfx /gitkeys/tweekgit_public.pfx 5 | VOLUME /gitkeys 6 | 7 | CMD ["/bin/true"] -------------------------------------------------------------------------------- /services/editor/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ "env", "react", "stage-0" ], 3 | "plugins": [ 4 | ["transform-runtime", { 5 | "polyfill": false, 6 | "regenerator": true 7 | }] 8 | ] 9 | } 10 | 11 | -------------------------------------------------------------------------------- /services/editor/src/index.js: -------------------------------------------------------------------------------- 1 | import 'papp-polyfill'; 2 | import React from 'react'; 3 | import ReactDOM from 'react-dom'; 4 | import Routes from './Routes'; 5 | 6 | ReactDOM.render(, document.getElementById('root')); 7 | -------------------------------------------------------------------------------- /services/gateway/utils/arrayUtils.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | func ContainsString(sl []string, v string) bool { 4 | for _, vv := range sl { 5 | if vv == v { 6 | return true 7 | } 8 | } 9 | return false 10 | } 11 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/implementations/jpad/@tweek/auth/@global/read_configuration.jpad: -------------------------------------------------------------------------------- 1 | { 2 | "partitions": [], 3 | "valueType": "string", 4 | "rules": [], 5 | "defaultValue": "allow" 6 | } -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/implementations/jpad/@tweek/editor/google_tag_manager/enabled.jpad: -------------------------------------------------------------------------------- 1 | { 2 | "partitions": [], 3 | "valueType": "boolean", 4 | "rules": [], 5 | "defaultValue": false 6 | } 7 | -------------------------------------------------------------------------------- /services/authoring/.dockerignore: -------------------------------------------------------------------------------- 1 | .git/ 2 | .idea/ 3 | .typings/ 4 | .vscode/ 5 | 6 | coverage-unit/ 7 | dist/ 8 | node_modules/ 9 | rulesRepository/ 10 | 11 | .dockerignore 12 | Dockerfile 13 | npm-debug.log 14 | yarn-error.log 15 | -------------------------------------------------------------------------------- /services/editor/src/contexts/SelectedKey/useSelectedKey.ts: -------------------------------------------------------------------------------- 1 | import { createUseContext } from '../utils'; 2 | import { SelectedKeyContext } from './SelectedKey'; 3 | 4 | export const createUseSelectedKey = createUseContext(SelectedKeyContext); 5 | -------------------------------------------------------------------------------- /core/Engine/Tweek.Engine.Drivers/Rules/IRulesetVersionProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Tweek.Engine.Drivers.Rules 4 | { 5 | public interface IRulesetVersionProvider 6 | { 7 | IObservable OnVersion(); 8 | } 9 | } -------------------------------------------------------------------------------- /docs/pages/play-with-tweek.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: play-with-tweek 3 | title: Play With Tweek 4 | permalink: /play-with-tweek 5 | hide: true 6 | --- 7 | 8 | 8 | -------------------------------------------------------------------------------- /services/editor/src/components/common/Label/Label.less: -------------------------------------------------------------------------------- 1 | @import (reference) '../../../styles/core/core.less'; 2 | 3 | *[data-comp='label'] { 4 | .metro-textbox; 5 | border-bottom-color: #bcbcbc; 6 | line-height: 28px; 7 | margin-right: 8px; 8 | } 9 | -------------------------------------------------------------------------------- /services/editor/src/pages/keys/components/KeyPage/KeyPage.less: -------------------------------------------------------------------------------- 1 | .key-page-wrapper { 2 | position: relative; 3 | display: flex; 4 | top: 0; 5 | left: 0; 6 | bottom: 0; 7 | right: 0; 8 | flex-grow: 1; 9 | flex-shrink: 0; 10 | } 11 | -------------------------------------------------------------------------------- /services/editor/.gitignore: -------------------------------------------------------------------------------- 1 | # dependencies 2 | /node_modules 3 | 4 | # testing 5 | /coverage-unit 6 | 7 | # production 8 | /build 9 | /dist 10 | /rulesRepository 11 | *.bundle.js 12 | *.bundle.js.map 13 | searchIndex.json 14 | *.css 15 | *.css.map 16 | -------------------------------------------------------------------------------- /services/editor/src/components/JPadFullEditor/JPadTextEditor/JPadTextEditor.less: -------------------------------------------------------------------------------- 1 | @import (reference) '../../../styles/core/core.less'; 2 | 3 | .save-code-changes-button { 4 | .metro-button; 5 | position: absolute; 6 | bottom: 10px; 7 | right: 20px; 8 | } 9 | -------------------------------------------------------------------------------- /docs/_layouts/play-with-tweek.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | --- 4 | {{ content }} 5 | 6 | -------------------------------------------------------------------------------- /services/authoring/src/utils/response-utils.ts: -------------------------------------------------------------------------------- 1 | import { Oid } from 'nodegit/oid'; 2 | import { Response } from 'express'; 3 | 4 | export function addOid(response: Response, oid: Oid) { 5 | if (oid) { 6 | response.setHeader('x-oid', oid.tostrS()); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /services/editor/src/components/common/Label/Label.tsx: -------------------------------------------------------------------------------- 1 | import React, { FunctionComponent } from 'react'; 2 | import './Label.css'; 3 | 4 | const Label: FunctionComponent = ({ children }) => ; 5 | 6 | export default Label; 7 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/implementations/jpad/behavior_tests/partitions/add_partition_group.jpad: -------------------------------------------------------------------------------- 1 | { 2 | "partitions": [ 3 | "user.FavoriteFruit", 4 | "user.FatherName" 5 | ], 6 | "valueType": "string", 7 | "rules": {} 8 | } -------------------------------------------------------------------------------- /services/api/Tweek.ApiService/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Serilog": { 3 | "MinimumLevel": "Debug", 4 | "Enrich": ["FromLogContext", "WithMachineName", "WithThreadId"], 5 | "Properties": { 6 | "Application": "TweekApi" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /services/publishing/Tweek.Publishing.Service/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Serilog": { 3 | "MinimumLevel": "Warning", 4 | "Enrich": ["FromLogContext", "WithMachineName", "WithThreadId"], 5 | "Properties": { 6 | "Application": "Publishing" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /e2e/ui/utils/selector-utils.js: -------------------------------------------------------------------------------- 1 | import R from 'ramda'; 2 | 3 | export const attributeSelector = R.curryN(2, (attribute, name) => `[${attribute}= "${name}"]`); 4 | 5 | export const dataComp = attributeSelector('data-comp'); 6 | 7 | export const dataField = attributeSelector('data-field'); 8 | -------------------------------------------------------------------------------- /services/editor/src/pages/settings/components/HooksPage/HookTypes.js: -------------------------------------------------------------------------------- 1 | export const hookTypes = [ 2 | { label: 'Webhook', value: 'notification_webhook' }, 3 | ]; 4 | 5 | export const hookLabelsByType = hookTypes.reduce((labelsByType, {value, label}) => ({ 6 | ...labelsByType, 7 | [value]: label 8 | }), {}); 9 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/manifests/integration_tests/alias_key.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "integration_tests/alias_key", 3 | "meta": { 4 | "archived": false 5 | }, 6 | "implementation": { 7 | "type": "alias", 8 | "key": "integration_tests/some_key" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /core/Engine/Tweek.Engine.Core/Rules/IRule.cs: -------------------------------------------------------------------------------- 1 | using LanguageExt; 2 | using Tweek.Engine.Core.Context; 3 | using Tweek.Engine.DataTypes; 4 | 5 | namespace Tweek.Engine.Core.Rules 6 | { 7 | public interface IRule 8 | { 9 | Option GetValue(GetContextValue fullContext); 10 | } 11 | } -------------------------------------------------------------------------------- /core/Engine/Tweek.Engine/Collections/LeafNode.cs: -------------------------------------------------------------------------------- 1 | namespace Tweek.Engine.Collections 2 | { 3 | /// 4 | /// Represents a value 5 | /// 6 | internal class LeafNode 7 | { 8 | internal string Key = string.Empty; 9 | internal TValue Value; 10 | } 11 | } -------------------------------------------------------------------------------- /services/editor/.dockerignore: -------------------------------------------------------------------------------- 1 | .git/ 2 | .idea/ 3 | .typings/ 4 | .vscode/ 5 | build/ 6 | coverage-unit/ 7 | dist/ 8 | node_modules/ 9 | obj/ 10 | rulesRepository/ 11 | 12 | .dockerignore 13 | .editorconfig 14 | .gitignore 15 | Dockerfile 16 | npm-debug.log 17 | searchIndex.json 18 | yarn-error.log 19 | *.css 20 | -------------------------------------------------------------------------------- /services/publishing/Tweek.Publishing.Service/Validation/IValidator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | 4 | namespace Tweek.Publishing.Service.Validation 5 | { 6 | public interface IValidator 7 | { 8 | Task Validate(string fileName, Func> reader); 9 | } 10 | } -------------------------------------------------------------------------------- /docs/_includes/header.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /services/gateway/handlers/healthHandler.go: -------------------------------------------------------------------------------- 1 | package handlers 2 | 3 | import "net/http" 4 | 5 | // NewHealthHandler - checks healths of all services and returns results 6 | func NewHealthHandler() http.HandlerFunc { 7 | return func(w http.ResponseWriter, r *http.Request) { 8 | w.Write([]byte("true")) 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/implementations/jpad/behavior_tests/read_only.jpad: -------------------------------------------------------------------------------- 1 | { 2 | "partitions": [], 3 | "valueType": "string", 4 | "rules": [ 5 | { 6 | "Matcher": {}, 7 | "Value": "", 8 | "Type": "SingleVariant" 9 | } 10 | ] 11 | } -------------------------------------------------------------------------------- /services/publishing/base-repo/security/subject_extraction_rules.rego: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | default subject = { "user": null, "group": null } 4 | 5 | subject = { "user": input.email, "group": "default" } { 6 | count(input.email) != 0 7 | } else = { "user": input.sub, "group": "default" } { 8 | count(input.sub) != 0 9 | } -------------------------------------------------------------------------------- /.github/workflows/push-tag.sh: -------------------------------------------------------------------------------- 1 | set -e 2 | git config --local user.email "$(git log --format='%ae' HEAD^!)" 3 | git config --local user.name "$(git log --format='%an' HEAD^!)" 4 | docker tag soluto/tweek-$1 soluto/tweek-$1:$2 5 | docker push soluto/tweek-$1:$2 6 | git tag -m "Version created: $1-$2" tweek-$1-$2 7 | git push --follow-tags 8 | -------------------------------------------------------------------------------- /core/Engine/Tweek.Engine.DataTypes/Tweek.Engine.DataTypes.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | netstandard2.1 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/manifests/behavior_tests/delete_key/delete_alias.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "behavior_tests/delete_key/delete_alias", 3 | "meta": { 4 | "archived": false 5 | }, 6 | "implementation": { 7 | "type": "alias", 8 | "key": "behavior_tests/delete_key/delete" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/manifests/behavior_tests/key_aliases/alias_key.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "behavior_tests/key_aliases/alias_key", 3 | "meta": { 4 | "archived": false 5 | }, 6 | "implementation": { 7 | "type": "alias", 8 | "key": "behavior_tests/key_aliases/regular_key" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/manifests/behavior_tests/key_aliases/delete_alias.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "behavior_tests/key_aliases/delete_alias", 3 | "meta": { 4 | "archived": false 5 | }, 6 | "implementation": { 7 | "type": "alias", 8 | "key": "behavior_tests/key_aliases/regular_key" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /e2e/ui/pages/Keys/TypedInput/index.js: -------------------------------------------------------------------------------- 1 | import TagInput from './TagInput'; 2 | import ObjectInput from './ObjectInput'; 3 | 4 | export default class TypedInput { 5 | constructor(container) { 6 | this.input = container; 7 | this.tagInput = new TagInput(container); 8 | this.objectInput = new ObjectInput(container); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /services/editor/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Tweek", 3 | "icons": [ 4 | { 5 | "src": "favicon.ico", 6 | "sizes": "192x192", 7 | "type": "image/png" 8 | } 9 | ], 10 | "start_url": "./index.html", 11 | "display": "standalone", 12 | "theme_color": "#000000", 13 | "background_color": "#ffffff" 14 | } 15 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/manifests/behavior_tests/key_aliases/alias_to_alias.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "behavior_tests/key_aliases/alias_to_alias", 3 | "meta": { 4 | "archived": false 5 | }, 6 | "implementation": { 7 | "type": "alias", 8 | "key": "behavior_tests/key_aliases/alias_key" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /services/editor/src/components/JPadFullEditor/JPadVisualEditor/Rule/DefaultValue.less: -------------------------------------------------------------------------------- 1 | @import (reference) '../../../../styles/core/core.less'; 2 | 3 | .default-value-container { 4 | display: flex; 5 | .transitioned(); 6 | 7 | .default-value-label { 8 | font-size: 13px; 9 | margin: 5px 5px 0 0; 10 | color: gray; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /services/editor/src/components/common/Input/DateInput.less: -------------------------------------------------------------------------------- 1 | @import (reference) '../../../styles/core/core.less'; 2 | 3 | .date-input > .calendar { 4 | .wrapper { 5 | position: relative; 6 | } 7 | .container { 8 | border-radius: 5px; 9 | background-color: white; 10 | z-index: 3; 11 | position: absolute; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /services/editor/src/pages/context/components/FixedKeys/FixedKeys.less: -------------------------------------------------------------------------------- 1 | @import (reference) '../../../../styles/core/core.less'; 2 | 3 | .fixed-keys-container { 4 | .override-keys-title { 5 | font-size: 22px; 6 | color: #a5a5a5; 7 | margin-bottom: 20px; 8 | display: flex; 9 | justify-content: space-between; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /services/editor/src/styles/core/accents/lightBlue.less: -------------------------------------------------------------------------------- 1 | @accent-color1: #007acc; 2 | @accent-color2: rgba(0, 122, 204, 0.8); 3 | @accent-color3: rgba(0, 122, 204, 0.6); 4 | @accent-color4: rgba(0, 122, 204, 0.5); 5 | @accent-color5: rgba(0, 122, 204, 0.4); 6 | @accent-color6: rgba(0, 122, 204, 0.2); 7 | @accent-active: #0098ff; 8 | @accent-foreground: white; 9 | -------------------------------------------------------------------------------- /docs/pages/3.usage/02.editor/01.create-new-key.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: page 3 | title: Create new key 4 | permalink: /usage/editor/create-new-key 5 | --- 6 | 7 | 8 | -------------------------------------------------------------------------------- /docs/pages/3.usage/02.editor/04.override-context.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: page 3 | title: Override keys 4 | permalink: /usage/editor/override-keys 5 | --- 6 | 7 | 8 | -------------------------------------------------------------------------------- /services/publishing/Tweek.Publishing.Service/Model/VersionsBlob.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace Tweek.Publishing.Service.Model 4 | { 5 | public class VersionsBlob 6 | { 7 | [JsonProperty("latest")] 8 | public string Latest; 9 | 10 | [JsonProperty("previous")] 11 | public string Previous; 12 | } 13 | } -------------------------------------------------------------------------------- /services/publishing/hooks/pre-receive: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | while read oldrev newrev refname 3 | do 4 | curl -i -f "http://localhost/validate?oldrev=$oldrev&newRev=$newrev&quarantinepath=$GIT_QUARANTINE_PATH" 5 | exitCode=$? 6 | if [ $exitCode != 0 ]; then 7 | exit $exitCode 8 | fi 9 | done 10 | curl http://localhost/log?message=completed-pre-receive-hook -------------------------------------------------------------------------------- /services/editor/src/pages/settings/components/HooksPage/WebHookFormats.js: -------------------------------------------------------------------------------- 1 | export const webhookFormats = [ 2 | { label: 'JSON', value: 'json' }, 3 | { label: 'Slack', value: 'slack' }, 4 | ]; 5 | 6 | export const webhookLabelsByFormat = webhookFormats.reduce((labelsByFormat, { value, label }) => ({ 7 | ...labelsByFormat, 8 | [value]: label, 9 | }), {}); 10 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/manifests/behavior_tests/dependent_keys/display/alias_key.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "behavior_tests/dependent_keys/display/alias_key", 3 | "meta": { 4 | "archived": false 5 | }, 6 | "implementation": { 7 | "type": "alias", 8 | "key": "behavior_tests/dependent_keys/display/used_by" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /e2e/integration/spec/authoring-api/test-data/subject_extraction_rules.rego: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | default subject = { "user": null, "group": null } 4 | 5 | subject = { "user": "admin-app", "group": "externalapps"} { 6 | input.aud = "tweek-openid-mock-client" 7 | input.sub = "user" 8 | } else = { "user": input.sub, "group": "default" } { 9 | true 10 | } 11 | -------------------------------------------------------------------------------- /docs/pages/3.usage/02.editor/03.define-identity-schema.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: page 3 | title: Define identity schema 4 | permalink: /usage/editor/define-identity-schema 5 | --- 6 | 7 | 8 | -------------------------------------------------------------------------------- /services/editor/src/pages/keys/components/KeysList/TreeView/utils.ts: -------------------------------------------------------------------------------- 1 | import { KeyManifest } from 'tweek-client'; 2 | 3 | export const getDataValueType = (item: KeyManifest) => { 4 | if (item.meta.archived) { 5 | return 'archived'; 6 | } else if (item.implementation.type === 'alias') { 7 | return 'alias'; 8 | } 9 | return item.valueType || 'key'; 10 | }; 11 | -------------------------------------------------------------------------------- /services/api/Tweek.ApiService/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.1.0", 3 | "command": "dotnet", 4 | "isShellCommand": true, 5 | "args": [], 6 | "tasks": [ 7 | { 8 | "taskName": "build", 9 | "args": ["${workspaceRoot}\\Tweek.ApiService.csproj"], 10 | "isBuildCommand": true, 11 | "problemMatcher": "$msCompile" 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /e2e/ui/pages/Keys/ConstValue.js: -------------------------------------------------------------------------------- 1 | import { Selector } from 'testcafe'; 2 | import { dataComp } from '../../utils/selector-utils'; 3 | import ObjectInput from './TypedInput/ObjectInput'; 4 | 5 | export default class ConstValue { 6 | container = Selector(dataComp('const-editor')); 7 | input = this.container.find('input'); 8 | objectInput = new ObjectInput(this.container); 9 | } 10 | -------------------------------------------------------------------------------- /services/editor/src/components/JPadFullEditor/JPadVisualEditor/Matcher/Properties/styles.less: -------------------------------------------------------------------------------- 1 | @import (reference) '../../../../../styles/core/comboBox.less'; 2 | 3 | .matcher-operator { 4 | margin: 0 5px; 5 | display: inline-block; 6 | @width: 80px; 7 | .combo-box-basic(@width, @width); 8 | 9 | .bootstrap-typeahead-input { 10 | text-align: center; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /services/editor/src/components/common/SaveButton/SaveButton.less: -------------------------------------------------------------------------------- 1 | @import (reference) '../../../styles/core/core.less'; 2 | 3 | @key-action-button-width: 125px; 4 | 5 | .save-button { 6 | .metro-button; 7 | width: @key-action-button-width; 8 | &[data-state-is-saving='true'], 9 | &[data-state-is-saving='true']:disabled { 10 | background-color: green; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/security/subject_extraction_rules.rego: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | default subject = { "user": null, "group": null } 4 | 5 | subject = { "user": "admin-app", "group": "externalapps"} { 6 | input.aud = "tweek-openid-mock-client" 7 | input.sub = "user" 8 | } else = { "user": input.sub, "group": "default" } { 9 | true 10 | } 11 | -------------------------------------------------------------------------------- /.vscode/cSpell.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.1", 3 | 4 | "language": "en", 5 | 6 | "words": [ 7 | "Couchbase", 8 | "jpad", 9 | "Minio", 10 | "Nats", 11 | "repo", 12 | "sbin", 13 | "soluto", 14 | "sshd", 15 | "Tweek", 16 | "guid", 17 | "codegen", 18 | "deserialize", 19 | "multiplart" 20 | ], 21 | 22 | "flagWords": ["hte"] 23 | } 24 | -------------------------------------------------------------------------------- /core/Utils/JsonValueConverter/JsonValueConverter.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | netstandard2.1 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /services/api/Tweek.ApiService.SmokeTests/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Abort script on error 4 | 5 | wget -O /dev/null --tries=20 --timeout=15 --read-timeout=20 --waitretry=30 --retry-connrefused http://gateway/status 6 | set -e 7 | sleep 10 8 | wget -O /dev/null --tries=20 --timeout=15 --read-timeout=20 --waitretry=30 --retry-connrefused http://api/health 9 | dotnet test --no-build 10 | -------------------------------------------------------------------------------- /services/editor/src/contexts/TypesService.ts: -------------------------------------------------------------------------------- 1 | import { createContext, useContext } from 'react'; 2 | import { isAllowedValue, safeConvertValue, types } from '../services/types-service'; 3 | 4 | export const TypesServiceContext = createContext({ 5 | safeConvertValue, 6 | types, 7 | isAllowedValue, 8 | }); 9 | 10 | export const useTypesService = () => useContext(TypesServiceContext); 11 | -------------------------------------------------------------------------------- /services/editor/src/styles/core/fonts/fonts.less: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'tweekIcons'; 3 | src: url('./resources/tweekIcons.eot'), 4 | url('./resources/tweekIcons.eot?#iefix') format('embedded-opentype'), 5 | url('./resources/tweekIcons.woff') format('woff'), 6 | url('./resources/tweekIcons.ttf') format('truetype'), 7 | url('./resources/tweekIcons.svg') format('svg'); 8 | } 9 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/manifests/smoke_tests/key_path/key1.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "smoke_tests/key_path/key1", 3 | "meta": { 4 | "description": "", 5 | "readOnly": false, 6 | "archived": false 7 | }, 8 | "implementation": { 9 | "type": "const", 10 | "value": "test" 11 | }, 12 | "valueType": "string", 13 | "dependencies": [] 14 | } 15 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/manifests/smoke_tests/key_path/key2.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "smoke_tests/key_path/key2", 3 | "meta": { 4 | "description": "", 5 | "readOnly": false, 6 | "archived": false 7 | }, 8 | "implementation": { 9 | "type": "const", 10 | "value": "test" 11 | }, 12 | "valueType": "string", 13 | "dependencies": [] 14 | } 15 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/manifests/smoke_tests/key_path_.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "smoke_tests/key_path_", 3 | "meta": { 4 | "description": "", 5 | "readOnly": false, 6 | "archived": false 7 | }, 8 | "implementation": { 9 | "type": "const", 10 | "value": "some value" 11 | }, 12 | "valueType": "string", 13 | "dependencies": [] 14 | } 15 | -------------------------------------------------------------------------------- /core/Engine/Tweek.Engine.Drivers/Context/IContextReader.cs: -------------------------------------------------------------------------------- 1 | using FSharpUtils.Newtonsoft; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | using Tweek.Engine.DataTypes; 5 | 6 | namespace Tweek.Engine.Drivers.Context 7 | { 8 | public interface IContextReader 9 | { 10 | Task> GetContext(Identity identity); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /e2e/ui/utils/assertion-utils.js: -------------------------------------------------------------------------------- 1 | export const waitFor = async (fn, timeout = 10000, delay = 100) => { 2 | const start = Date.now(); 3 | while (true) { 4 | try { 5 | await fn(); 6 | return; 7 | } catch (e) { 8 | if (Date.now() - start >= timeout) { 9 | throw e; 10 | } 11 | await new Promise((r) => setTimeout(r, delay)); 12 | } 13 | } 14 | }; 15 | -------------------------------------------------------------------------------- /services/editor/src/components/AppPage.tsx: -------------------------------------------------------------------------------- 1 | import React, { FunctionComponent } from 'react'; 2 | 3 | import Alerts from './alerts/Alerts'; 4 | import '../styles/core/fonts/fonts.css'; 5 | import './App.css'; 6 | 7 | const AppPage: FunctionComponent = ({ children }) => ( 8 |
9 | {children} 10 | 11 |
12 | ); 13 | 14 | export default AppPage; 15 | -------------------------------------------------------------------------------- /e2e/ui/clients/api-client.js: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | import { waitFor } from '../utils/assertion-utils'; 3 | import { tweekClient } from './tweek-clients'; 4 | 5 | export const waitForValueToEqual = async (keyPath, expected) => { 6 | await waitFor(async () => { 7 | const result = await tweekClient.getValues(keyPath); 8 | expect(result).to.deep.equal(expected); 9 | }); 10 | }; 11 | -------------------------------------------------------------------------------- /services/gateway/security/testdata/subject_extraction_rules.rego: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | default subject = { "user": null, "group": null } 4 | 5 | subject = { "user": input.sub, "group": "google" } { 6 | input.iss = "https://accounts.google.com" 7 | } else = { "user": input.sub, "group": "azure" } { 8 | input.iss = "https://login.microsoftonline.com/11111111-1111-1111-1111-111111111111" 9 | } 10 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/manifests/smoke_tests/rule_based_keys/in.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "smoke_tests/rule_based_keys/in", 3 | "meta": { 4 | "description": "", 5 | "readOnly": false, 6 | "archived": false 7 | }, 8 | "implementation": { 9 | "type": "file", 10 | "format": "jpad" 11 | }, 12 | "valueType": "string", 13 | "dependencies": [] 14 | } 15 | -------------------------------------------------------------------------------- /services/editor/src/components/JPadFullEditor/JPadVisualEditor/Partition/PartitionsSelector.less: -------------------------------------------------------------------------------- 1 | @import (reference) '../../../../styles/core/tags.less'; 2 | 3 | .partitions-selector-container { 4 | display: flex; 5 | 6 | .partitions-label { 7 | font-size: 13px; 8 | margin: 5px 5px 0 0; 9 | color: gray; 10 | } 11 | 12 | .partition-tags-wrapper { 13 | .metro-tags; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /services/gateway/Makefile: -------------------------------------------------------------------------------- 1 | build: 2 | CGO_ENABLED=0 go build 3 | 4 | start: 5 | CONFIGOR_ENV=local TWEEKGATEWAY_CONFIGFILEPATH=../../deployments/dev/gateway/config/gateway.json ./tweek-gateway 6 | 7 | test: 8 | go test ./... 9 | 10 | start-full-env: 11 | docker-compose -f ../../deployments/dev/docker-compose.yml -f ../../deployments/dev/docker-compose.override.yml up -d api authoring && make start 12 | -------------------------------------------------------------------------------- /e2e/ui/utils/constants.js: -------------------------------------------------------------------------------- 1 | import nconf from 'nconf'; 2 | 3 | nconf 4 | .argv() 5 | .env() 6 | .defaults({ 7 | GATEWAY_URL: 'http://localhost:8081', 8 | EDITOR_URL: 'http://localhost:8081/', 9 | }); 10 | 11 | const trimEnd = (str) => str.replace(/\/+$/, ''); 12 | 13 | export const gatewayUrl = trimEnd(nconf.get('GATEWAY_URL')); 14 | export const editorUrl = trimEnd(nconf.get('EDITOR_URL')); 15 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/manifests/behavior_tests/tags.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "behavior_tests/tags", 3 | "meta": { 4 | "name": "behavior_tests/tags", 5 | "tags": [], 6 | "description": "", 7 | "archived": false 8 | }, 9 | "implementation": { 10 | "type": "const", 11 | "value": "value" 12 | }, 13 | "valueType": "string", 14 | "dependencies": [] 15 | } 16 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/manifests/smoke_tests/identity_context/color.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "smoke_tests/identity_context/color", 3 | "meta": { 4 | "description": "", 5 | "readOnly": false, 6 | "archived": false 7 | }, 8 | "implementation": { 9 | "type": "file", 10 | "format": "jpad" 11 | }, 12 | "valueType": "string", 13 | "dependencies": [] 14 | } 15 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/manifests/smoke_tests/key_path_suffix/key.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "smoke_tests/key_path_suffix/key", 3 | "meta": { 4 | "description": "", 5 | "readOnly": false, 6 | "archived": false 7 | }, 8 | "implementation": { 9 | "type": "const", 10 | "value": "some value" 11 | }, 12 | "valueType": "string", 13 | "dependencies": [] 14 | } 15 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/manifests/smoke_tests/not_hidden/some_key.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "smoke_tests/not_hidden/some_key", 3 | "meta": { 4 | "description": "", 5 | "readOnly": false, 6 | "archived": false 7 | }, 8 | "implementation": { 9 | "type": "const", 10 | "value": "some value" 11 | }, 12 | "valueType": "string", 13 | "dependencies": [] 14 | } 15 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/manifests/smoke_tests/rule_based_keys/simple.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "smoke_tests/rule_based_keys/simple", 3 | "meta": { 4 | "description": "", 5 | "readOnly": false, 6 | "archived": false 7 | }, 8 | "implementation": { 9 | "type": "file", 10 | "format": "jpad" 11 | }, 12 | "valueType": "string", 13 | "dependencies": [] 14 | } 15 | -------------------------------------------------------------------------------- /services/publishing/Tweek.Publishing.Service/Validation/CircularValidationException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Tweek.Publishing.Service.Validation 4 | { 5 | public class CircularValidationException : Exception 6 | { 7 | public CircularValidationException(string path) : base("circular dependencies detected") 8 | { 9 | Data["Path"] = path; 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /deployments/dev/ssh/tweekgit.pub: -------------------------------------------------------------------------------- 1 | ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDEwi/TACgw0eyxHP0eiOzrIJ6SX0Ov0Qq5nnSC3/HLxk4LNAl6deiehLGtT4TqQow9mpbwg9SHsaH8kebUVazV5PBrdnX5QQWSLdD14MUg9M7fmdqpXSlStjbAotNafezu92FwBI5e+6OzKq+iUI+QSqsBS4hBaDnAbQkO6nGf/S1Eiio44fXqJ/FlivsMLj8Em+AfqO2S7mflcwvwCK6DCnCVJJIby+5669mafBR0mCo7cNQcu5KkWlC7YnoXK/x1RphPVW2pf2HEuIeYYREqEv7mQ8bh3XcGh3lNdV2jrHF8I9qGFSfOV0x6wDRv9VpDwCJCGSgJxCJs5qtTvTdF yshay@Yshay-PC 2 | -------------------------------------------------------------------------------- /services/editor/src/components/common/EditableText/EditableText.less: -------------------------------------------------------------------------------- 1 | @import (reference) '../../../styles/core/core.less'; 2 | .editable-text-container { 3 | @text-color: #515c66; 4 | border: 1px solid transparent; 5 | .editable-text-input { 6 | color: @text-color; 7 | box-sizing: border-box; 8 | } 9 | .editable-text-value { 10 | color: @text-color; 11 | margin-left: 4px; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/implementations/jpad/smoke_tests/value_distribution/bernoulli.jpad: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "Id": "4fe86e17-83d9-52e2-97fe-6e03ef037c8c", 4 | "Matcher": {}, 5 | "Type": "MultiVariant", 6 | "OwnerType": "device", 7 | "ValueDistribution": { 8 | "type": "bernoulliTrial", 9 | "args": 0.3 10 | } 11 | } 12 | ] -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/manifests/smoke_tests/ignore_key_types/number_type.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "smoke_tests/ignore_key_types/number_type", 3 | "meta": { 4 | "description": "", 5 | "readOnly": false, 6 | "archived": false 7 | }, 8 | "implementation": { 9 | "type": "const", 10 | "value": 15 11 | }, 12 | "valueType": "number", 13 | "dependencies": [] 14 | } 15 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/manifests/smoke_tests/rule_based_keys/comparison.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "smoke_tests/rule_based_keys/comparison", 3 | "meta": { 4 | "description": "", 5 | "readOnly": false, 6 | "archived": false 7 | }, 8 | "implementation": { 9 | "type": "file", 10 | "format": "jpad" 11 | }, 12 | "valueType": "string", 13 | "dependencies": [] 14 | } 15 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/source/security/subject_extraction_rules.rego: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | default subject = { "user": null, "group": null } 4 | 5 | subject = { "user": "admin-app", "group": "externalapps"} { 6 | startswith(input.iss, "http://localhost:") 7 | input.aud = "tweek-openid-mock-client" 8 | input.sub = "user" 9 | } else = { "user": input.sub, "group": "default" } { 10 | true 11 | } -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/manifests/behavior_tests/routing.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "behavior_tests/routing", 3 | "meta": { 4 | "name": "behavior_tests/routing", 5 | "tags": [], 6 | "description": "", 7 | "archived": false 8 | }, 9 | "implementation": { 10 | "type": "const", 11 | "value": "value" 12 | }, 13 | "valueType": "string", 14 | "dependencies": [] 15 | } 16 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/manifests/smoke_tests/ignore_key_types/boolean_type.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "smoke_tests/ignore_key_types/boolean_type", 3 | "meta": { 4 | "description": "", 5 | "readOnly": false, 6 | "archived": false 7 | }, 8 | "implementation": { 9 | "type": "const", 10 | "value": true 11 | }, 12 | "valueType": "boolean", 13 | "dependencies": [] 14 | } 15 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/manifests/smoke_tests/ignore_key_types/string_type.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "smoke_tests/ignore_key_types/string_type", 3 | "meta": { 4 | "description": "", 5 | "readOnly": false, 6 | "archived": false 7 | }, 8 | "implementation": { 9 | "type": "const", 10 | "value": "hello" 11 | }, 12 | "valueType": "string", 13 | "dependencies": [] 14 | } 15 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/manifests/smoke_tests/value_distribution/bernoulli.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "smoke_tests/value_distribution/bernoulli", 3 | "meta": { 4 | "description": "", 5 | "readOnly": false, 6 | "archived": false 7 | }, 8 | "implementation": { 9 | "type": "file", 10 | "format": "jpad" 11 | }, 12 | "valueType": "string", 13 | "dependencies": [] 14 | } 15 | -------------------------------------------------------------------------------- /addons/Rules/Tweek.Drivers.Rules.Minio/MinioSettings.cs: -------------------------------------------------------------------------------- 1 | namespace Tweek.Drivers.Rules.Minio 2 | { 3 | public class MinioSettings 4 | { 5 | public string Endpoint { get; set; } 6 | 7 | public string Bucket { get; set; } 8 | 9 | public string AccessKey { get; set; } 10 | 11 | public string SecretKey { get; set; } 12 | 13 | public bool IsSecure { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/manifests/integration_tests/some_key.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "integration_tests/some_key", 3 | "meta": { 4 | "name": "integration_tests/some_key", 5 | "tags": [], 6 | "description": "", 7 | "archived": false 8 | }, 9 | "implementation": { 10 | "type": "const", 11 | "value": 1 12 | }, 13 | "valueType": "number", 14 | "dependencies": [] 15 | } 16 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/manifests/smoke_tests/rule_based_keys/array_contains.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "smoke_tests/rule_based_keys/array_contains", 3 | "meta": { 4 | "description": "", 5 | "readOnly": false, 6 | "archived": false 7 | }, 8 | "implementation": { 9 | "type": "file", 10 | "format": "jpad" 11 | }, 12 | "valueType": "string", 13 | "dependencies": [] 14 | } 15 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/manifests/smoke_tests/ignore_key_types/array_type.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "smoke_tests/ignore_key_types/array_type", 3 | "meta": { 4 | "description": "", 5 | "readOnly": false, 6 | "archived": false 7 | }, 8 | "implementation": { 9 | "type": "const", 10 | "value": ["hello", "world"] 11 | }, 12 | "valueType": "array", 13 | "dependencies": [] 14 | } 15 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/manifests/smoke_tests/not_hidden/@hidden/@hidden_key.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "smoke_tests/not_hidden/@hidden/@hidden_key", 3 | "meta": { 4 | "description": "", 5 | "readOnly": false, 6 | "archived": false 7 | }, 8 | "implementation": { 9 | "type": "const", 10 | "value": "hidden value" 11 | }, 12 | "valueType": "string", 13 | "dependencies": [] 14 | } 15 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/manifests/smoke_tests/not_hidden/@some_hidden_key.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "smoke_tests/not_hidden/@some_hidden_key", 3 | "meta": { 4 | "description": "", 5 | "readOnly": false, 6 | "archived": false 7 | }, 8 | "implementation": { 9 | "type": "const", 10 | "value": "some hidden value" 11 | }, 12 | "valueType": "string", 13 | "dependencies": [] 14 | } 15 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/manifests/smoke_tests/rule_based_keys/multi_conditions.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "smoke_tests/rule_based_keys/multi_conditions", 3 | "meta": { 4 | "description": "", 5 | "readOnly": false, 6 | "archived": false 7 | }, 8 | "implementation": { 9 | "type": "file", 10 | "format": "jpad" 11 | }, 12 | "valueType": "string", 13 | "dependencies": [] 14 | } 15 | -------------------------------------------------------------------------------- /core/Engine/Tweek.Engine/Rules/Schema/ContextValidation.cs: -------------------------------------------------------------------------------- 1 | using Tweek.Engine.Core.Context; 2 | 3 | namespace Tweek.Engine.Rules.Schema 4 | { 5 | public delegate GetContextValue ValidateContext(GetContextValue origin); 6 | public class ContextValidation 7 | { 8 | public static ValidateContext CreateValidator(bool attemptToPatchChanges) 9 | { 10 | return x => x; 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /services/editor/src/pages/keys/components/KeyPage/KeyAddPage/NewKeyInput.less: -------------------------------------------------------------------------------- 1 | @import (reference) './KeyAddPage.less'; 2 | 3 | .keypath-input-wrapper { 4 | width: 100%; 5 | .combo-box-basic(@dropdown-min-width: 100%, @input-width: 100%, @font-size: @smaller-font); 6 | .keypath-input-container { 7 | width: 100%; 8 | display: flex; 9 | @with-validation-icon(); 10 | } 11 | @with-adjusted-combo-box-wrapper(); 12 | } 13 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/implementations/jpad/behavior_tests/dependent_keys/fail/first.jpad: -------------------------------------------------------------------------------- 1 | { 2 | "partitions": [], 3 | "valueType": "string", 4 | "rules": [ 5 | { 6 | "Matcher": { 7 | "keys.behavior_tests/dependent_keys/fail/second": "value" 8 | }, 9 | "Value": "value", 10 | "Type": "SingleVariant" 11 | } 12 | ] 13 | } -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/implementations/jpad/behavior_tests/dependent_keys/fail/second.jpad: -------------------------------------------------------------------------------- 1 | { 2 | "partitions": [], 3 | "valueType": "string", 4 | "rules": [ 5 | { 6 | "Matcher": { 7 | "keys.behavior_tests/dependent_keys/fail/third": "value" 8 | }, 9 | "Value": "value", 10 | "Type": "SingleVariant" 11 | } 12 | ] 13 | } -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/manifests/smoke_tests/not_hidden/@hidden/visible_key.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "smoke_tests/not_hidden/@hidden/visible_key", 3 | "meta": { 4 | "description": "", 5 | "readOnly": false, 6 | "archived": false 7 | }, 8 | "implementation": { 9 | "type": "const", 10 | "value": "visible value" 11 | }, 12 | "valueType": "string", 13 | "dependencies": [] 14 | } 15 | -------------------------------------------------------------------------------- /services/editor/src/pages/keys/components/KeyPage/KeyEditPage/KeyTags/KeyTags.less: -------------------------------------------------------------------------------- 1 | @import (reference) '../../../../../../styles/core/tags.less'; 2 | .key-tags { 3 | .tag { 4 | cursor: pointer !important; 5 | } 6 | .tags-basic; 7 | .tags-container { 8 | .tag-input { 9 | input, 10 | input:focus { 11 | padding: 0px 0px 0px 5px; 12 | margin: 3px 0px 0px 0px; 13 | } 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/manifests/smoke_tests/value_distribution/weighted_normalized.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "smoke_tests/value_distribution/weighted_normalized", 3 | "meta": { 4 | "description": "", 5 | "readOnly": false, 6 | "archived": false 7 | }, 8 | "implementation": { 9 | "type": "file", 10 | "format": "jpad" 11 | }, 12 | "valueType": "string", 13 | "dependencies": [] 14 | } 15 | -------------------------------------------------------------------------------- /services/publishing/Tweek.Publishing.Service/Validation/PolicyValidationException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Tweek.Publishing.Service.Validation 4 | { 5 | public class PolicyValidationException : Exception 6 | { 7 | public PolicyValidationException(Exception originalException) : base("policy is invalid") 8 | { 9 | this.Data["Original Exception"] = originalException; 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/implementations/jpad/behavior_tests/dependent_keys/display/depends_on.jpad: -------------------------------------------------------------------------------- 1 | { 2 | "partitions": [], 3 | "valueType": "string", 4 | "rules": [ 5 | { 6 | "Matcher": { 7 | "keys.behavior_tests/dependent_keys/display/used_by": "value" 8 | }, 9 | "Value": "value", 10 | "Type": "SingleVariant" 11 | } 12 | ] 13 | } -------------------------------------------------------------------------------- /services/publishing/Tweek.Publishing.Service/Model/Rules/KeyDef.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace Tweek.Publishing.Service.Model.Rules 4 | { 5 | public class KeyDef 6 | { 7 | [JsonProperty("payload")] 8 | public string Payload; 9 | 10 | [JsonProperty("dependencies")] 11 | public string[] Dependencies; 12 | 13 | [JsonProperty("format")] 14 | public string Format; 15 | } 16 | } -------------------------------------------------------------------------------- /addons/Context/Tweek.Drivers.Context.Couchbase.IntegrationTests/setup-couchbase.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | sleep 10 6 | curl --retry 3 --retry-max-time 10 -v http://couchbase:8091/settings/web -d port=8091 -d username=Administrator -d password=password 7 | curl --retry 3 --retry-max-time 10 -v -u Administrator:password -X POST http://couchbase:8091/pools/default/buckets -d authType=sasl -d name=testbucket -d ramQuotaMB=100 -d saslPassword=password 8 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/manifests/@tweek/schema/other.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "@tweek/schema/other", 3 | "meta": { 4 | "description": "", 5 | "readOnly": false, 6 | "archived": false 7 | }, 8 | "implementation": { 9 | "type": "const", 10 | "value": { 11 | "Type": { 12 | "type": "string" 13 | } 14 | } 15 | }, 16 | "valueType": "object", 17 | "dependencies": [] 18 | } 19 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/manifests/behavior_tests/delete_key/delete.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "behavior_tests/delete_key/delete", 3 | "meta": { 4 | "name": "behavior_tests/delete_key/delete", 5 | "tags": [], 6 | "description": "", 7 | "archived": true 8 | }, 9 | "implementation": { 10 | "type": "const", 11 | "value": "value" 12 | }, 13 | "valueType": "string", 14 | "dependencies": [] 15 | } 16 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/manifests/smoke_tests/ignore_key_types/object_type.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "smoke_tests/ignore_key_types/object_type", 3 | "meta": { 4 | "description": "", 5 | "readOnly": false, 6 | "archived": false 7 | }, 8 | "implementation": { 9 | "type": "const", 10 | "value": { 11 | "key": "value" 12 | } 13 | }, 14 | "valueType": "object", 15 | "dependencies": [] 16 | } 17 | -------------------------------------------------------------------------------- /e2e/integration/spec/setup.js: -------------------------------------------------------------------------------- 1 | const chai = require('chai'); 2 | chai.should(); 3 | 4 | const client = require('../utils/client'); 5 | const { waitUntil } = require('../utils/utils'); 6 | 7 | before('wait for authoring and api', async function() { 8 | this.timeout(0); 9 | console.log('Waiting for api and authoring services to be healthy...(up to 1 min)'); 10 | 11 | await waitUntil(() => client.get('/status').expect(200), 60000, 1000); 12 | }); 13 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/implementations/jpad/behavior_tests/dependent_keys/display/depends_on_alias.jpad: -------------------------------------------------------------------------------- 1 | { 2 | "partitions": [], 3 | "valueType": "string", 4 | "rules": [ 5 | { 6 | "Matcher": { 7 | "keys.behavior_tests/dependent_keys/display/alias_key": "value" 8 | }, 9 | "Value": "value", 10 | "Type": "SingleVariant" 11 | } 12 | ] 13 | } -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/manifests/behavior_tests/context/override_key.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "behavior_tests/context/override_key", 3 | "meta": { 4 | "name": "behavior_tests/context/override_key", 5 | "tags": [], 6 | "description": "", 7 | "archived": false 8 | }, 9 | "implementation": { 10 | "type": "const", 11 | "value": 0 12 | }, 13 | "valueType": "number", 14 | "dependencies": [] 15 | } 16 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/manifests/behavior_tests/delete_key/archive.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "behavior_tests/delete_key/archive", 3 | "meta": { 4 | "name": "behavior_tests/delete_key/archive", 5 | "tags": [], 6 | "description": "", 7 | "archived": false 8 | }, 9 | "implementation": { 10 | "type": "const", 11 | "value": "value" 12 | }, 13 | "valueType": "string", 14 | "dependencies": [] 15 | } 16 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/manifests/behavior_tests/read_only.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "behavior_tests/read_only", 3 | "meta": { 4 | "name": "behavior_tests/read_only", 5 | "description": "", 6 | "tags": [], 7 | "readOnly": true, 8 | "archived": false 9 | }, 10 | "implementation": { 11 | "type": "file", 12 | "format": "jpad" 13 | }, 14 | "valueType": "string", 15 | "dependencies": [] 16 | } 17 | -------------------------------------------------------------------------------- /services/publishing/base-repo/manifests/@tweek/editor/google_tag_manager/id.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "@tweek/editor/google_tag_manager/id", 3 | "meta": { 4 | "name": "@tweek/editor/google_tag_manager/id", 5 | "tags": [], 6 | "description": "ID of Google Tag Manager", 7 | "archived": false 8 | }, 9 | "implementation": { 10 | "type": "const", 11 | "value": "" 12 | }, 13 | "valueType": "string", 14 | "dependencies": [] 15 | } 16 | -------------------------------------------------------------------------------- /services/editor/src/pages/keys/components/KeyPage/MessageKeyPage/MessageKeyPage.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import './MessageKeyPage.css'; 3 | 4 | export type MessageKeyPageProps = { 5 | message: string; 6 | 'data-comp': string; 7 | }; 8 | 9 | const MessageKeyPage = ({ message, ...props }: MessageKeyPageProps) => ( 10 |
11 | {message} 12 |
13 | ); 14 | 15 | export default MessageKeyPage; 16 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/manifests/behavior_tests/key_aliases/regular_key.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "behavior_tests/key_aliases/regular_key", 3 | "meta": { 4 | "name": "behavior_tests/key_aliases/regular_key", 5 | "tags": [], 6 | "description": "", 7 | "archived": false 8 | }, 9 | "implementation": { 10 | "type": "const", 11 | "value": "hello" 12 | }, 13 | "valueType": "string", 14 | "dependencies": [] 15 | } 16 | -------------------------------------------------------------------------------- /services/publishing/Tweek.Publishing.Service/Sync/Converters/IConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO.Compression; 4 | using System.Threading.Tasks; 5 | 6 | namespace Tweek.Publishing.Service.Sync.Converters 7 | { 8 | public interface IConverter 9 | { 10 | (string fileName, string fileContent, string fileMimeType) Convert(string commitId, ICollection files, Func readFn); 11 | } 12 | } -------------------------------------------------------------------------------- /services/api/Tweek.ApiService/Security/ClaimsPrincipalExtentions.cs: -------------------------------------------------------------------------------- 1 | using LanguageExt; 2 | using System.Security.Claims; 3 | 4 | namespace Tweek.ApiService.Security 5 | { 6 | public static class ClaimsPrincipalExtentions 7 | { 8 | public static bool IsTweekIdentity(this ClaimsPrincipal identity) 9 | { 10 | return Prelude.Optional(identity.FindFirst("iss")).Match(c => c.Value.Equals("tweek"), () => false); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /services/authoring/src/security/configure-passport.ts: -------------------------------------------------------------------------------- 1 | import TweekInternalStrategy from './strategies/tweek-internal'; 2 | import AppCredentialsStrategy from './strategies/app-credentials'; 3 | const passport = require('passport'); 4 | 5 | export default function configurePassport(publickey, appsRepository) { 6 | passport.use(new TweekInternalStrategy(publickey)); 7 | passport.use(new AppCredentialsStrategy(appsRepository)); 8 | return passport.initialize(); 9 | } 10 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/source/manifests/@tweek/editor/google_tag_manager/id.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "@tweek/editor/google_tag_manager/id", 3 | "meta": { 4 | "name": "@tweek/editor/google_tag_manager/id", 5 | "tags": [], 6 | "description": "ID of Google Tag Manager", 7 | "archived": false 8 | }, 9 | "implementation": { 10 | "type": "const", 11 | "value": "" 12 | }, 13 | "valueType": "string", 14 | "dependencies": [] 15 | } 16 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/manifests/behavior_tests/keys_list/banana.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "behavior_tests/keys_list/banana", 3 | "meta": { 4 | "name": "behavior_tests/keys_list/banana", 5 | "description": "", 6 | "tags": [], 7 | "readOnly": false, 8 | "archived": false 9 | }, 10 | "implementation": { 11 | "type": "const", 12 | "value": "value" 13 | }, 14 | "valueType": "string", 15 | "dependencies": [] 16 | } 17 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/manifests/behavior_tests/revision_history.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "behavior_tests/revision_history", 3 | "meta": { 4 | "name": "behavior_tests/revision_history", 5 | "description": "", 6 | "tags": [], 7 | "readOnly": false, 8 | "archived": false 9 | }, 10 | "implementation": { 11 | "type": "file", 12 | "format": "jpad" 13 | }, 14 | "valueType": "string", 15 | "dependencies": [] 16 | } 17 | -------------------------------------------------------------------------------- /addons/Context/Tweek.Drivers.Context.Couchbase.IntegrationTests/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.1' 2 | 3 | 4 | services: 5 | couchbase: 6 | image: couchbase:community-4.5.1 7 | ports: 8 | - "8091-8094:8091-8094" 9 | - "11210:11210" 10 | 11 | couchbase-setup: 12 | image: joffotron/docker-net-tools 13 | depends_on: 14 | - couchbase 15 | volumes: 16 | - ./setup-couchbase.sh:/opt/setup-couchbase.sh 17 | command: /opt/setup-couchbase.sh -------------------------------------------------------------------------------- /e2e/integration/utils/manifest.js: -------------------------------------------------------------------------------- 1 | const createManifestForJPadKey = (key_path, name = 'aaaaaa') => ({ 2 | key_path: `${key_path}`, 3 | meta: { 4 | name: name, 5 | tags: [], 6 | description: '', 7 | archived: false, 8 | }, 9 | implementation: { 10 | type: 'file', 11 | format: 'jpad', 12 | }, 13 | valueType: 'number', 14 | dependencies: [], 15 | enabled: true, 16 | }); 17 | 18 | module.exports.createManifestForJPadKey = createManifestForJPadKey; -------------------------------------------------------------------------------- /e2e/ui/utils/location-utils.js: -------------------------------------------------------------------------------- 1 | import { ClientFunction, t } from 'testcafe'; 2 | import { editorUrl } from './constants'; 3 | 4 | export const getLocation = ClientFunction(() => document.location.href); 5 | 6 | export const refresh = ClientFunction(() => location.reload(true)); 7 | 8 | export const navigateToKey = async (keyPath) => { 9 | await t 10 | .navigateTo(`/keys/${keyPath}`) 11 | .expect(getLocation()) 12 | .eql(`${editorUrl}/keys/${keyPath}`); 13 | }; 14 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/manifests/@tweek/schema/delete_property_test.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "@tweek/schema/delete_property_test", 3 | "meta": { 4 | "description": "", 5 | "readOnly": false, 6 | "archived": false 7 | }, 8 | "implementation": { 9 | "type": "const", 10 | "value": { 11 | "Group": { 12 | "type": "string" 13 | } 14 | } 15 | }, 16 | "valueType": "object", 17 | "dependencies": [] 18 | } 19 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/manifests/@tweek/schema/edit_properties_test.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "@tweek/schema/edit_properties_test", 3 | "meta": { 4 | "description": "", 5 | "readOnly": false, 6 | "archived": false 7 | }, 8 | "implementation": { 9 | "type": "const", 10 | "value": { 11 | "Group": { 12 | "type": "string" 13 | } 14 | } 15 | }, 16 | "valueType": "object", 17 | "dependencies": [] 18 | } 19 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/manifests/integration_tests/include_errors.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "integration_tests/include_errors", 3 | "meta": { 4 | "name": "integration_tests/include_errors", 5 | "description": "", 6 | "tags": [], 7 | "readOnly": false, 8 | "archived": false 9 | }, 10 | "implementation": { 11 | "type": "file", 12 | "format": "jpad" 13 | }, 14 | "valueType": "string", 15 | "dependencies": [] 16 | } 17 | -------------------------------------------------------------------------------- /core/JPad/Tweek.JPad.Utils/app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /services/authoring/src/utils/error-utils.ts: -------------------------------------------------------------------------------- 1 | import { ValidationError } from '../repositories/git-repository'; 2 | 3 | type errorMapping = [Function, number]; 4 | 5 | const errorMapping: errorMapping[] = [[ValidationError, 400]]; 6 | 7 | export function getErrorStatusCode(error): number { 8 | for (const [errorType, errorCode] of errorMapping) { 9 | if (error instanceof errorType) { 10 | return errorCode; 11 | } 12 | } 13 | return error.statusCode || 500; 14 | } 15 | -------------------------------------------------------------------------------- /services/editor/src/pages/keys/utils/search.ts: -------------------------------------------------------------------------------- 1 | export function getTagLink(tag: string) { 2 | const whiteSpaces = (tag.match(/\s/g) || []).length; 3 | return `/keys/$search/tags:${tag.replace(/\s/g, '_')}${whiteSpaces > 0 ? `~${whiteSpaces}` : ''}`; 4 | } 5 | 6 | export function getSearchLink(query: string) { 7 | const whiteSpaces = (query.match(/[\s/]/g) || []).length; 8 | return `/keys/$search/${query.replace(/[\s/]/g, '_')}${whiteSpaces > 0 ? `~${whiteSpaces}` : ''}`; 9 | } 10 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/manifests/@tweek/editor/google_tag_manager/id.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "@tweek/editor/google_tag_manager/id", 3 | "meta": { 4 | "name": "@tweek/editor/google_tag_manager/id", 5 | "tags": [], 6 | "description": "ID of Google Tag Manager", 7 | "archived": false 8 | }, 9 | "implementation": { 10 | "type": "file", 11 | "format": "jpad" 12 | }, 13 | "valueType": "string", 14 | "dependencies": [] 15 | } 16 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/manifests/behavior_tests/keys_list/red_apple.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "behavior_tests/keys_list/red_apple", 3 | "meta": { 4 | "name": "behavior_tests/keys_list/red_apple", 5 | "description": "", 6 | "tags": [], 7 | "readOnly": false, 8 | "archived": false 9 | }, 10 | "implementation": { 11 | "type": "const", 12 | "value": "value" 13 | }, 14 | "valueType": "string", 15 | "dependencies": [] 16 | } 17 | -------------------------------------------------------------------------------- /services/publishing/base-repo/manifests/@tweek/editor/search/max_results.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "@tweek/editor/search/max_results", 3 | "meta": { 4 | "name": "@tweek/editor/search/max_results", 5 | "description": "Max number of search results", 6 | "tags": [], 7 | "readOnly": false, 8 | "archived": false 9 | }, 10 | "implementation": { 11 | "type": "const", 12 | "value": 25 13 | }, 14 | "valueType": "number", 15 | "dependencies": [] 16 | } 17 | -------------------------------------------------------------------------------- /core/Utils/JsonValueConverter/app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/manifests/behavior_tests/keys_list/green_apple.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "behavior_tests/keys_list/green_apple", 3 | "meta": { 4 | "name": "behavior_tests/keys_list/green_apple", 5 | "description": "", 6 | "tags": [], 7 | "readOnly": false, 8 | "archived": false 9 | }, 10 | "implementation": { 11 | "type": "const", 12 | "value": "value" 13 | }, 14 | "valueType": "string", 15 | "dependencies": [] 16 | } 17 | -------------------------------------------------------------------------------- /services/publishing/Tweek.Publishing.Service/Model/ExternalApps/SecretKey.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Newtonsoft.Json; 3 | 4 | namespace Tweek.Publishing.Service.Model.ExternalApps 5 | { 6 | public class SecretKey 7 | { 8 | [JsonProperty("salt")] 9 | public string Salt; 10 | 11 | [JsonProperty("hash")] 12 | public string Hash; 13 | 14 | [JsonProperty("creationDate")] 15 | public string CreationDate; 16 | } 17 | } -------------------------------------------------------------------------------- /services/publishing/base-repo/manifests/@tweek/editor/history/since.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "@tweek/editor/history/since", 3 | "meta": { 4 | "name": "@tweek/editor/history/since", 5 | "description": "Revision history - how far back to look", 6 | "tags": [], 7 | "readOnly": false, 8 | "archived": false 9 | }, 10 | "implementation": { 11 | "type": "const", 12 | "value": "1 month ago" 13 | }, 14 | "valueType": "string", 15 | "dependencies": [] 16 | } 17 | -------------------------------------------------------------------------------- /core/Engine/Tweek.Engine.DataTypes/app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/source/manifests/@tweek/editor/search/max_results.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "@tweek/editor/search/max_results", 3 | "meta": { 4 | "name": "@tweek/editor/search/max_results", 5 | "description": "Max number of search results", 6 | "tags": [], 7 | "readOnly": false, 8 | "archived": false 9 | }, 10 | "implementation": { 11 | "type": "const", 12 | "value": 25 13 | }, 14 | "valueType": "number", 15 | "dependencies": [] 16 | } 17 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/manifests/behavior_tests/edit_key/text/edit_test.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "behavior_tests/edit_key/text/edit_test", 3 | "meta": { 4 | "name": "behavior_tests/edit_key/text/edit_test", 5 | "description": "", 6 | "tags": [], 7 | "readOnly": false, 8 | "archived": false 9 | }, 10 | "implementation": { 11 | "type": "file", 12 | "format": "jpad" 13 | }, 14 | "valueType": "string", 15 | "dependencies": [] 16 | } 17 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/manifests/behavior_tests/edit_key/visual/const/number_type.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "behavior_tests/edit_key/visual/const/number_type", 3 | "meta": { 4 | "name": "behavior_tests/edit_key/visual/const/number_type", 5 | "tags": [], 6 | "description": "", 7 | "archived": false 8 | }, 9 | "implementation": { 10 | "type": "const", 11 | "value": 10 12 | }, 13 | "valueType": "number", 14 | "dependencies": [] 15 | } 16 | -------------------------------------------------------------------------------- /services/publishing/base-repo/manifests/@tweek/auth/@global/write_context.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "@tweek/auth/@global/write_context", 3 | "meta": { 4 | "name": "@tweek/auth/@global/write_context", 5 | "description": "This key determines, whether a user can write context", 6 | "readOnly": false, 7 | "archived": false 8 | }, 9 | "implementation": { 10 | "type": "file", 11 | "format": "jpad" 12 | }, 13 | "valueType": "string", 14 | "dependencies": [] 15 | } 16 | -------------------------------------------------------------------------------- /core/Engine/Tweek.Engine.Drivers/Utils/JsonValueExtensions.cs: -------------------------------------------------------------------------------- 1 | using FSharpUtils.Newtonsoft; 2 | using LanguageExt; 3 | using static LanguageExt.Prelude; 4 | 5 | namespace Tweek.Engine.Drivers { 6 | public static class JsonValueExtensions 7 | { 8 | public static Option GetPropertyOption(this JsonValue json, string propName) 9 | { 10 | var prop = json.TryGetProperty(propName); 11 | return Optional(prop?.Value); 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/manifests/@tweek/editor/search/max_results.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "@tweek/editor/search/max_results", 3 | "meta": { 4 | "name": "@tweek/editor/search/max_results", 5 | "description": "Max number of search results", 6 | "tags": [], 7 | "readOnly": false, 8 | "archived": false 9 | }, 10 | "implementation": { 11 | "type": "const", 12 | "value": 25 13 | }, 14 | "valueType": "number", 15 | "dependencies": [] 16 | } 17 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/manifests/behavior_tests/dependent_keys/fail/third.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "behavior_tests/dependent_keys/fail/third", 3 | "meta": { 4 | "name": "behavior_tests/dependent_keys/fail/third", 5 | "description": "", 6 | "tags": [], 7 | "readOnly": false, 8 | "archived": false 9 | }, 10 | "implementation": { 11 | "type": "file", 12 | "format": "jpad" 13 | }, 14 | "valueType": "string", 15 | "dependencies": [] 16 | } 17 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/manifests/behavior_tests/edit_key/visual/const/string_type.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "behavior_tests/edit_key/visual/const/string_type", 3 | "meta": { 4 | "name": "behavior_tests/edit_key/visual/const/string_type", 5 | "tags": [], 6 | "description": "", 7 | "archived": false 8 | }, 9 | "implementation": { 10 | "type": "const", 11 | "value": "hello" 12 | }, 13 | "valueType": "string", 14 | "dependencies": [] 15 | } 16 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/manifests/behavior_tests/edit_key/visual/edit_test.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "behavior_tests/edit_key/visual/edit_test", 3 | "meta": { 4 | "name": "behavior_tests/edit_key/visual/edit_test", 5 | "description": "", 6 | "tags": [], 7 | "readOnly": false, 8 | "archived": false 9 | }, 10 | "implementation": { 11 | "type": "file", 12 | "format": "jpad" 13 | }, 14 | "valueType": "string", 15 | "dependencies": [] 16 | } 17 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/manifests/behavior_tests/partitions/add_partition.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "behavior_tests/partitions/add_partition", 3 | "meta": { 4 | "name": "behavior_tests/partitions/add_partition", 5 | "description": "", 6 | "tags": [], 7 | "readOnly": false, 8 | "archived": false 9 | }, 10 | "implementation": { 11 | "type": "file", 12 | "format": "jpad" 13 | }, 14 | "valueType": "string", 15 | "dependencies": [] 16 | } 17 | -------------------------------------------------------------------------------- /services/publishing/Tweek.Publishing.Service/Sync/RevisionException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Tweek.Publishing.Service.Sync 4 | { 5 | public class RevisionException : System.Exception 6 | { 7 | public RevisionException(string currentCommit, string incomingCommit, string reason) 8 | { 9 | Data["CurrentCommit"] = currentCommit; 10 | Data["IncomingCommit"] = incomingCommit; 11 | Data["Reason"] = reason; 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /services/editor/src/utils/hooks/useDebounceValue.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react'; 2 | 3 | export const useDebounceValue = (value: T, delay: number): T => { 4 | const [debouncedValue, setDebouncedValue] = useState(value); 5 | 6 | useEffect(() => { 7 | const timeout = setTimeout(() => setDebouncedValue(value), delay); 8 | 9 | return () => clearTimeout(timeout); 10 | }, [value]); //eslint-disable-line react-hooks/exhaustive-deps 11 | 12 | return debouncedValue; 13 | }; 14 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/source/manifests/@tweek/auth/@global/write_context.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "@tweek/auth/@global/write_context", 3 | "meta": { 4 | "name": "@tweek/auth/@global/write_context", 5 | "description": "This key determines, whether a user can write context", 6 | "readOnly": false, 7 | "archived": false 8 | }, 9 | "implementation": { 10 | "type": "file", 11 | "format": "jpad" 12 | }, 13 | "valueType": "string", 14 | "dependencies": [] 15 | } 16 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/source/manifests/@tweek/editor/history/since.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "@tweek/editor/history/since", 3 | "meta": { 4 | "name": "@tweek/editor/history/since", 5 | "description": "Revision history - how far back to look", 6 | "tags": [], 7 | "readOnly": false, 8 | "archived": false 9 | }, 10 | "implementation": { 11 | "type": "const", 12 | "value": "1 month ago" 13 | }, 14 | "valueType": "string", 15 | "dependencies": [] 16 | } 17 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/manifests/behavior_tests/edit_key/visual/const/date_type.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "behavior_tests/edit_key/visual/const/date_type", 3 | "meta": { 4 | "archived": false, 5 | "name": "behavior_tests/edit_key/visual/const/date_type", 6 | "description": "", 7 | "tags": [] 8 | }, 9 | "implementation": { 10 | "type": "const", 11 | "value": "2018-10-10T00:00:00Z" 12 | }, 13 | "valueType": "date", 14 | "dependencies": [] 15 | } 16 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/manifests/behavior_tests/partitions/empty_partition.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "behavior_tests/partitions/empty_partition", 3 | "meta": { 4 | "name": "behavior_tests/partitions/empty_partition", 5 | "description": "", 6 | "tags": [], 7 | "readOnly": false, 8 | "archived": false 9 | }, 10 | "implementation": { 11 | "type": "file", 12 | "format": "jpad" 13 | }, 14 | "valueType": "string", 15 | "dependencies": [] 16 | } 17 | -------------------------------------------------------------------------------- /services/publishing/base-repo/manifests/@tweek/editor/google_tag_manager/enabled.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "@tweek/editor/google_tag_manager/enabled", 3 | "meta": { 4 | "name": "@tweek/editor/google_tag_manager/enabled", 5 | "tags": [], 6 | "description": "Determines whether Google Tag Manager is enabled", 7 | "archived": false 8 | }, 9 | "implementation": { 10 | "type": "const", 11 | "value": false 12 | }, 13 | "valueType": "boolean", 14 | "dependencies": [] 15 | } 16 | -------------------------------------------------------------------------------- /services/gateway/.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, build with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | # vendor directory should not be committed 15 | vendor 16 | 17 | # output executable should not be committed 18 | gateway 19 | 20 | # macOS Finder creates these files 21 | .DS_Store 22 | 23 | # gateway executable file 24 | tweek-gateway -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/manifests/@tweek/editor/history/since.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "@tweek/editor/history/since", 3 | "meta": { 4 | "name": "@tweek/editor/history/since", 5 | "description": "Revision history - how far back to look", 6 | "tags": [], 7 | "readOnly": false, 8 | "archived": false 9 | }, 10 | "implementation": { 11 | "type": "const", 12 | "value": "1 month ago" 13 | }, 14 | "valueType": "string", 15 | "dependencies": [] 16 | } 17 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/manifests/behavior_tests/dependent_keys/pass/used_by.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "behavior_tests/dependent_keys/pass/used_by", 3 | "meta": { 4 | "name": "behavior_tests/dependent_keys/pass/used_by", 5 | "description": "", 6 | "tags": [], 7 | "readOnly": false, 8 | "archived": false 9 | }, 10 | "implementation": { 11 | "type": "file", 12 | "format": "jpad" 13 | }, 14 | "valueType": "string", 15 | "dependencies": [] 16 | } 17 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/manifests/behavior_tests/partitions/delete_partition.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "behavior_tests/partitions/delete_partition", 3 | "meta": { 4 | "name": "behavior_tests/partitions/delete_partition", 5 | "description": "", 6 | "tags": [], 7 | "readOnly": false, 8 | "archived": false 9 | }, 10 | "implementation": { 11 | "type": "file", 12 | "format": "jpad" 13 | }, 14 | "valueType": "string", 15 | "dependencies": [] 16 | } 17 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/manifests/behavior_tests/partitions/partition_groups.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "behavior_tests/partitions/partition_groups", 3 | "meta": { 4 | "name": "behavior_tests/partitions/partition_groups", 5 | "description": "", 6 | "tags": [], 7 | "readOnly": false, 8 | "archived": false 9 | }, 10 | "implementation": { 11 | "type": "file", 12 | "format": "jpad" 13 | }, 14 | "valueType": "string", 15 | "dependencies": [] 16 | } 17 | -------------------------------------------------------------------------------- /services/publishing/Tweek.Publishing.Service/Model/ExternalApps/ExternalApp.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Newtonsoft.Json; 3 | 4 | namespace Tweek.Publishing.Service.Model.ExternalApps 5 | { 6 | public class ExternalApp 7 | { 8 | [JsonProperty("name")] 9 | public string Name; 10 | 11 | [JsonProperty("version")] 12 | public string Version; 13 | 14 | [JsonProperty("secretKeys")] 15 | public IEnumerable SecretKeys; 16 | } 17 | } -------------------------------------------------------------------------------- /services/publishing/base-repo/manifests/@tweek/editor/show_internal_keys.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "@tweek/editor/show_internal_keys", 3 | "meta": { 4 | "name": "@tweek/editor/show_internal_keys", 5 | "description": "Toggle showing internal keys starting with '@tweek'", 6 | "tags": [], 7 | "readOnly": false, 8 | "archived": false 9 | }, 10 | "implementation": { 11 | "type": "const", 12 | "value": false 13 | }, 14 | "valueType": "boolean", 15 | "dependencies": [] 16 | } 17 | -------------------------------------------------------------------------------- /TweekApiSmokeTest.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/dotnet/sdk:5.0 as source 2 | 3 | RUN apt-get update && \ 4 | apt-get install -y --no-install-recommends jq && \ 5 | apt-get clean && \ 6 | wget https://raw.githubusercontent.com/vishnubob/wait-for-it/8ed92e8cab83cfed76ff012ed4a36cef74b28096/wait-for-it.sh && \ 7 | chmod +x wait-for-it.sh 8 | 9 | COPY . /repo/tweek 10 | WORKDIR /repo/tweek/services/api/Tweek.ApiService.SmokeTests 11 | RUN dotnet restore && dotnet build 12 | 13 | CMD ./test.sh 14 | -------------------------------------------------------------------------------- /e2e/ui/clients/tweek-clients.js: -------------------------------------------------------------------------------- 1 | import { createTweekClient, createTweekManagementClient } from 'tweek-client'; 2 | import { gatewayUrl } from '../utils/constants'; 3 | import { credentials } from '../utils/auth-utils'; 4 | 5 | const options = { 6 | baseServiceUrl: gatewayUrl, 7 | clientId: credentials.username, 8 | clientSecret: credentials.password, 9 | }; 10 | 11 | export const tweekClient = createTweekClient(options); 12 | 13 | export const tweekManagementClient = createTweekManagementClient(options); 14 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/source/manifests/@tweek/editor/google_tag_manager/enabled.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "@tweek/editor/google_tag_manager/enabled", 3 | "meta": { 4 | "name": "@tweek/editor/google_tag_manager/enabled", 5 | "tags": [], 6 | "description": "Determines whether Google Tag Manager is enabled", 7 | "archived": false 8 | }, 9 | "implementation": { 10 | "type": "const", 11 | "value": false 12 | }, 13 | "valueType": "boolean", 14 | "dependencies": [] 15 | } 16 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/manifests/@tweek/auth/@global/write_context.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "@tweek/auth/@global/write_context", 3 | "meta": { 4 | "name": "@tweek/auth/@global/write_context", 5 | "description": "This key determines, whether a user can write context", 6 | "readOnly": false, 7 | "archived": false 8 | }, 9 | "implementation": { 10 | "type": "file", 11 | "format": "jpad" 12 | }, 13 | "valueType": "string", 14 | "dependencies": [] 15 | } 16 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/manifests/behavior_tests/dependent_keys/display/used_by.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "behavior_tests/dependent_keys/display/used_by", 3 | "meta": { 4 | "name": "behavior_tests/dependent_keys/display/used_by", 5 | "description": "", 6 | "tags": [], 7 | "readOnly": false, 8 | "archived": false 9 | }, 10 | "implementation": { 11 | "type": "file", 12 | "format": "jpad" 13 | }, 14 | "valueType": "string", 15 | "dependencies": [] 16 | } 17 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/manifests/behavior_tests/dependent_keys/pass/depends_on.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "behavior_tests/dependent_keys/pass/depends_on", 3 | "meta": { 4 | "name": "behavior_tests/dependent_keys/pass/depends_on", 5 | "description": "", 6 | "tags": [], 7 | "readOnly": false, 8 | "archived": false 9 | }, 10 | "implementation": { 11 | "type": "file", 12 | "format": "jpad" 13 | }, 14 | "valueType": "string", 15 | "dependencies": [] 16 | } 17 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/manifests/behavior_tests/partitions/add_partition_group.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "behavior_tests/partitions/add_partition_group", 3 | "meta": { 4 | "name": "behavior_tests/partitions/add_partition_group", 5 | "description": "", 6 | "tags": [], 7 | "readOnly": false, 8 | "archived": false 9 | }, 10 | "implementation": { 11 | "type": "file", 12 | "format": "jpad" 13 | }, 14 | "valueType": "string", 15 | "dependencies": [] 16 | } 17 | -------------------------------------------------------------------------------- /services/publishing/base-repo/manifests/@tweek/auth/@global/read_configuration.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "@tweek/auth/@global/read_configuration", 3 | "meta": { 4 | "name": "@tweek/auth/@global/read_configuration", 5 | "description": "This key determines, whether a user can read configuration", 6 | "readOnly": false, 7 | "archived": false 8 | }, 9 | "implementation": { 10 | "type": "file", 11 | "format": "jpad" 12 | }, 13 | "valueType": "string", 14 | "dependencies": [] 15 | } 16 | -------------------------------------------------------------------------------- /services/publishing/base-repo/manifests/@tweek/editor/service_worker/is_enabled.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "@tweek/editor/service_worker/is_enabled", 3 | "meta": { 4 | "name": "@tweek/editor/service_worker/is_enabled", 5 | "description": "Is service-worker enabled in editor", 6 | "tags": [], 7 | "readOnly": false, 8 | "archived": false 9 | }, 10 | "implementation": { 11 | "type": "const", 12 | "value": false 13 | }, 14 | "valueType": "boolean", 15 | "dependencies": [] 16 | } 17 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/manifests/behavior_tests/edit_key/visual/edit_array_test.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "behavior_tests/edit_key/visual/edit_array_test", 3 | "meta": { 4 | "name": "behavior_tests/edit_key/visual/edit_array_test", 5 | "description": "", 6 | "tags": [], 7 | "readOnly": false, 8 | "archived": false 9 | }, 10 | "implementation": { 11 | "type": "file", 12 | "format": "jpad" 13 | }, 14 | "valueType": "array", 15 | "dependencies": [] 16 | } 17 | -------------------------------------------------------------------------------- /core/Engine/Tweek.Engine.Drivers/Context/IContextWriter.cs: -------------------------------------------------------------------------------- 1 | using FSharpUtils.Newtonsoft; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | using Tweek.Engine.DataTypes; 5 | 6 | namespace Tweek.Engine.Drivers.Context 7 | { 8 | public interface IContextWriter 9 | { 10 | Task AppendContext(Identity identity, Dictionary context); 11 | Task RemoveFromContext(Identity identity, string key); 12 | Task DeleteContext(Identity identity); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /core/Engine/Tweek.Engine/Tweek.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Tweek.Engine.Drivers.Rules; 3 | using Tweek.Engine.Rules.Creation; 4 | 5 | namespace Tweek.Engine 6 | { 7 | public static class Tweek 8 | { 9 | public static async Task Create(IRulesRepository rulesRepository, GetRuleParser parserResolver) 10 | { 11 | var rulesLoader = await RulesLoader.Factory(rulesRepository, parserResolver); 12 | return new TweekRunner(rulesLoader); 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /services/git-service/BareRepository/source/manifests/@tweek/auth/@global/read_configuration.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "@tweek/auth/@global/read_configuration", 3 | "meta": { 4 | "name": "@tweek/auth/@global/read_configuration", 5 | "description": "This key determines, whether a user can read configuration", 6 | "readOnly": false, 7 | "archived": false 8 | }, 9 | "implementation": { 10 | "type": "file", 11 | "format": "jpad" 12 | }, 13 | "valueType": "string", 14 | "dependencies": [] 15 | } 16 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/source/manifests/@tweek/editor/show_internal_keys.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "@tweek/editor/show_internal_keys", 3 | "meta": { 4 | "name": "@tweek/editor/show_internal_keys", 5 | "description": "Toggle showing internal keys starting with '@tweek'", 6 | "tags": [], 7 | "readOnly": false, 8 | "archived": false 9 | }, 10 | "implementation": { 11 | "type": "const", 12 | "value": false 13 | }, 14 | "valueType": "boolean", 15 | "dependencies": [] 16 | } 17 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/manifests/@tweek/editor/google_tag_manager/enabled.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "@tweek/editor/google_tag_manager/enabled", 3 | "meta": { 4 | "name": "@tweek/editor/google_tag_manager/enabled", 5 | "tags": [], 6 | "description": "Determines whether Google Tag Manager is enabled", 7 | "archived": false 8 | }, 9 | "implementation": { 10 | "type": "file", 11 | "format": "jpad" 12 | }, 13 | "valueType": "boolean", 14 | "dependencies": [] 15 | } 16 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/manifests/behavior_tests/edit_key/visual/edit_object_test.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "behavior_tests/edit_key/visual/edit_object_test", 3 | "meta": { 4 | "name": "behavior_tests/edit_key/visual/edit_object_test", 5 | "description": "", 6 | "tags": [], 7 | "readOnly": false, 8 | "archived": false 9 | }, 10 | "implementation": { 11 | "type": "file", 12 | "format": "jpad" 13 | }, 14 | "valueType": "object", 15 | "dependencies": [] 16 | } 17 | -------------------------------------------------------------------------------- /services/publishing/Tweek.Publishing.Service/Validation/SubjectExtractionRulesValidationException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Tweek.Publishing.Service.Validation 4 | { 5 | public class SubjectExtractionRulesValidationException : Exception 6 | { 7 | public SubjectExtractionRulesValidationException(Exception originalException) : base("subject extraction Open Policy Agent rules are invalid") 8 | { 9 | this.Data["Original Exception"] = originalException; 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /addons/ApplicationInsights/Tweek.Addons.ApplicationInsights/Tweek.Addons.ApplicationInsights.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | netstandard2.1 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/source/manifests/@tweek/editor/service_worker/is_enabled.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "@tweek/editor/service_worker/is_enabled", 3 | "meta": { 4 | "name": "@tweek/editor/service_worker/is_enabled", 5 | "description": "Is service-worker enabled in editor", 6 | "tags": [], 7 | "readOnly": false, 8 | "archived": false 9 | }, 10 | "implementation": { 11 | "type": "const", 12 | "value": false 13 | }, 14 | "valueType": "boolean", 15 | "dependencies": [] 16 | } 17 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/manifests/@tweek/editor/show_internal_keys.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "@tweek/editor/show_internal_keys", 3 | "meta": { 4 | "name": "@tweek/editor/show_internal_keys", 5 | "description": "Toggle showing internal keys starting with '@tweek'", 6 | "tags": [], 7 | "readOnly": false, 8 | "archived": false 9 | }, 10 | "implementation": { 11 | "type": "const", 12 | "value": false 13 | }, 14 | "valueType": "boolean", 15 | "dependencies": [] 16 | } 17 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/manifests/behavior_tests/edit_key/visual/const/object_type.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "behavior_tests/edit_key/visual/const/object_type", 3 | "meta": { 4 | "name": "behavior_tests/edit_key/visual/const/object_type", 5 | "tags": [], 6 | "description": "", 7 | "archived": false 8 | }, 9 | "implementation": { 10 | "type": "const", 11 | "value": { 12 | "boolProp": true 13 | } 14 | }, 15 | "valueType": "object", 16 | "dependencies": [] 17 | } 18 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/manifests/integration_tests/value_distribution/array_format.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "integration_tests/value_distribution/array_format", 3 | "meta": { 4 | "name": "integration_tests/value_distribution/array_format", 5 | "description": "", 6 | "tags": [], 7 | "readOnly": false, 8 | "archived": false 9 | }, 10 | "implementation": { 11 | "type": "file", 12 | "format": "jpad" 13 | }, 14 | "valueType": "number", 15 | "dependencies": [] 16 | } 17 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/manifests/integration_tests/value_distribution/object_format.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "integration_tests/value_distribution/object_format", 3 | "meta": { 4 | "name": "integration_tests/value_distribution/object_format", 5 | "description": "", 6 | "tags": [], 7 | "readOnly": false, 8 | "archived": false 9 | }, 10 | "implementation": { 11 | "type": "file", 12 | "format": "jpad" 13 | }, 14 | "valueType": "number", 15 | "dependencies": [] 16 | } 17 | -------------------------------------------------------------------------------- /services/publishing/base-repo/manifests/@tweek/auth/tweek_editor_user/write_context.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "@tweek/auth/tweek_editor_user/write_context", 3 | "meta": { 4 | "name": "@tweek/auth/tweek_editor_user/write_context", 5 | "description": "Determines permissions to write context", 6 | "tags": [], 7 | "readOnly": false, 8 | "archived": false 9 | }, 10 | "implementation": { 11 | "type": "const", 12 | "value": false 13 | }, 14 | "valueType": "boolean", 15 | "dependencies": [] 16 | } 17 | -------------------------------------------------------------------------------- /services/authoring/src/security/permissions/consts.ts: -------------------------------------------------------------------------------- 1 | export const PERMISSIONS = { 2 | KEYS_LIST: 'keys-list', 3 | KEYS_READ: 'keys-read', 4 | KEYS_WRITE: 'keys-write', 5 | SCHEMAS_READ: 'schemas-read', 6 | SCHEMAS_WRITE: 'schemas-write', 7 | HISTORY: 'history', 8 | SEARCH: 'search', 9 | SEARCH_INDEX: 'search-index', 10 | ADMIN: 'admin', 11 | TAGS_READ: 'tags-read', 12 | TAGS_WRITE: 'tags-write', 13 | HOOKS_READ: 'hooks-read', 14 | HOOKS_WRITE: 'hooks-write', 15 | }; 16 | 17 | // export default PERMISSIONS; 18 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/manifests/@tweek/auth/@global/read_configuration.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "@tweek/auth/@global/read_configuration", 3 | "meta": { 4 | "name": "@tweek/auth/@global/read_configuration", 5 | "description": "This key determines, whether a user can read configuration", 6 | "readOnly": false, 7 | "archived": false 8 | }, 9 | "implementation": { 10 | "type": "file", 11 | "format": "jpad" 12 | }, 13 | "valueType": "string", 14 | "dependencies": [] 15 | } 16 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/manifests/@tweek/editor/service_worker/is_enabled.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "@tweek/editor/service_worker/is_enabled", 3 | "meta": { 4 | "name": "@tweek/editor/service_worker/is_enabled", 5 | "description": "Is service-worker enabled in editor", 6 | "tags": [], 7 | "readOnly": false, 8 | "archived": false 9 | }, 10 | "implementation": { 11 | "type": "const", 12 | "value": false 13 | }, 14 | "valueType": "boolean", 15 | "dependencies": [] 16 | } 17 | -------------------------------------------------------------------------------- /core/Engine/Tweek.Engine.Drivers/Rules/IRulesDriver.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | 5 | namespace Tweek.Engine.Drivers.Rules 6 | { 7 | public interface IRulesDriver 8 | { 9 | Task GetVersion(CancellationToken cancellationToken = default(CancellationToken)); 10 | 11 | Task> GetRuleset(string version, 12 | CancellationToken cancellationToken = default(CancellationToken)); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /services/editor/src/components/JPadFullEditor/JPadVisualEditor/JPadVisualEditor.less: -------------------------------------------------------------------------------- 1 | @import (reference) '../../../styles/core/comboBox.less'; 2 | .jpad-editor-container { 3 | .jpad-settings { 4 | display: flex; 5 | float: right; 6 | padding: 20px 25px; 7 | 8 | .default-value .bootstrap-typeahead .dropdown-menu { 9 | min-width: 150px; 10 | } 11 | } 12 | .vertical-separator { 13 | width: 1px; 14 | background-color: lightgray; 15 | height: 30px; 16 | margin: 0 15px 0 12px; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /services/editor/src/components/common/EditableTextArea/EditableTextArea.less: -------------------------------------------------------------------------------- 1 | @import (reference) '../../../styles/core/core.less'; 2 | .textarea-container { 3 | .textarea-input { 4 | display: block; 5 | border: 1px solid lightgray; 6 | border-radius: 5px; 7 | padding: 10px; 8 | .transitioned(); 9 | &.read-only { 10 | border: 1px solid transparent; 11 | padding-left: 3px; 12 | } 13 | &:disabled { 14 | border: 1px solid transparent; 15 | padding-left: 10px; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/source/manifests/@tweek/auth/tweek_editor_user/write_context.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "@tweek/auth/tweek_editor_user/write_context", 3 | "meta": { 4 | "name": "@tweek/auth/tweek_editor_user/write_context", 5 | "description": "Determines permissions to write context", 6 | "tags": [], 7 | "readOnly": false, 8 | "archived": false 9 | }, 10 | "implementation": { 11 | "type": "const", 12 | "value": false 13 | }, 14 | "valueType": "boolean", 15 | "dependencies": [] 16 | } 17 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/manifests/behavior_tests/dependent_keys/fail/first.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "behavior_tests/dependent_keys/fail/first", 3 | "meta": { 4 | "name": "behavior_tests/dependent_keys/fail/first", 5 | "description": "", 6 | "tags": [], 7 | "readOnly": false, 8 | "archived": false 9 | }, 10 | "implementation": { 11 | "type": "file", 12 | "format": "jpad" 13 | }, 14 | "valueType": "string", 15 | "dependencies": ["behavior_tests/dependent_keys/fail/second"] 16 | } 17 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/manifests/behavior_tests/dependent_keys/fail/second.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "behavior_tests/dependent_keys/fail/second", 3 | "meta": { 4 | "name": "behavior_tests/dependent_keys/fail/second", 5 | "description": "", 6 | "tags": [], 7 | "readOnly": false, 8 | "archived": false 9 | }, 10 | "implementation": { 11 | "type": "file", 12 | "format": "jpad" 13 | }, 14 | "valueType": "string", 15 | "dependencies": ["behavior_tests/dependent_keys/fail/third"] 16 | } 17 | -------------------------------------------------------------------------------- /services/publishing/base-repo/manifests/@tweek/custom_types/version.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "@tweek/custom_types/version", 3 | "meta": { 4 | "name": "@tweek/custom_types/version", 5 | "description": "", 6 | "readOnly": false, 7 | "archived": false 8 | }, 9 | "implementation": { 10 | "type": "const", 11 | "value": { 12 | "base": "string", 13 | "comparer": "version", 14 | "validation": "^[0-9]+(\\.[0-9]+){1,3}$" 15 | } 16 | }, 17 | "valueType": "object", 18 | "dependencies": [] 19 | } 20 | -------------------------------------------------------------------------------- /services/editor/src/utils/hooks/useSearchConfig.ts: -------------------------------------------------------------------------------- 1 | import { useTweekValue } from 'react-tweek'; 2 | 3 | export const useMaxSearchResults = useTweekValue.create('@tweek/editor/search/max_results', 25); 4 | export const useShowInternalKeys = useTweekValue.create('@tweek/editor/show_internal_keys', false); 5 | export const useHistorySince = useTweekValue.create('@tweek/editor/history/since', '1 month ago'); 6 | export const useEnableCardsView = useTweekValue.create( 7 | '@tweek/editor/experimental/keys_search/enable_cards_view', 8 | false, 9 | ); 10 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/manifests/@tweek/auth/tweek_editor_user/write_context.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "@tweek/auth/tweek_editor_user/write_context", 3 | "meta": { 4 | "name": "@tweek/auth/tweek_editor_user/write_context", 5 | "description": "Determines permissions to write context", 6 | "tags": [], 7 | "readOnly": false, 8 | "archived": false 9 | }, 10 | "implementation": { 11 | "type": "const", 12 | "value": false 13 | }, 14 | "valueType": "boolean", 15 | "dependencies": [] 16 | } 17 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/manifests/@tweek_clients_tests/test_category/test_key2.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "@tweek_clients_tests/test_category/test_key2", 3 | "meta": { 4 | "name": "@tweek_clients_tests/test_category/test_key2", 5 | "description": "e2e", 6 | "tags": ["test", "do not delete or change"], 7 | "readOnly": false, 8 | "archived": false 9 | }, 10 | "implementation": { 11 | "type": "file", 12 | "format": "jpad" 13 | }, 14 | "valueType": "boolean", 15 | "dependencies": [] 16 | } 17 | -------------------------------------------------------------------------------- /core/Engine/Tweek.Engine.Tests/TestDrivers/ITestDriver.cs: -------------------------------------------------------------------------------- 1 | using FSharpUtils.Newtonsoft; 2 | using System.Collections.Generic; 3 | using Tweek.Engine.DataTypes; 4 | using Tweek.Engine.Drivers.Context; 5 | using Tweek.Engine.Drivers.Rules; 6 | 7 | namespace Tweek.Engine.Tests.TestDrivers 8 | { 9 | public interface ITestDriver 10 | { 11 | IContextDriver Context { get; } 12 | TestScope SetTestEnvironment(Dictionary> contexts, string[] keys, Dictionary rules); 13 | } 14 | } -------------------------------------------------------------------------------- /services/editor/src/styles/styles.less: -------------------------------------------------------------------------------- 1 | @import (reference) './core/core.less'; 2 | * { 3 | padding: 0; 4 | margin: 0; 5 | font-family: 'Roboto', sans-serif; 6 | } 7 | 8 | .select-key-message { 9 | .centered-message; 10 | } 11 | 12 | h3 { 13 | font-size: 22px; 14 | color: #515c66; 15 | margin-left: 4px; 16 | margin-bottom: 4px; 17 | } 18 | 19 | .validation-icon-wrapper { 20 | i { 21 | background-image: url('../resources/alert-icon.svg'); 22 | width: 16px; 23 | height: 16px; 24 | display: inline-block; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/source/manifests/@tweek/custom_types/version.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "@tweek/custom_types/version", 3 | "meta": { 4 | "name": "@tweek/custom_types/version", 5 | "description": "", 6 | "readOnly": false, 7 | "archived": false 8 | }, 9 | "implementation": { 10 | "type": "const", 11 | "value": { 12 | "base": "string", 13 | "comparer": "version", 14 | "validation": "^[0-9]+(\\.[0-9]+){1,3}$" 15 | } 16 | }, 17 | "valueType": "object", 18 | "dependencies": [] 19 | } 20 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/implementations/jpad/integration_tests/include_errors.jpad: -------------------------------------------------------------------------------- 1 | { 2 | "partitions": [], 3 | "valueType": "string", 4 | "rules": [ 5 | { 6 | "Matcher": { 7 | "user.BadProperty": [] 8 | }, 9 | "Value": "BadPropertyValue", 10 | "Type": "SingleVariant" 11 | }, 12 | { 13 | "Matcher": {}, 14 | "Value": "DefaultValue", 15 | "Type": "SingleVariant" 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/manifests/@tweek_clients_tests/test_category/test_key1.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "@tweek_clients_tests/test_category/test_key1", 3 | "meta": { 4 | "name": "@tweek_clients_tests/test_category/test_key1", 5 | "description": "e2e test", 6 | "tags": ["test", "do not delete or change"], 7 | "readOnly": false, 8 | "archived": false 9 | }, 10 | "implementation": { 11 | "type": "file", 12 | "format": "jpad" 13 | }, 14 | "valueType": "string", 15 | "dependencies": [] 16 | } 17 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/manifests/@tweek_clients_tests/test_category2/user_fruit.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "@tweek_clients_tests/test_category2/user_fruit", 3 | "meta": { 4 | "name": "@tweek_clients_tests/test_category2/user_fruit", 5 | "description": "e2e", 6 | "tags": ["test", "do not delete or change"], 7 | "readOnly": false, 8 | "archived": false 9 | }, 10 | "implementation": { 11 | "type": "file", 12 | "format": "jpad" 13 | }, 14 | "valueType": "string", 15 | "dependencies": [] 16 | } 17 | -------------------------------------------------------------------------------- /core/Tweek.ApiService.Addons/ITweekAddon.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using Microsoft.Extensions.Configuration; 3 | using Microsoft.Extensions.DependencyInjection; 4 | using System; 5 | using Microsoft.Extensions.Logging; 6 | using Tweek.Engine.Drivers.Context; 7 | 8 | namespace Tweek.ApiService.Addons 9 | { 10 | public interface ITweekAddon 11 | { 12 | void Use(IApplicationBuilder builder, IConfiguration configuration); 13 | void Configure(IServiceCollection services, IConfiguration configuration); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /deployments/kubernetes/infra/nats.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: extensions/v1beta1 2 | kind: Deployment 3 | metadata: 4 | name: nats 5 | spec: 6 | replicas: 1 7 | template: 8 | metadata: 9 | labels: 10 | app: nats 11 | spec: 12 | containers: 13 | - name: nats 14 | image: nats:1.0.4-linux 15 | ports: 16 | - containerPort: 4222 17 | --- 18 | kind: Service 19 | apiVersion: v1 20 | metadata: 21 | name: nats 22 | spec: 23 | selector: 24 | app: nats 25 | ports: 26 | - port: 4222 27 | targetPort: 4222 -------------------------------------------------------------------------------- /e2e/ui/pages/Keys/TypedInput/ObjectInput.js: -------------------------------------------------------------------------------- 1 | import { t } from 'testcafe'; 2 | import { dataComp } from '../../../utils/selector-utils'; 3 | import Alert from '../../Alert'; 4 | import ObjectEditor from '../ObjectEditor'; 5 | 6 | export default class ObjectInput { 7 | constructor(container) { 8 | this.editObjectButton = container.find(dataComp('object-editor')); 9 | this.alert = new Alert(container); 10 | } 11 | 12 | async editObject() { 13 | await t.click(this.editObjectButton); 14 | 15 | return new ObjectEditor(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /services/editor/src/components/GoogleTagManager.ts: -------------------------------------------------------------------------------- 1 | import { useEffect } from 'react'; 2 | import TagManager from 'react-gtm-module'; 3 | import { useTweekValue } from 'react-tweek'; 4 | 5 | export const useGoogleTagManager = () => { 6 | const gtmId = useTweekValue('@tweek/editor/google_tag_manager/id', ''); 7 | const isEnabled = useTweekValue('@tweek/editor/google_tag_manager/enabled', false); 8 | 9 | useEffect(() => { 10 | if (isEnabled && gtmId) { 11 | TagManager.initialize({ gtmId }); 12 | } 13 | }, [gtmId, isEnabled]); 14 | }; 15 | -------------------------------------------------------------------------------- /services/editor/src/pages/settings/components/PoliciesPage/JWTExtraction.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { tweekManagementClient } from '../../../../utils/tweekClients'; 3 | import RemoteCodeEditor from './RemoteCodeEditor'; 4 | 5 | export default function JWTPolicyEditor() { 6 | return ( 7 | tweekManagementClient.getJWTExtractionPolicy()} 11 | writer={(x) => tweekManagementClient.saveJWTExtractionPolicy(x)} 12 | /> 13 | ); 14 | } 15 | -------------------------------------------------------------------------------- /deployments/kubernetes/infra/redis.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: extensions/v1beta1 2 | kind: Deployment 3 | metadata: 4 | name: redis 5 | spec: 6 | replicas: 1 7 | template: 8 | metadata: 9 | labels: 10 | app: redis 11 | spec: 12 | containers: 13 | - name: redis 14 | image: redis:alpine 15 | ports: 16 | - containerPort: 6379 17 | --- 18 | kind: Service 19 | apiVersion: v1 20 | metadata: 21 | name: redis 22 | spec: 23 | selector: 24 | app: redis 25 | ports: 26 | - port: 6379 27 | targetPort: 6379 -------------------------------------------------------------------------------- /services/editor/src/components/JPadFullEditor/JPadVisualEditor/Rule/DefaultValue.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { TypedInput } from '../../../common'; 3 | import { TypedInputProps } from '../../../common/Input/TypedInput'; 4 | import './DefaultValue.css'; 5 | 6 | const DefaultValue = (props: TypedInputProps) => ( 7 |
8 | 9 | 10 |
11 | ); 12 | 13 | export default DefaultValue; 14 | -------------------------------------------------------------------------------- /services/editor/src/pages/keys/components/utils/keyFormatHelpers.ts: -------------------------------------------------------------------------------- 1 | import { KeyImplementation } from 'tweek-client'; 2 | 3 | export const validKeyFormats = ['const', 'jpad']; 4 | 5 | export const getManifestImplementationByFormat = (format: string): KeyImplementation => { 6 | switch (format) { 7 | case 'const': 8 | return { type: 'const', format: undefined, value: '' }; 9 | case 'jpad': 10 | return { type: 'file', format: 'jpad', value: undefined }; 11 | default: 12 | throw new Error(`Invalid format ${format}`); 13 | } 14 | }; 15 | -------------------------------------------------------------------------------- /services/editor/src/services/auth/clients/storage.ts: -------------------------------------------------------------------------------- 1 | const getStorage = (): Pick => { 2 | if (typeof localStorage === 'undefined') { 3 | const store: Record = {}; 4 | return { 5 | setItem: (itemKey, itemValue) => { 6 | store[itemKey] = itemValue; 7 | }, 8 | getItem: (itemKey) => store[itemKey], 9 | removeItem: (itemKey) => delete store[itemKey], 10 | }; 11 | } else { 12 | return localStorage; 13 | } 14 | }; 15 | 16 | export default getStorage(); 17 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/manifests/@tweek/custom_types/version.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "@tweek/custom_types/version", 3 | "meta": { 4 | "name": "@tweek/custom_types/version", 5 | "description": "", 6 | "readOnly": false, 7 | "archived": false 8 | }, 9 | "implementation": { 10 | "type": "const", 11 | "value": { 12 | "base": "string", 13 | "comparer": "version", 14 | "validation": "^[0-9]+(\\.[0-9]+){1,3}$" 15 | } 16 | }, 17 | "valueType": "object", 18 | "dependencies": [] 19 | } 20 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | "[javascript]": { 4 | "editor.tabSize": 2 5 | }, 6 | "[csharp]": { 7 | "editor.tabSize": 4 8 | }, 9 | "workbench.editor.enablePreview": false, 10 | "workbench.editor.enablePreviewFromQuickOpen": false, 11 | "jest.pathToJest": "./services/editor/node_modules/.bin/jest", 12 | "eslint.enable": true, 13 | "csharp.testsCodeLens.enabled": true, 14 | "FSharp.fsacRuntime": "netcore", 15 | "dotnet-test-explorer.showCodeLens": true 16 | } 17 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/implementations/jpad/smoke_tests/value_distribution/weighted_normalized.jpad: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "Id": "98e76a52-c7d8-59d8-86de-b957938e5f87", 4 | "Matcher": {}, 5 | "Type": "MultiVariant", 6 | "OwnerType": "device", 7 | "ValueDistribution": { 8 | "type": "weighted", 9 | "args": { 10 | "test1": 25, 11 | "test2": 25, 12 | "test3": 25, 13 | "test4": 25 14 | } 15 | } 16 | } 17 | ] -------------------------------------------------------------------------------- /addons/Context/Tweek.Drivers.Context.Redis.IntegrationTests/RedisIntegrationTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Tweek.Engine.Drivers.Context; 3 | 4 | namespace Tweek.Drivers.Context.Redis.IntegrationTests 5 | { 6 | public class RedisIntegrationTests: ContextIntegrationTests.IntegrationTests 7 | { 8 | public RedisIntegrationTests() 9 | { 10 | Driver = new RedisDriver(Environment.GetEnvironmentVariable("REDIS_TEST_CONNECTION")); 11 | } 12 | 13 | protected sealed override IContextDriver Driver { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /core/Engine/Tweek.Engine.DataTypes/Identity.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Tweek.Engine.DataTypes 4 | { 5 | public class Identity: Tuple 6 | { 7 | public string Type => Item1; 8 | public string Id => Item2; 9 | 10 | public Identity(string type, string id) 11 | : base(type.ToLower(), id.ToLower()) 12 | { 13 | } 14 | 15 | public const string GlobalIdentityType = "@global"; 16 | public static readonly Identity GlobalIdentity = new Identity(GlobalIdentityType, ""); 17 | } 18 | } -------------------------------------------------------------------------------- /core/Engine/Tweek.Engine/app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/manifests/behavior_tests/dependent_keys/display/depends_on.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "behavior_tests/dependent_keys/display/depends_on", 3 | "meta": { 4 | "name": "behavior_tests/dependent_keys/display/depends_on", 5 | "description": "", 6 | "tags": [], 7 | "readOnly": false, 8 | "archived": false 9 | }, 10 | "implementation": { 11 | "type": "file", 12 | "format": "jpad" 13 | }, 14 | "valueType": "string", 15 | "dependencies": ["behavior_tests/dependent_keys/display/used_by"] 16 | } 17 | -------------------------------------------------------------------------------- /core/Engine/Tweek.Engine.Core/Tweek.Engine.Core.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | netstandard2.1 4 | 1591, 1701, 1702, 1998 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /core/Engine/Tweek.Engine.Core/app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /services/authoring/src/utils/requestLogger.ts: -------------------------------------------------------------------------------- 1 | import morgan from 'morgan'; 2 | 3 | morgan.token('level', (req, res) => { 4 | const status = res.statusCode; 5 | if (status >= 500) { 6 | return '50'; 7 | } 8 | 9 | if (status >= 400) { 10 | return '40'; 11 | } 12 | 13 | return '30'; 14 | }); 15 | 16 | const messageFormat = 17 | '{"level"::level,"msg":"request handling result","method":":method","url":":url","status":":status","content-length":":res[content-length]","response-time-ms":":response-time"}'; 18 | 19 | export default morgan(messageFormat); 20 | -------------------------------------------------------------------------------- /addons/Context/Tweek.Drivers.Context.MongoDb.IntegrationTests/MongoDbIntegrationTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Tweek.Engine.Drivers.Context; 3 | 4 | namespace Tweek.Drivers.Context.MongoDb.IntegrationTests 5 | { 6 | public class MongoDbIntegrationTests: ContextIntegrationTests.IntegrationTests 7 | { 8 | public MongoDbIntegrationTests() 9 | { 10 | Driver = new MongoDbDriver(Environment.GetEnvironmentVariable("MONGODB_TEST_CONNECTION")); 11 | } 12 | 13 | protected sealed override IContextDriver Driver { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /docs/assets/openapi.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Tweek OpenApi 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /addons/Rules/Tweek.Drivers.Rules.FileSystem/Tweek.Drivers.Rules.FileSystem.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | netstandard2.1 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/manifests/behavior_tests/dependent_keys/display/depends_on_alias.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "behavior_tests/dependent_keys/display/depends_on_alias", 3 | "meta": { 4 | "name": "behavior_tests/dependent_keys/display/depends_on_alias", 5 | "description": "", 6 | "tags": [], 7 | "readOnly": false, 8 | "archived": false 9 | }, 10 | "implementation": { 11 | "type": "file", 12 | "format": "jpad" 13 | }, 14 | "valueType": "string", 15 | "dependencies": ["behavior_tests/dependent_keys/display/alias_key"] 16 | } 17 | -------------------------------------------------------------------------------- /e2e/ui/pages/Keys/Rules/NewRule.js: -------------------------------------------------------------------------------- 1 | import { Selector, t } from 'testcafe'; 2 | import { dataComp } from '../../../utils/selector-utils'; 3 | import Condition from './Condition'; 4 | import Rule from './Rule'; 5 | 6 | export default class NewRule { 7 | addButton = Selector(dataComp('add-rule')); 8 | 9 | async add() { 10 | await t.click(this.addButton); 11 | const rule = new Rule(); 12 | 13 | await t 14 | .expect(new Condition(rule).propertyInput.focused) 15 | .ok('should focus the added rule first condition property name'); 16 | 17 | return rule; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /services/editor/src/resources/archive-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /services/gateway/audit/auditor.go: -------------------------------------------------------------------------------- 1 | package audit 2 | 3 | // Auditor is the interface which defines auditing 4 | type Auditor interface { 5 | // Allowed sends indication that the action was allowed 6 | Allowed(subject, object, action string) 7 | // Denied sends indication that the action was denied 8 | Denied(subject, object, action string) 9 | // AuthorizerError sends indication that the authorization failed for technical reasons 10 | AuthorizerError(subject, object, action string, err error) 11 | // TokenError sends indication that user supplied invalid token 12 | TokenError(err error) 13 | } 14 | -------------------------------------------------------------------------------- /core/Engine/Tweek.Engine.Core/Context/Context.cs: -------------------------------------------------------------------------------- 1 | using FSharpUtils.Newtonsoft; 2 | using LanguageExt; 3 | using Tweek.Engine.DataTypes; 4 | 5 | namespace Tweek.Engine.Core.Context 6 | { 7 | public delegate Option GetContextValue(string key); 8 | 9 | //utils 10 | public delegate GetContextValue GetLoadedContextByIdentity(Identity identity); 11 | public delegate GetContextValue GetLoadedContextByIdentityType(string identityType); 12 | 13 | //move to extension 14 | public delegate Option GetContextFixedConfigurationValue(ConfigurationPath path); 15 | } 16 | -------------------------------------------------------------------------------- /core/Engine/Tweek.Engine/ITweek.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Tweek.Engine.Core.Context; 3 | using Tweek.Engine.DataTypes; 4 | using IdentityHashSet = System.Collections.Generic.HashSet; 5 | 6 | namespace Tweek.Engine 7 | { 8 | public interface ITweek 9 | { 10 | Dictionary Calculate( 11 | ICollection pathQuery, 12 | IdentityHashSet identities, GetLoadedContextByIdentityType context, 13 | ConfigurationPath[] includeFixedPaths = null); 14 | } 15 | } -------------------------------------------------------------------------------- /services/editor/src/components/ConstEditor.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { ValueType } from 'tweek-client'; 3 | import { TypedInput } from './common'; 4 | 5 | export type ConstEditorProps = { 6 | value: any; 7 | valueType: string | ValueType; 8 | onChange: (value: any) => void; 9 | }; 10 | 11 | const ConstEditor = ({ value, valueType, onChange }: ConstEditorProps) => ( 12 |
13 | 14 |
15 | ); 16 | 17 | export default ConstEditor; 18 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/manifests/@tweek/schema/test.json: -------------------------------------------------------------------------------- 1 | { 2 | "key_path": "@tweek/schema/test", 3 | "meta": { 4 | "description": "", 5 | "readOnly": false, 6 | "archived": false 7 | }, 8 | "implementation": { 9 | "type": "const", 10 | "value": { 11 | "FavoriteFruit": { 12 | "type": "string" 13 | }, 14 | "NickName": { 15 | "type": "string" 16 | }, 17 | "Age": { 18 | "type": "number" 19 | } 20 | } 21 | }, 22 | "valueType": "object", 23 | "enabled": true, 24 | "dependencies": [] 25 | } 26 | -------------------------------------------------------------------------------- /services/editor/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "esModuleInterop": true, 8 | "allowSyntheticDefaultImports": true, 9 | "strict": true, 10 | "forceConsistentCasingInFileNames": true, 11 | "module": "esnext", 12 | "moduleResolution": "node", 13 | "resolveJsonModule": true, 14 | "isolatedModules": true, 15 | "noEmit": true, 16 | "jsx": "react-jsx", 17 | "noFallthroughCasesInSwitch": true 18 | }, 19 | "include": ["src"] 20 | } 21 | -------------------------------------------------------------------------------- /services/publishing/Tweek.Publishing.Service/Validation/Patterns.cs: -------------------------------------------------------------------------------- 1 | namespace Tweek.Publishing.Service.Validation 2 | { 3 | public static class Patterns 4 | { 5 | public static readonly string Manifests = "^manifests/.*\\.json$"; 6 | public static readonly string JPad = "^implementations/jpad/.*\\.jpad$"; 7 | public static readonly string ExternalApp = "^external_apps/(.+)\\.json$"; 8 | public static readonly string Policy = "^security/policy.json$"; 9 | public static readonly string SubjectExtractionRules = "^security/subject_extraction_rules.rego$"; 10 | } 11 | } -------------------------------------------------------------------------------- /deployments/kubernetes/editor.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: extensions/v1beta1 2 | kind: Deployment 3 | metadata: 4 | name: editor 5 | spec: 6 | replicas: 1 7 | template: 8 | metadata: 9 | labels: 10 | app: editor 11 | spec: 12 | containers: 13 | - name: editor 14 | image: soluto/tweek-editor 15 | imagePullPolicy: IfNotPresent 16 | ports: 17 | - containerPort: 3000 18 | --- 19 | kind: Service 20 | apiVersion: v1 21 | metadata: 22 | name: editor 23 | spec: 24 | selector: 25 | app: editor 26 | ports: 27 | - port: 3000 28 | targetPort: 3000 29 | -------------------------------------------------------------------------------- /e2e/integration/spec/tweek-api/key-aliases.test.js: -------------------------------------------------------------------------------- 1 | const { expect } = require('chai'); 2 | const client = require('../../utils/client'); 3 | 4 | const originalKey = 'integration_tests/some_key'; 5 | const aliasKey = 'integration_tests/alias_key'; 6 | 7 | describe('tweek api - key aliases', () => { 8 | it('should have the same value as the original key', async () => { 9 | const original = await client.get(`/api/v2/values/${originalKey}`); 10 | const alias = await client.get(`/api/v2/values/${aliasKey}`); 11 | 12 | expect(JSON.parse(original.body)).to.equal(JSON.parse(alias.body)); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /tweek.code-workspace: -------------------------------------------------------------------------------- 1 | { 2 | "folders": [ 3 | { 4 | "path": "." 5 | }, 6 | { 7 | "name": "services/gateway", 8 | "path": "services/gateway" 9 | }, 10 | { 11 | "name": "services/editor", 12 | "path": "services/editor" 13 | }, 14 | { 15 | "name": "services/authoring", 16 | "path": "services/authoring" 17 | }, 18 | { 19 | "name": "services/publishing", 20 | "path": "services/publishing" 21 | } 22 | ], 23 | "settings": { 24 | "workbench.editor.enablePreviewFromQuickOpen": false, 25 | "csharp.testsCodeLens.enabled": true, 26 | "FSharp.fsacRuntime": "netcore" 27 | } 28 | } -------------------------------------------------------------------------------- /services/editor/src/components/common/__snapshots__/ErrorHandler.spec.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`ErrorHandler component should not render when exception is thrown and no error message is passed 1`] = `null`; 4 | 5 | exports[`ErrorHandler component should render error message when exception is thrown 1`] = ` 6 |
7 | some error message 8 |
9 | `; 10 | 11 | exports[`ErrorHandler component should render original component correctly 1`] = ` 12 | Array [ 13 |
14 | some element 15 |
, 16 |
17 | another element 18 |
, 19 | ] 20 | `; 21 | -------------------------------------------------------------------------------- /services/gateway/corsSupport/corsSupport.go: -------------------------------------------------------------------------------- 1 | package corsSupport 2 | 3 | import ( 4 | "tweek-gateway/appConfig" 5 | "github.com/rs/cors" 6 | "github.com/urfave/negroni" 7 | ) 8 | 9 | // New creates and configures CORS support middlware 10 | func New(config *appConfig.Cors) negroni.Handler { 11 | 12 | if !config.Enabled { 13 | return nil 14 | } 15 | 16 | return cors.New(cors.Options{ 17 | MaxAge: config.MaxAge, 18 | AllowedOrigins: config.AllowedOrigins, 19 | AllowedHeaders: config.AllowedHeaders, 20 | AllowedMethods: config.AllowedMethods, 21 | AllowCredentials: true, 22 | }) 23 | } 24 | -------------------------------------------------------------------------------- /e2e/ui/pages/Context/Property.js: -------------------------------------------------------------------------------- 1 | import { Selector, t } from 'testcafe'; 2 | import { dataComp, dataField } from '../../utils/selector-utils'; 3 | 4 | export default class Property { 5 | constructor(property) { 6 | this.container = Selector(dataComp('identity-property')).withAttribute( 7 | 'data-property', 8 | property, 9 | ); 10 | this.valueInput = this.container.find(dataField('value')); 11 | } 12 | 13 | async update(value) { 14 | await t 15 | .expect(this.valueInput.disabled) 16 | .notOk() 17 | .typeText(this.valueInput, value.toString(), { replace: true }); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /services/publishing/Tweek.Publishing.Service/Validation/ManifestStructureException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Tweek.Publishing.Service.Validation 4 | { 5 | public class ManifestStructureException : Exception 6 | { 7 | public ManifestStructureException(string path, Exception innerException = null) 8 | : base("Manifest structure is invalid", innerException) 9 | { 10 | Data["Path"] = path; 11 | } 12 | 13 | public ManifestStructureException(string path, string message) : base(message) 14 | { 15 | Data["Path"] = path; 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /addons/Rules/Tweek.Drivers.Rules.Minio/Tweek.Drivers.Rules.Minio.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | netstandard2.1 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /core/Engine/Tweek.Engine.Core/Utils/OptionHelpers.cs: -------------------------------------------------------------------------------- 1 | using LanguageExt; 2 | using System; 3 | using System.Threading.Tasks; 4 | 5 | namespace Tweek.Engine.Core.Utils 6 | { 7 | public static class OptionHelpers 8 | { 9 | public static Option IfNone(this Option option, Func> altFunc) 10 | { 11 | return option.MatchUnsafe(x => x, altFunc); 12 | } 13 | 14 | public static Task> IfNoneAsync(this Option option, Func>> altFunc) 15 | { 16 | return option.MatchAsync(async x => (Option)x, altFunc); 17 | } 18 | 19 | } 20 | } -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/implementations/jpad/integration_tests/value_distribution/object_format.jpad: -------------------------------------------------------------------------------- 1 | { 2 | "partitions": [], 3 | "valueType": "number", 4 | "rules": [ 5 | { 6 | "Matcher": {}, 7 | "Type": "MultiVariant", 8 | "OwnerType": "user", 9 | "ValueDistribution": { 10 | "type": "weighted", 11 | "args": { 12 | "15": 100, 13 | "30": 0 14 | } 15 | }, 16 | "Salt": "integration_tests/value_distribution/object_format" 17 | } 18 | ] 19 | } -------------------------------------------------------------------------------- /services/editor/src/pages/context/components/ContextPage/ContextPage.less: -------------------------------------------------------------------------------- 1 | .context-page-container { 2 | background-color: #eee; 3 | width: 100%; 4 | display: flex; 5 | flex-direction: column; 6 | align-items: center; 7 | overflow-y: auto; 8 | 9 | .context-page { 10 | background-color: white; 11 | border-radius: 5px; 12 | border: 1px solid lightgray; 13 | padding: 15px; 14 | box-sizing: content-box; 15 | margin: 20px; 16 | display: block; 17 | width: 900px; 18 | 19 | .horizontal-separator { 20 | background: lightgrey; 21 | height: 1px; 22 | margin: 20px 0; 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /addons/Rules/Tweek.Drivers.Rules.Minio/ConfigurationExtention.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using Microsoft.Extensions.Configuration; 3 | 4 | namespace Tweek.Drivers.Rules.Minio 5 | { 6 | public static class ConfigurationExtention 7 | { 8 | public static string GetValueFromEnvOrFile(this IConfiguration configuration, string inlineKey, string fileKey) 9 | { 10 | var result = configuration[inlineKey]; 11 | if (!string.IsNullOrEmpty(result)) return result; 12 | 13 | var file = configuration[fileKey]; 14 | return File.Exists(file) ? File.ReadAllText(file) : null; 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /services/api/Tweek.ApiService/Utils/TimerOptionsExtentions.cs: -------------------------------------------------------------------------------- 1 | using App.Metrics; 2 | using App.Metrics.Timer; 3 | 4 | namespace Tweek.ApiService.Utils 5 | { 6 | public static class TimerOptionsExtentions 7 | { 8 | public static TimerOptions GetTimer(this string timerContext, string name) 9 | { 10 | return new TimerOptions 11 | { 12 | Context = timerContext, 13 | Name = name, 14 | MeasurementUnit = Unit.None, 15 | DurationUnit = TimeUnit.Milliseconds, 16 | RateUnit = TimeUnit.Seconds, 17 | }; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /e2e/ui/spec/keys/readonly-key.js: -------------------------------------------------------------------------------- 1 | import { editorUrl } from '../../utils/constants'; 2 | import { credentials, login } from '../../utils/auth-utils'; 3 | import EditKey from '../../pages/Keys/EditKey'; 4 | 5 | const testKeyFullPath = 'behavior_tests/read_only'; 6 | 7 | fixture`Readonly Key`.page`${editorUrl}/keys`.httpAuth(credentials).beforeEach(login); 8 | 9 | test('should open the key as readonly', async (t) => { 10 | const editKey = await EditKey.open(testKeyFullPath); 11 | 12 | await t 13 | .expect(editKey.messageText.visible) 14 | .ok() 15 | .expect(editKey.jpad.container.find('fieldset').withAttribute('disabled').exists) 16 | .ok(); 17 | }); 18 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/implementations/jpad/@tweek_clients_tests/test_category/test_key1.jpad: -------------------------------------------------------------------------------- 1 | { 2 | "partitions": [], 3 | "valueType": "string", 4 | "rules": [ 5 | { 6 | "Id": "cb310798-17b7-5412-8010-46cc86fed5d0", 7 | "Matcher": { 8 | "device.DeviceOsType": "Ios" 9 | }, 10 | "Value": "ios value", 11 | "Type": "SingleVariant" 12 | }, 13 | { 14 | "Id": "93b5538a-e7d4-5e47-9b05-bfd465392fc5", 15 | "Matcher": {}, 16 | "Value": "def value", 17 | "Type": "SingleVariant" 18 | } 19 | ] 20 | } -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/implementations/jpad/@tweek_clients_tests/test_category/test_key2.jpad: -------------------------------------------------------------------------------- 1 | { 2 | "partitions": [], 3 | "valueType": "boolean", 4 | "rules": [ 5 | { 6 | "Id": "c820d23b-9581-5ce1-9780-df85b2dc6278", 7 | "Matcher": { 8 | "device.PartnerBrandId": "testPartner" 9 | }, 10 | "Value": true, 11 | "Type": "SingleVariant" 12 | }, 13 | { 14 | "Id": "9d909c0c-089d-5c48-aee2-adc8bc641104", 15 | "Matcher": {}, 16 | "Value": false, 17 | "Type": "SingleVariant" 18 | } 19 | ] 20 | } -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/implementations/jpad/@tweek_clients_tests/test_category2/user_fruit.jpad: -------------------------------------------------------------------------------- 1 | { 2 | "partitions": [], 3 | "valueType": "string", 4 | "rules": [ 5 | { 6 | "Id": "ef0bc1ef-f4c3-5198-a59d-930efe2d569a", 7 | "Matcher": { 8 | "device.DeviceType": "Desktop" 9 | }, 10 | "Value": "orange", 11 | "Type": "SingleVariant" 12 | }, 13 | { 14 | "Id": "9915db32-2a8c-5110-a518-94878ed19541", 15 | "Matcher": {}, 16 | "Value": "apple", 17 | "Type": "SingleVariant" 18 | } 19 | ] 20 | } -------------------------------------------------------------------------------- /e2e/ui/spec/login/redirect-to-login-page.js: -------------------------------------------------------------------------------- 1 | import { Selector } from 'testcafe'; 2 | import { editorUrl } from '../../utils/constants'; 3 | import { dataComp } from '../../utils/selector-utils'; 4 | import { getLocation } from '../../utils/location-utils'; 5 | 6 | const tweekLogo = Selector(dataComp('tweek-logo')); 7 | const basicAuthLink = Selector(dataComp('@@tweek-basic-auth')); 8 | 9 | fixture`Login Page`.page`${editorUrl}`; 10 | 11 | test('should navigate to login page', async (t) => { 12 | await t 13 | .expect(getLocation()) 14 | .eql(`${editorUrl}/login`) 15 | .expect(tweekLogo.visible) 16 | .ok() 17 | .expect(basicAuthLink.visible) 18 | .ok(); 19 | }); 20 | -------------------------------------------------------------------------------- /services/api/Tweek.ApiService/Controllers/RepoVersionController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Cors; 2 | using Microsoft.AspNetCore.Mvc; 3 | using Tweek.ApiService.Security; 4 | using Tweek.Engine.Drivers.Rules; 5 | 6 | namespace Tweek.ApiService.Controllers 7 | { 8 | public class RepoVersionController : Controller 9 | { 10 | private readonly IRulesRepository mRulesRepository; 11 | 12 | public RepoVersionController(IRulesRepository rulesRepository) 13 | { 14 | mRulesRepository = rulesRepository; 15 | } 16 | 17 | [HttpGet("api/v1/repo-version")] 18 | public string Get() => mRulesRepository.CurrentLabel; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /e2e/ui/spec/login/authorization.js: -------------------------------------------------------------------------------- 1 | import { editorUrl } from '../../utils/constants'; 2 | import { login } from '../../utils/auth-utils'; 3 | import { getLocation } from '../../utils/location-utils'; 4 | import { Selector } from 'testcafe'; 5 | import { dataComp } from '../../utils/selector-utils'; 6 | 7 | const errorMessage = Selector(dataComp('error-message')); 8 | 9 | fixture`Authorization error`.page`${editorUrl}`; 10 | 11 | test.skip('no permissions to access tweek', async (t) => { 12 | await login(t, { username: 'user2', password: 'pwd' }); 13 | 14 | await t 15 | .expect(getLocation()) 16 | .notEql(`${editorUrl}/login`) 17 | .expect(errorMessage.visible) 18 | .ok(); 19 | }); 20 | -------------------------------------------------------------------------------- /services/authoring/src/security/strategies/tweek-internal.ts: -------------------------------------------------------------------------------- 1 | import { Strategy as JwtStrategy, ExtractJwt } from 'passport-jwt'; 2 | 3 | const tweekApp = { 4 | version: '1', 5 | name: 'tweek', 6 | isTweekService: true, 7 | permissions: '*', 8 | }; 9 | 10 | export default class TweekInternalStrategy extends JwtStrategy { 11 | readonly name = 'tweek-internal'; 12 | constructor(publicKey) { 13 | super( 14 | { 15 | secretOrKey: publicKey, 16 | issuer: 'tweek', 17 | algorithms: ['RS256'], 18 | jwtFromRequest: ExtractJwt.fromAuthHeaderWithScheme('Bearer'), 19 | }, 20 | (jwt_payload, done) => done(null, tweekApp), 21 | ); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /services/api/Tweek.ApiService.SmokeTests/GetConfigurations/Models/TestContext.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using FSharpUtils.Newtonsoft; 3 | using static FSharpUtils.Newtonsoft.JsonValue; 4 | 5 | namespace Tweek.ApiService.SmokeTests.GetConfigurations.Models 6 | { 7 | public class TestContext 8 | { 9 | public string TestName { get; set; } 10 | public string KeyName { get; set; } 11 | public string ExpectedValue { get; set; } 12 | public Dictionary Context { get; set; } = new Dictionary(); 13 | 14 | public override string ToString() 15 | { 16 | return TestName; 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /services/editor/src/components/JPadFullEditor/JPadVisualEditor/Rule/Rule.less: -------------------------------------------------------------------------------- 1 | @import (reference) '../../../../styles/core/core.less'; 2 | .rule-container { 3 | display: flex; 4 | padding: 10px; 5 | width: 100%; 6 | .transitioned(); 7 | &.default-value { 8 | background-color: #ebebeb; 9 | } 10 | .rule-partial-title { 11 | color: gray; 12 | font-size: 13px; 13 | margin-bottom: 5px; 14 | } 15 | .conditions { 16 | flex-basis: 60%; 17 | border-right: 1px solid lightgray; 18 | padding-right: 5px; 19 | } 20 | .values { 21 | margin-left: 10px; 22 | margin-left: 10px; 23 | display: flex; 24 | flex-grow: 1; 25 | flex-direction: column; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /services/editor/src/contexts/SelectedKey/localKeyStorage.ts: -------------------------------------------------------------------------------- 1 | import { KeyDefinition } from 'tweek-client'; 2 | 3 | const getStorageKey = (keyPath: string) => `@tweek:local-key:${keyPath}`; 4 | 5 | export const LocalKeyStorage = { 6 | set: (keyPath: string, key: KeyDefinition) => { 7 | localStorage.setItem(getStorageKey(keyPath), JSON.stringify(key)); 8 | }, 9 | get: (keyPath: string) => { 10 | const stored = localStorage.getItem(getStorageKey(keyPath)); 11 | try { 12 | return JSON.parse(stored!) as KeyDefinition; 13 | } catch { 14 | return undefined; 15 | } 16 | }, 17 | remove: (keyPath: string) => { 18 | localStorage.removeItem(getStorageKey(keyPath)); 19 | }, 20 | }; 21 | -------------------------------------------------------------------------------- /e2e/ui/pages/Keys/TypedInput/TagInput.js: -------------------------------------------------------------------------------- 1 | import { t } from 'testcafe'; 2 | 3 | export default class TagInput { 4 | constructor(selector) { 5 | this.container = selector.find('.tags-container'); 6 | this.input = this.container.find('.tag-input input'); 7 | this.deleteButton = this.container.find('.tag-delete-button'); 8 | } 9 | 10 | async add(item, paste) { 11 | await t 12 | .expect(this.input.visible) 13 | .ok() 14 | .typeText(this.input, item.toString(), { paste }); 15 | 16 | if (!paste) { 17 | await t.pressKey('enter'); 18 | } 19 | } 20 | 21 | async addMany(items) { 22 | for (const item of items) { 23 | await this.add(item); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /.devcontainer/install-all-deps: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env zx 2 | try 3 | { 4 | console.log(chalk.blue('Pulling Docker images')) 5 | await $`docker-compose -f deployments/dev/tilt.yml pull --parallel` 6 | console.log(chalk.blue('Installing packages for all projects')) 7 | await Promise.all([$`dotnet restore ./Tweek.sln`, $`cd services/editor && yarn`]) 8 | await Promise.all([$`cd services/publishing && dotnet restore ./Tweek.Publishing.sln`, $`cd services/authoring && yarn`]) 9 | await Promise.all([$`cd e2e/integration && yarn`, $`cd e2e/ui && yarn`, $`yarn`]) 10 | console.log(chalk.green('All packages installed')) 11 | } catch (ex){ 12 | console.log(chalk.red('Not all packages successfully loaded, try rerunning the script')) 13 | } -------------------------------------------------------------------------------- /e2e/ui/pages/Settings/Identity.js: -------------------------------------------------------------------------------- 1 | import { Selector, t } from 'testcafe'; 2 | import { dataComp } from '../../utils/selector-utils'; 3 | import NewProperty from './NewProperty'; 4 | import Property from './Property'; 5 | 6 | export default class Identity { 7 | newProperty = new NewProperty(); 8 | 9 | constructor(identityType) { 10 | this.container = Selector('.identity-page').withAttribute( 11 | 'data-identity-type', 12 | identityType.toLowerCase(), 13 | ); 14 | 15 | this.saveButton = this.container.find(dataComp('save-button')); 16 | this.deleteButton = this.container.find(dataComp('delete-identity')); 17 | } 18 | 19 | property(name) { 20 | return new Property(name); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /services/api/Tweek.ApiService.Tests/InMemoryContextServiceAddon.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using Microsoft.Extensions.Configuration; 3 | using Microsoft.Extensions.DependencyInjection; 4 | using Tweek.ApiService.Addons; 5 | using Tweek.Engine.Drivers.Context; 6 | 7 | namespace Tweek.ApiService.Tests 8 | { 9 | public class InMemoryContextServiceAddon : ITweekAddon 10 | { 11 | public void Use(IApplicationBuilder builder, IConfiguration configuration) 12 | { 13 | } 14 | 15 | public void Configure(IServiceCollection services, IConfiguration configuration) 16 | { 17 | services.AddSingleton(new InMemoryContext()); 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /services/publishing/Tweek.Publishing.Service/Storage/IObjectStorage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | 6 | namespace Tweek.Publishing.Service.Storage 7 | { 8 | public interface IObjectStorage 9 | { 10 | Task Get(string objectName, Action reader, CancellationToken cancellationToken = default); 11 | 12 | Task Put(string objectName, Action writer, string mimeType, CancellationToken cancellationToken = default); 13 | 14 | Task Delete(string objectName, CancellationToken cancellationToken = default); 15 | 16 | Task Exists(string objectName, CancellationToken cancellationToken = default); 17 | } 18 | } -------------------------------------------------------------------------------- /utils/generate_keys.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | openssl req -x509 -newkey rsa:4096 -keyout private-key.pem -out certificate.pem -days 365 -nodes -subj '/CN=tweek' 4 | chmod go-rwx *.pem 5 | ssh-keygen -y -f private-key.pem > public_ssh 6 | openssl pkcs12 -export -nodes -noiter -in certificate.pem -out certificate.pfx -nokeys -name "Tweek" -passout pass: 7 | 8 | mkdir -p ssh 9 | mv private-key.pem ssh/id_rsa # ssh private key 10 | mv public_ssh ssh/id_rsa.pub # ssh public key 11 | rm certificate.pem # use certificate.pfx 12 | echo Basic Authentication password: 13 | cat ssh/id_rsa | tr -d '\r' | base64 -w 0 | md5sum | cut -d " " -f1 \ 14 | | python -c 'import sys;print sys.stdin.read().rstrip("\r\n").decode("hex")' | base64 15 | 16 | -------------------------------------------------------------------------------- /services/editor/src/pages/settings/components/ExternalAppsPage/CreateExternalAppSecret.less: -------------------------------------------------------------------------------- 1 | @import '../../../../styles/core/core.less'; 2 | 3 | @small-font: 16px; 4 | 5 | .label-text() { 6 | all: unset; 7 | font-size: @small-font; 8 | margin: 0px 5px 0 0; 9 | color: gray; 10 | display: flex; 11 | white-space: nowrap; 12 | } 13 | 14 | .field-wrapper { 15 | display: flex; 16 | flex-grow: 1; 17 | flex-direction: row; 18 | margin-top: 5px; 19 | 20 | .field-label { 21 | .label-text(); 22 | align-items: center; 23 | padding-bottom: 1px; 24 | } 25 | 26 | .long-text { 27 | overflow-wrap: anywhere; 28 | } 29 | } 30 | 31 | .note { 32 | overflow-wrap: break-word; 33 | font-style: italic; 34 | } 35 | -------------------------------------------------------------------------------- /services/editor/src/utils/tweekClients.ts: -------------------------------------------------------------------------------- 1 | import { createTweekClient, createTweekManagementClient } from 'tweek-client'; 2 | import { getClient } from '../services/auth-service'; 3 | 4 | export const getGatewayBaseUrl = (): string => (window as any).GATEWAY_URL || ''; 5 | 6 | const config = { 7 | baseServiceUrl: getGatewayBaseUrl(), 8 | getAuthenticationToken: async (): Promise => { 9 | const client = getClient(); 10 | const token = await client?.getAuthToken(); 11 | return token || ''; 12 | }, 13 | }; 14 | 15 | export const tweekManagementClient = createTweekManagementClient({ 16 | ...config, 17 | requestTimeoutInMillis: 60 * 1000, 18 | }); 19 | 20 | export const tweekClient = createTweekClient(config); 21 | -------------------------------------------------------------------------------- /addons/Context/Tweek.Drivers.Context.InMemory/InMemoryServiceAddon.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using Microsoft.Extensions.Configuration; 3 | using Microsoft.Extensions.DependencyInjection; 4 | using Tweek.ApiService.Addons; 5 | using Tweek.Engine.Drivers.Context; 6 | 7 | namespace Tweek.Drivers.Context.InMemory 8 | { 9 | public class InMemoryServiceAddon : ITweekAddon 10 | { 11 | 12 | public void Use(IApplicationBuilder builder, IConfiguration configuration) 13 | { 14 | } 15 | 16 | public void Configure(IServiceCollection services, IConfiguration configuration) 17 | { 18 | services.AddSingleton(new InMemoryContext()); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /services/editor/src/contexts/SelectedKey/SelectedKey.ts: -------------------------------------------------------------------------------- 1 | import { createContext, useContext } from 'react'; 2 | import { BehaviorSubject } from 'rxjs'; 3 | import { KeyDefinition, Revision } from 'tweek-client'; 4 | import { KeyManifest } from 'tweek-client/src/TweekManagementClient/types'; 5 | 6 | export type SelectedKey = { 7 | remote?: KeyDefinition; 8 | manifest?: KeyManifest; 9 | implementation?: string; 10 | revision?: string; 11 | revisionHistory?: Revision[]; 12 | usedBy?: string[]; 13 | aliases?: string[]; 14 | isSaving?: boolean; 15 | }; 16 | 17 | export const SelectedKeyContext = createContext(new BehaviorSubject({})); 18 | 19 | export const useSelectedKeyContext = () => useContext(SelectedKeyContext); 20 | -------------------------------------------------------------------------------- /services/gateway/debug.Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax = docker/dockerfile:1.2 2 | FROM golang:1.16.3-stretch as build 3 | WORKDIR /app 4 | 5 | ADD go.mod /app/go.mod 6 | ADD go.sum /app/go.sum 7 | 8 | RUN --mount=id=tweek-gateway-pkgcache,type=cache,target=/go/pkg/mod go mod download -x 9 | ADD . /app 10 | 11 | RUN --mount=id=tweek-gateway-build-cache,type=cache,target=/root/.cache/go-build --mount=id=tweek-gateway-pkgcache,type=cache,target=/go/pkg/mod go build -o entry \ 12 | && go test -cover -v ./... 13 | 14 | RUN --mount=id=tweek-gateway-build-cache,type=cache,target=/root/.cache/go-build go build -o hcheck "tweek-gateway/healthcheck" 15 | HEALTHCHECK --interval=5s --timeout=2s --retries=10 CMD ["/app/healthcheck"] 16 | ENTRYPOINT [ "/app/entry" ] -------------------------------------------------------------------------------- /addons/Context/Tweek.Drivers.Context.Couchbase/app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /core/Tweek.ApiService.Addons/TweekContractResolver.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using FSharpUtils.Newtonsoft; 4 | using Newtonsoft.Json.Serialization; 5 | using Tweek.Utils; 6 | 7 | namespace Tweek.ApiService.Addons 8 | { 9 | public class TweekContractResolver : DefaultContractResolver 10 | { 11 | protected override JsonContract CreateContract(Type objectType) 12 | { 13 | var contract = base.CreateContract(objectType); 14 | 15 | if (typeof(JsonValue).GetTypeInfo().IsAssignableFrom(objectType.GetTypeInfo())) 16 | { 17 | contract.Converter = new JsonValueConverter(); 18 | } 19 | 20 | return contract; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /docs/assets/folder-icon-closed.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 8 | 9 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /services/publishing/base-repo/security/policy.json: -------------------------------------------------------------------------------- 1 | { 2 | "policies": [ 3 | { 4 | "group": "*", 5 | "user": "*", 6 | "contexts": { 7 | "*": "*" 8 | }, 9 | "object": "values/*", 10 | "action": "*", 11 | "effect": "allow" 12 | }, 13 | { 14 | "group": "*", 15 | "user": "*", 16 | "contexts": { 17 | "tweek_editor_user": "self" 18 | }, 19 | "object": "values/*", 20 | "action": "*", 21 | "effect": "allow" 22 | }, 23 | { 24 | "group": "externalapps", 25 | "user": "admin-app", 26 | "contexts": { 27 | "*": "*" 28 | }, 29 | "object": "*", 30 | "action": "*", 31 | "effect": "allow" 32 | } 33 | ] 34 | } 35 | -------------------------------------------------------------------------------- /deployments/dev/gateway/config/gateway.test.json: -------------------------------------------------------------------------------- 1 | { 2 | "security": { 3 | "auth": { 4 | "providers": { 5 | "mock": { 6 | "name": "Mock OpenId Connect server", 7 | "issuer": "http://oidc-server-mock", 8 | "authority": "http://oidc-server-mock", 9 | "jwks_uri": "http://oidc-server-mock/.well-known/openid-configuration/jwks", 10 | "client_id": "tweek-openid-mock-client", 11 | "login_info": { 12 | "login_type": "oidc", 13 | "scope": "openid profile email", 14 | "response_type": "code" 15 | } 16 | } 17 | }, 18 | "basic_auth": { 19 | "redirect_urls": ["http://editor", "http://gateway"] 20 | } 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /services/authoring/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": true, 3 | "compilerOptions": { 4 | "allowSyntheticDefaultImports": true, 5 | "experimentalDecorators": true, 6 | "emitDecoratorMetadata": true, 7 | "module": "commonjs", 8 | "newLine": "LF", 9 | "noFallthroughCasesInSwitch": true, 10 | "noImplicitAny": false, 11 | "noImplicitReturns": true, 12 | "noImplicitThis": false, 13 | "noUnusedParameters": false, 14 | "noUnusedLocals": false, 15 | "outDir": "dist", 16 | "skipLibCheck": true, 17 | "sourceMap": true, 18 | "strictNullChecks": false, 19 | "esModuleInterop": true, 20 | "lib": ["es2015"], 21 | "target": "es5", 22 | "types": ["mocha"] 23 | }, 24 | "include": ["src/**/*.ts"] 25 | } 26 | -------------------------------------------------------------------------------- /addons/Context/Tweek.Drivers.Context.Redis/RedisServiceAddon.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using Microsoft.Extensions.Configuration; 3 | using Microsoft.Extensions.DependencyInjection; 4 | using Tweek.ApiService.Addons; 5 | using Tweek.Engine.Drivers.Context; 6 | 7 | namespace Tweek.Drivers.Context.Redis 8 | { 9 | public class RedisServiceAddon: ITweekAddon 10 | { 11 | public void Use(IApplicationBuilder builder, IConfiguration configuration) 12 | { 13 | } 14 | 15 | public void Configure(IServiceCollection services, IConfiguration configuration) 16 | { 17 | services.AddSingleton(new RedisDriver(configuration.GetSection("Redis")["ConnectionString"])); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /services/git-service/BareRepository/tests-source/implementations/jpad/smoke_tests/rule_based_keys/simple.jpad: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "Id": "ee6a6ada-2eb2-5683-b4db-8d31ed2b892d", 4 | "Matcher": { 5 | "device.DeviceOsType": "Ios" 6 | }, 7 | "Value": "ios result", 8 | "Type": "SingleVariant" 9 | }, 10 | { 11 | "Id": "d0bd0f7a-9841-5411-8b43-15a523b0e173", 12 | "Matcher": { 13 | "device.DeviceOsType": "Android" 14 | }, 15 | "Value": "android result", 16 | "Type": "SingleVariant" 17 | }, 18 | { 19 | "Id": "3d25cee7-d5cd-50ad-97b6-99178d4333f6", 20 | "Matcher": {}, 21 | "Type": "SingleVariant", 22 | "Value": "default result" 23 | } 24 | ] -------------------------------------------------------------------------------- /services/git-service/BareRepository/source/security/policy.json: -------------------------------------------------------------------------------- 1 | { 2 | "policies": [ 3 | { 4 | "group": "*", 5 | "user": "*", 6 | "contexts": { 7 | "*": "*" 8 | }, 9 | "object": "values/*", 10 | "action": "*", 11 | "effect": "allow" 12 | }, 13 | { 14 | "group": "*", 15 | "user": "*", 16 | "contexts": { 17 | "tweek_editor_user": "self" 18 | }, 19 | "object": "values/*", 20 | "action": "*", 21 | "effect": "allow" 22 | }, 23 | { 24 | "group": "externalapps", 25 | "user": "admin-app", 26 | "contexts": { 27 | "*": "*" 28 | }, 29 | "object": "*", 30 | "action": "*", 31 | "effect": "allow" 32 | } 33 | ] 34 | } 35 | -------------------------------------------------------------------------------- /addons/Context/Tweek.Drivers.Context.MongoDb/MongoDbServiceAddon.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using Microsoft.Extensions.Configuration; 3 | using Microsoft.Extensions.DependencyInjection; 4 | using Tweek.ApiService.Addons; 5 | using Tweek.Engine.Drivers.Context; 6 | 7 | namespace Tweek.Drivers.Context.MongoDb 8 | { 9 | public class MongoDbServiceAddon: ITweekAddon 10 | { 11 | public void Use(IApplicationBuilder builder, IConfiguration configuration) 12 | { 13 | } 14 | 15 | public void Configure(IServiceCollection services, IConfiguration configuration) 16 | { 17 | services.AddSingleton(new MongoDbDriver(configuration.GetSection("MongoDb")["ConnectionString"])); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /core/Engine/Tweek.Engine.Core/Utils/OptionIEnumrableHelpers.cs: -------------------------------------------------------------------------------- 1 | using LanguageExt; 2 | using System.Collections.Generic; 3 | 4 | namespace Tweek.Engine.Core.Utils 5 | { 6 | public static class OptionIEnumrableHelpers 7 | { 8 | public static IEnumerable SkipEmpty(this IEnumerable> it) 9 | { 10 | foreach (var item in it) 11 | { 12 | if (!item.IsNone) yield return item.IfNone(default(T)); 13 | } 14 | } 15 | 16 | public static Option FirstOrNone(this IEnumerable it) 17 | { 18 | foreach (var item in it) 19 | { 20 | return Prelude.Some(item); 21 | } 22 | return Prelude.None; 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /core/Engine/Tweek.Engine.Drivers/Tweek.Engine.Drivers.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | netstandard2.1 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /services/gateway/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Launch", 9 | "type": "go", 10 | "request": "launch", 11 | "mode": "debug", 12 | "remotePath": "", 13 | "port": 2345, 14 | "host": "127.0.0.1", 15 | "program": "${workspaceRoot}", 16 | "env": { 17 | "CONFIGOR_ENV": "local", 18 | "TWEEKGATEWAY_CONFIGFILEPATH": "../../deployments/dev/gateway/config/gateway.json" 19 | }, 20 | "args": [], 21 | "showLog": true 22 | } 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /core/Engine/Tweek.Engine.Core/Rules/RuleSet.cs: -------------------------------------------------------------------------------- 1 | using LanguageExt; 2 | using Tweek.Engine.Core.Context; 3 | using Tweek.Engine.DataTypes; 4 | 5 | namespace Tweek.Engine.Core.Rules 6 | { 7 | public class RuleSet:IRule 8 | { 9 | private readonly IRule[] _rules; 10 | 11 | public RuleSet(params IRule[] rules) 12 | { 13 | _rules = rules; 14 | } 15 | 16 | public Option GetValue(GetContextValue fullContext) 17 | { 18 | foreach (var rule in _rules) 19 | { 20 | var contextValue = rule.GetValue(fullContext); 21 | if (contextValue.IsSome) return contextValue; 22 | } 23 | return Prelude.None; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /core/Engine/Tweek.Engine/Tweek.Engine.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | netstandard2.1 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /services/editor/Dockerfile: -------------------------------------------------------------------------------- 1 | # ---- Deps ---- 2 | FROM node:16.0.0-alpine AS dependencies 3 | WORKDIR /opt/app/ 4 | COPY package.json yarn.lock ./ 5 | RUN yarn --production --network-timeout 1000000 6 | 7 | # ---- Build ---- 8 | FROM dependencies AS build 9 | 10 | RUN yarn --prefer-offline 11 | 12 | COPY . . 13 | RUN CI=true yarn test && yarn build 14 | 15 | # ---- Release ---- 16 | FROM dependencies AS release 17 | 18 | ENV NODE_ENV=production 19 | ENV PORT=3000 20 | EXPOSE 3000 21 | 22 | COPY --from=build /opt/app/build/ ./build 23 | COPY --from=build /opt/app/index.js ./index.js 24 | 25 | VOLUME [ "/opt/app/build/config" ] 26 | 27 | HEALTHCHECK --interval=10s --timeout=10s --retries=8 \ 28 | CMD wget -O - http://localhost:3000/health || exit 1 29 | 30 | CMD node index.js 31 | -------------------------------------------------------------------------------- /docs/_layouts/default.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {% include head.html %} 5 | 6 | 7 | 8 | 9 | 10 | 11 | {% include header.html %} 12 | 13 |
14 | {% include sidemenu.html %} 15 |
16 | {{ content }} 17 |
18 |
19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /e2e/ui/pages/Alert.js: -------------------------------------------------------------------------------- 1 | import { Selector, t } from 'testcafe'; 2 | import { attributeSelector } from '../utils/selector-utils'; 3 | 4 | export default class Alert { 5 | background = Selector('.rodal-mask'); 6 | dialog = Selector('.rodal-dialog'); 7 | 8 | constructor(container) { 9 | this.section = container || Selector('#alerts'); 10 | this.okButton = this.button('ok'); 11 | this.cancelButton = this.button('cancel'); 12 | this.saveButton = this.button('save'); 13 | } 14 | 15 | button(name) { 16 | return this.section.find(attributeSelector('data-alert-button', name)); 17 | } 18 | 19 | async acceptIfRaised() { 20 | const isVisible = await this.okButton.visible; 21 | if (isVisible) { 22 | await t.click(this.okButton); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /services/api/Tweek.ApiService.SmokeTests/RepositoryVersionTests.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Xunit; 3 | using Xunit.Abstractions; 4 | 5 | namespace Tweek.ApiService.SmokeTests 6 | { 7 | public class RepositoryVersionTests 8 | { 9 | private readonly ITweekApi mTweekApi; 10 | 11 | public RepositoryVersionTests(ITestOutputHelper output) 12 | { 13 | mTweekApi = TweekApiServiceFactory.GetTweekApiClient(output); 14 | } 15 | 16 | [Fact] 17 | public async Task GetRepositoryVersion_LatestVersion() 18 | { 19 | var latestVersion = await mTweekApi.GetRepositoryVersion(); 20 | Assert.NotNull(latestVersion); 21 | Assert.NotEmpty(latestVersion); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /services/publishing/Tweek.Publishing.Service/Utils/ConfigurationHelper.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using Microsoft.Extensions.Configuration; 3 | 4 | namespace Tweek.Publishing.Service.Utils 5 | { 6 | public static class ConfigurationExtensions 7 | { 8 | public static string GetValueInlineOrFile(this IConfiguration configuration, string keyPrefix) 9 | { 10 | var inline = configuration[$"{keyPrefix}"]; 11 | if (inline != null) 12 | { 13 | return inline; 14 | } 15 | 16 | var file = configuration[$"{keyPrefix}Path"]; 17 | if (file != null) 18 | { 19 | return File.ReadAllText(file); 20 | } 21 | 22 | return null; 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /core/JPad/Tweek.JPad.Utils/Tweek.JPad.Utils.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | netstandard2.1 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /services/authoring/src/routes/config.ts: -------------------------------------------------------------------------------- 1 | import TagsRepository from '../repositories/tags-repository'; 2 | import KeysRepository from '../repositories/keys-repository'; 3 | import AppsRepository from '../repositories/apps-repository'; 4 | import PolicyRepository from '../repositories/policy-repository'; 5 | import SubjectExtractionRulesRepository from '../repositories/extraction-rules-repository'; 6 | import { HooksRepositoryFactory } from '../repositories/hooks-repository'; 7 | 8 | export type RoutesConfig = { 9 | tagsRepository: TagsRepository; 10 | keysRepository: KeysRepository; 11 | appsRepository: AppsRepository; 12 | policyRepository: PolicyRepository; 13 | subjectExtractionRulesRepository: SubjectExtractionRulesRepository; 14 | hooksRepositoryFactory: HooksRepositoryFactory; 15 | }; 16 | -------------------------------------------------------------------------------- /docs/pages/1.getting-started/01.first-setup.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: page 3 | title: First setup 4 | permalink: /getting started/first-setup 5 | --- 6 | 7 | The easiest way to experience Tweek, is to setup a playground environment. 8 | There are several ways to do it: 9 | 10 | - Create online playground environment - using [play-with-tweek](/play-with-tweek) (quickest, experimental) 11 | - Create local playground environment - using docker-compose 12 | 13 | #### Local - Run Locally using docker-compose 14 | 15 | - clone Tweek's repo (`git clone https://github.com/Soluto/tweek.git`) 16 | - go to deployments/dev (`cd tweek/deployments/dev`) 17 | - (optional) pull the images instead of building (`docker-compose pull --parallel`) 18 | - run (`docker-compose up`) - this might take a few minutes the first time 19 | -------------------------------------------------------------------------------- /services/api/Tweek.ApiService/Controllers/DiagnosticsController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using System.Reflection; 3 | 4 | namespace Tweek.ApiService.Controllers 5 | { 6 | public class DiagnosticsController : Controller 7 | { 8 | private string _version = Assembly.GetEntryAssembly().GetCustomAttribute().InformationalVersion; 9 | 10 | [HttpGet("version")] 11 | public string Version()=> _version; 12 | 13 | [HttpGet("gc")] 14 | [ApiExplorerSettings(IgnoreApi = true)] 15 | public bool GC() => System.Runtime.GCSettings.IsServerGC; 16 | 17 | [HttpGet("")] 18 | [ApiExplorerSettings(IgnoreApi = true)] 19 | public void Default(){ 20 | 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /services/editor/src/pages/keys/components/KeysList/resources/Folder-icon-closed.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 8 | 9 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /services/authoring/src/search-index/get-manifests.ts: -------------------------------------------------------------------------------- 1 | import path = require('path'); 2 | import { promisify } from 'util'; 3 | import _ = require('highland'); 4 | const glob = promisify(require('glob')); 5 | const readFile = _.wrapCallback(require('fs').readFile); 6 | 7 | export async function getManifests(repoDir: string): Promise { 8 | const fileNames = await glob(path.join(repoDir, 'manifests/**/*.json')); 9 | 10 | return new Promise((resolve, reject) => { 11 | const manifests = []; 12 | _(fileNames) 13 | .map(readFile) 14 | .parallel(10) 15 | .map((x) => JSON.parse(x.toString())) 16 | .filter((x) => x.key_path) 17 | .on('error', (err) => reject(err)) 18 | .on('data', (x) => manifests.push(x)) 19 | .on('end', () => resolve(manifests)); 20 | }); 21 | } 22 | -------------------------------------------------------------------------------- /services/editor/src/components/common/ComboBox/MultiSourceComboBox.less: -------------------------------------------------------------------------------- 1 | .multi-source-combo-box-suggestions { 2 | display: flex; 3 | flex-direction: column; 4 | position: absolute; 5 | border: 1px solid lightgray; 6 | z-index: 5; 7 | background-color: white; 8 | 9 | .source-select { 10 | display: flex; 11 | border-bottom: 1px solid lightgray; 12 | color: gray; 13 | text-align: center; 14 | 15 | .source-item { 16 | flex-grow: 1; 17 | flex-basis: 0; 18 | border-right: 1px solid lightgray; 19 | padding: 3px; 20 | cursor: pointer; 21 | 22 | &:last-child { 23 | border-right: none; 24 | } 25 | 26 | &.active { 27 | color: white; 28 | background: gray; 29 | cursor: default; 30 | } 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /e2e/integration/spec/gateway/v2_cors.spec.js: -------------------------------------------------------------------------------- 1 | const client = require('../../utils/client'); 2 | 3 | describe('Gateway v2 CORS tests', () => { 4 | const key = 'integration_tests/some_key'; 5 | 6 | it('Test GET request', async () => { 7 | await client 8 | .options(`/api/v2/values/${key}`) 9 | .set('Origin', 'tweek.test.origin') 10 | .set('Access-Control-Request-Method', 'GET') 11 | .set('Access-Control-Request-Headers', 'Origin,Accept,Content-Type') 12 | .expect(200) 13 | .expect('access-control-allow-credentials', 'true') 14 | .expect('access-control-allow-headers', 'Origin, Accept, Content-Type') 15 | .expect('access-control-allow-methods', 'GET') 16 | .expect('access-control-allow-origin', '*') 17 | .expect('access-control-max-age', '60'); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /e2e/integration/spec/tweek-api/value-distribution.test.js: -------------------------------------------------------------------------------- 1 | const { expect } = require('chai'); 2 | const client = require('../../utils/client'); 3 | 4 | const objectFormatKey = 'integration_tests/value_distribution/object_format'; 5 | const arrayFormatKey = 'integration_tests/value_distribution/array_format'; 6 | 7 | describe('tweek api - value distribution', () => { 8 | it('should get correct value - array format', async () => { 9 | const result = await client.get(`/api/v2/values/${arrayFormatKey}?user=some_user`); 10 | expect(JSON.parse(result.body)).to.equal(15); 11 | }); 12 | 13 | it('should get correct value - object format', async () => { 14 | const result = await client.get(`/api/v2/values/${objectFormatKey}?user=some_user`); 15 | expect(JSON.parse(result.body)).to.equal(15); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /services/authoring/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "parser": "@typescript-eslint/parser", 4 | "plugins": [ 5 | "@typescript-eslint" 6 | ], 7 | "extends": [ 8 | "eslint:recommended", 9 | "plugin:@typescript-eslint/recommended", 10 | "prettier", 11 | "prettier/@typescript-eslint" 12 | ], 13 | "rules": { 14 | "@typescript-eslint/ban-ts-comment": "off", 15 | "@typescript-eslint/ban-types": "off", 16 | "@typescript-eslint/explicit-module-boundary-types": "off", 17 | "@typescript-eslint/no-empty-function": "off", 18 | "@typescript-eslint/no-explicit-any": "off", 19 | "@typescript-eslint/no-non-null-assertion": "off", 20 | "@typescript-eslint/no-var-requires": "off" 21 | }, 22 | "env": { 23 | "es6": true, 24 | "node": true, 25 | "mocha": true 26 | } 27 | } -------------------------------------------------------------------------------- /services/editor/src/pages/context/components/ContextPage/SearchBox/SearchBox.less: -------------------------------------------------------------------------------- 1 | @import (reference) '../../../../../styles/core/core.less'; 2 | @import (reference) '../../../../../styles/core/comboBox.less'; 3 | 4 | .context-search-container { 5 | display: flex; 6 | flex-direction: row; 7 | align-items: flex-end; 8 | margin: -5px; 9 | 10 | .identity-type-container, 11 | .identity-id-container, 12 | .search-button-container { 13 | padding: 5px; 14 | } 15 | 16 | .identity-type-container { 17 | .identity-type { 18 | .combo-box-basic(@dropdown-min-width: 200px, @font-size: 16px); 19 | } 20 | } 21 | 22 | .identity-id-container { 23 | input { 24 | width: 350px; 25 | font-size: 16px; 26 | } 27 | } 28 | .search-button { 29 | .search-circle-button; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /services/editor/src/resources/key-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /e2e/ui/pages/Keys/ObjectEditor.js: -------------------------------------------------------------------------------- 1 | import { ClientFunction, Selector, t } from 'testcafe'; 2 | 3 | const timeoutOptions = { timeoutSeconds: 10 }; 4 | 5 | const getSource = ClientFunction(() => { 6 | const value = window.monaco.editor.getModels()[0].getValue(); 7 | return JSON.parse(value); 8 | }); 9 | 10 | const setSource = ClientFunction(() => { 11 | window.monaco.editor.getModels()[0].setValue(source); 12 | }); 13 | 14 | export default class ObjectEditor { 15 | monaco = Selector('.monaco-editor'); 16 | 17 | async getSource() { 18 | await t.expect(this.monaco.visible).ok(timeoutOptions); 19 | return await getSource(); 20 | } 21 | 22 | async setSource(source) { 23 | await t.expect(this.monaco.visible).ok(timeoutOptions); 24 | return await setSource.with({ dependencies: { source } })(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /e2e/ui/pages/Context/FixedKey.js: -------------------------------------------------------------------------------- 1 | import { Selector, t } from 'testcafe'; 2 | import { dataComp, dataField } from '../../utils/selector-utils'; 3 | 4 | export default class FixedKey { 5 | constructor(key, type = 'string') { 6 | this.container = Selector(dataComp('fixed-key')).withAttribute('data-fixed-key', key); 7 | this.deleteButton = this.container.find(dataComp('delete-fixed-key')); 8 | 9 | this.valueInput = this.container.find(dataField('value')); 10 | if (type) { 11 | this.valueInput = this.valueInput.withAttribute('data-value-type', type.toLowerCase()); 12 | } 13 | } 14 | 15 | async update(value) { 16 | value = value.toString(); 17 | 18 | await t 19 | .expect(this.valueInput.disabled) 20 | .notOk() 21 | .typeText(this.valueInput, value, { replace: true }); 22 | } 23 | } 24 | --------------------------------------------------------------------------------