diff --git a/independent-publisher-connectors/LaunchDarkly/Readme.md b/independent-publisher-connectors/LaunchDarkly/Readme.md new file mode 100644 index 0000000000..4525c2543c --- /dev/null +++ b/independent-publisher-connectors/LaunchDarkly/Readme.md @@ -0,0 +1,77 @@ +# LaunchDarkly + +Manage feature flags, projects, and environments in LaunchDarkly from Power Automate. Create flags, toggle them on and off across environments, check flag statuses, copy configurations between environments, and monitor changes through the audit log — enabling automated feature flag management as part of your CI/CD and operational workflows. + +## Publisher +### Aaron Mah + +## Prerequisites + +You need a LaunchDarkly account with an API access token to use this connector: + +1. **Sign up** for a free LaunchDarkly Developer account at [https://app.launchdarkly.com/signup](https://app.launchdarkly.com/signup) (supports Google and GitHub SSO; no credit card required). +2. **Create an API access token:** + - Log in to LaunchDarkly at [https://app.launchdarkly.com](https://app.launchdarkly.com). + - Click the **gear icon** in the left sidebar → **Organization settings**. + - Click **Authorization**. + - In the "Access tokens" section, click **Create token**. + - Enter a name (e.g., `Power Automate Connector`). + - Select the **Writer** role (required for create, toggle, and delete operations; Reader suffices for read-only). + - Click **Save token** and **copy the token immediately** — it is shown only once. +3. In Power Automate, create a new LaunchDarkly connection and paste the token. + +## Supported Operations + +### List Projects +List all projects in your LaunchDarkly account. + +### Get Project +Get the details of a single LaunchDarkly project. + +### List Environments +List all environments in a LaunchDarkly project. + +### Get Environment +Get the details of a single environment in a project. + +### List Feature Flags +List all feature flags in a LaunchDarkly project. + +### Create Feature Flag +Create a new feature flag in a LaunchDarkly project. + +### Get Feature Flag +Get the details of a single feature flag including its per-environment configuration. + +### Turn Flag On +Turn on targeting for a feature flag in a specific environment. + +### Turn Flag Off +Turn off targeting for a feature flag in a specific environment. + +### Delete Feature Flag +Permanently delete a feature flag from a project. + +### Copy Flag Configuration +Copy a feature flag's configuration from one environment to another. + +### Get Flag Status +Get the status of a feature flag in a specific environment (active, inactive, launched, or new). + +### List Audit Log Entries +List recent audit log entries for your LaunchDarkly account. + +## API Documentation +Visit [LaunchDarkly API Docs](https://launchdarkly.com/docs/api) for further details. + +## Known Issues and Limitations + +- **Rate limits:** The LaunchDarkly API has rate limits that vary by endpoint and plan. If you hit rate limits, add delays between actions in your flows. +- **Token visibility:** API access tokens are shown only once when created. If lost, you must create a new token. +- **Plan-gated features:** Some advanced features (custom roles, service tokens, workflows, approvals, scheduled changes) require an Enterprise plan and are not available through this connector. +- **Semantic patch operations:** The Turn Flag On and Turn Flag Off operations use LaunchDarkly's Semantic Patch format. They cannot be combined with other flag modifications in a single call. +- **Pagination:** List operations return a maximum of 20 items by default. Use the Limit and Offset parameters to paginate through larger result sets. +- **Variation values:** Feature flag variation values can be boolean, string, number, or JSON. The connector returns them as-is from the API. + +## License +Distributed under the MIT License. diff --git a/independent-publisher-connectors/LaunchDarkly/apiDefinition.swagger.json b/independent-publisher-connectors/LaunchDarkly/apiDefinition.swagger.json new file mode 100644 index 0000000000..1fe5d2dc2e --- /dev/null +++ b/independent-publisher-connectors/LaunchDarkly/apiDefinition.swagger.json @@ -0,0 +1,1372 @@ +{ + "swagger": "2.0", + "info": { + "title": "LaunchDarkly", + "description": "Manage feature flags, projects, and environments in LaunchDarkly. Create, toggle, and monitor feature flags across your software delivery lifecycle.", + "version": "1.0", + "contact": { + "name": "Aaron Mah", + "url": "https://github.com/aaronmah", + "email": "aaronmah@microsoft.com" + } + }, + "x-ms-connector-metadata": [ + { + "propertyName": "Website", + "propertyValue": "https://launchdarkly.com" + }, + { + "propertyName": "Privacy policy", + "propertyValue": "https://launchdarkly.com/policies/privacy" + }, + { + "propertyName": "Categories", + "propertyValue": "IT Operations;Productivity" + } + ], + "host": "app.launchdarkly.com", + "basePath": "/api/v2", + "schemes": [ + "https" + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "paths": { + "/projects": { + "get": { + "operationId": "listProjects", + "summary": "List Projects", + "description": "List all projects in your LaunchDarkly account.", + "tags": [ + "Projects" + ], + "parameters": [ + { + "name": "limit", + "in": "query", + "type": "integer", + "required": false, + "x-ms-summary": "Limit", + "description": "Maximum number of projects to return (default 20).", + "x-ms-visibility": "advanced" + }, + { + "name": "offset", + "in": "query", + "type": "integer", + "required": false, + "x-ms-summary": "Offset", + "description": "Number of projects to skip for pagination.", + "x-ms-visibility": "advanced" + }, + { + "name": "filter", + "in": "query", + "type": "string", + "required": false, + "x-ms-summary": "Filter", + "description": "Filter expression (e.g., query:my-project).", + "x-ms-visibility": "advanced" + }, + { + "name": "sort", + "in": "query", + "type": "string", + "required": false, + "x-ms-summary": "Sort", + "description": "Sort order (e.g., name, -createdOn).", + "x-ms-visibility": "advanced" + } + ], + "responses": { + "200": { + "description": "A list of projects.", + "schema": { + "$ref": "#/definitions/ProjectListResponse" + } + } + } + } + }, + "/projects/{projectKey}": { + "get": { + "operationId": "getProject", + "summary": "Get Project", + "description": "Get the details of a single LaunchDarkly project.", + "tags": [ + "Projects" + ], + "parameters": [ + { + "name": "projectKey", + "in": "path", + "type": "string", + "required": true, + "x-ms-summary": "Project Key", + "x-ms-url-encoding": "single", + "description": "The unique key of the project." + }, + { + "name": "expand", + "in": "query", + "type": "string", + "required": false, + "x-ms-summary": "Expand", + "description": "Expand options (e.g., environments to include full environment list).", + "x-ms-visibility": "advanced" + } + ], + "responses": { + "200": { + "description": "The project details.", + "schema": { + "$ref": "#/definitions/ProjectDetail" + } + } + } + } + }, + "/projects/{projectKey}/environments": { + "get": { + "operationId": "listEnvironments", + "summary": "List Environments", + "description": "List all environments in a LaunchDarkly project.", + "tags": [ + "Environments" + ], + "parameters": [ + { + "name": "projectKey", + "in": "path", + "type": "string", + "required": true, + "x-ms-summary": "Project Key", + "x-ms-url-encoding": "single", + "description": "The unique key of the project." + }, + { + "name": "limit", + "in": "query", + "type": "integer", + "required": false, + "x-ms-summary": "Limit", + "description": "Maximum number of environments to return (default 20).", + "x-ms-visibility": "advanced" + }, + { + "name": "offset", + "in": "query", + "type": "integer", + "required": false, + "x-ms-summary": "Offset", + "description": "Number of environments to skip for pagination.", + "x-ms-visibility": "advanced" + } + ], + "responses": { + "200": { + "description": "A list of environments.", + "schema": { + "$ref": "#/definitions/EnvironmentListResponse" + } + } + } + } + }, + "/projects/{projectKey}/environments/{environmentKey}": { + "get": { + "operationId": "getEnvironment", + "summary": "Get Environment", + "description": "Get the details of a single environment in a project.", + "tags": [ + "Environments" + ], + "parameters": [ + { + "name": "projectKey", + "in": "path", + "type": "string", + "required": true, + "x-ms-summary": "Project Key", + "x-ms-url-encoding": "single", + "description": "The unique key of the project." + }, + { + "name": "environmentKey", + "in": "path", + "type": "string", + "required": true, + "x-ms-summary": "Environment Key", + "x-ms-url-encoding": "single", + "description": "The unique key of the environment." + } + ], + "responses": { + "200": { + "description": "The environment details.", + "schema": { + "$ref": "#/definitions/EnvironmentDetail" + } + } + } + } + }, + "/flags/{projectKey}": { + "get": { + "operationId": "listFeatureFlags", + "summary": "List Feature Flags", + "description": "List all feature flags in a LaunchDarkly project.", + "tags": [ + "Feature Flags" + ], + "parameters": [ + { + "name": "projectKey", + "in": "path", + "type": "string", + "required": true, + "x-ms-summary": "Project Key", + "x-ms-url-encoding": "single", + "description": "The unique key of the project." + }, + { + "name": "env", + "in": "query", + "type": "string", + "required": false, + "x-ms-summary": "Environment", + "description": "Filter flag data to a specific environment key.", + "x-ms-visibility": "important" + }, + { + "name": "tag", + "in": "query", + "type": "string", + "required": false, + "x-ms-summary": "Tag", + "description": "Filter flags by tag." + }, + { + "name": "limit", + "in": "query", + "type": "integer", + "required": false, + "x-ms-summary": "Limit", + "description": "Maximum number of flags to return (default 20).", + "x-ms-visibility": "advanced" + }, + { + "name": "offset", + "in": "query", + "type": "integer", + "required": false, + "x-ms-summary": "Offset", + "description": "Number of flags to skip for pagination.", + "x-ms-visibility": "advanced" + }, + { + "name": "filter", + "in": "query", + "type": "string", + "required": false, + "x-ms-summary": "Filter", + "description": "Filter expression (e.g., query:my-flag).", + "x-ms-visibility": "advanced" + }, + { + "name": "sort", + "in": "query", + "type": "string", + "required": false, + "x-ms-summary": "Sort", + "description": "Sort order (e.g., name, -creationDate).", + "x-ms-visibility": "advanced" + }, + { + "name": "summary", + "in": "query", + "type": "boolean", + "required": false, + "x-ms-summary": "Summary Only", + "description": "Whether to return summary representations only (default true).", + "x-ms-visibility": "advanced" + }, + { + "name": "archived", + "in": "query", + "type": "boolean", + "required": false, + "x-ms-summary": "Include Archived", + "description": "Whether to include archived flags.", + "x-ms-visibility": "advanced" + } + ], + "responses": { + "200": { + "description": "A list of feature flags.", + "schema": { + "$ref": "#/definitions/FlagListResponse" + } + } + } + }, + "post": { + "operationId": "createFeatureFlag", + "summary": "Create Feature Flag", + "description": "Create a new feature flag in a LaunchDarkly project.", + "tags": [ + "Feature Flags" + ], + "parameters": [ + { + "name": "projectKey", + "in": "path", + "type": "string", + "required": true, + "x-ms-summary": "Project Key", + "x-ms-url-encoding": "single", + "description": "The unique key of the project." + }, + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object", + "required": [ + "name", + "key" + ], + "properties": { + "name": { + "type": "string", + "x-ms-summary": "Flag Name", + "description": "A human-readable name for the flag." + }, + "key": { + "type": "string", + "x-ms-summary": "Flag Key", + "description": "A unique key for the flag (used in code, e.g., enable-dark-mode)." + }, + "description": { + "type": "string", + "x-ms-summary": "Description", + "description": "A description of what the flag controls." + }, + "tags": { + "type": "array", + "items": { + "type": "string" + }, + "x-ms-summary": "Tags", + "description": "Tags to associate with the flag.", + "x-ms-visibility": "advanced" + }, + "temporary": { + "type": "boolean", + "x-ms-summary": "Temporary", + "description": "Whether the flag is temporary (default false).", + "x-ms-visibility": "advanced" + }, + "includeInSnippet": { + "type": "boolean", + "x-ms-summary": "Include in Snippet", + "description": "Whether to include the flag in the client-side JavaScript snippet.", + "x-ms-visibility": "advanced" + }, + "clientSideAvailability": { + "type": "object", + "x-ms-summary": "Client-Side Availability", + "description": "Client-side SDK availability settings.", + "x-ms-visibility": "advanced", + "properties": { + "usingMobileKey": { + "type": "boolean", + "x-ms-summary": "Using Mobile Key", + "description": "Whether the flag is available to mobile SDKs." + }, + "usingEnvironmentId": { + "type": "boolean", + "x-ms-summary": "Using Environment ID", + "description": "Whether the flag is available to client-side JS SDKs." + } + } + } + } + } + } + ], + "responses": { + "201": { + "description": "The newly created feature flag.", + "schema": { + "$ref": "#/definitions/FlagDetail" + } + } + } + } + }, + "/flags/{projectKey}/{featureFlagKey}": { + "get": { + "operationId": "getFeatureFlag", + "summary": "Get Feature Flag", + "description": "Get the details of a single feature flag including its per-environment configuration.", + "tags": [ + "Feature Flags" + ], + "parameters": [ + { + "name": "projectKey", + "in": "path", + "type": "string", + "required": true, + "x-ms-summary": "Project Key", + "x-ms-url-encoding": "single", + "description": "The unique key of the project." + }, + { + "name": "featureFlagKey", + "in": "path", + "type": "string", + "required": true, + "x-ms-summary": "Flag Key", + "x-ms-url-encoding": "single", + "description": "The unique key of the feature flag." + }, + { + "name": "env", + "in": "query", + "type": "string", + "required": false, + "x-ms-summary": "Environment", + "description": "Filter to a specific environment key.", + "x-ms-visibility": "important" + } + ], + "responses": { + "200": { + "description": "The feature flag details.", + "schema": { + "$ref": "#/definitions/FlagDetail" + } + } + } + }, + "delete": { + "operationId": "deleteFeatureFlag", + "summary": "Delete Feature Flag", + "description": "Permanently delete a feature flag from a project.", + "tags": [ + "Feature Flags" + ], + "parameters": [ + { + "name": "projectKey", + "in": "path", + "type": "string", + "required": true, + "x-ms-summary": "Project Key", + "x-ms-url-encoding": "single", + "description": "The unique key of the project." + }, + { + "name": "featureFlagKey", + "in": "path", + "type": "string", + "required": true, + "x-ms-summary": "Flag Key", + "x-ms-url-encoding": "single", + "description": "The unique key of the feature flag to delete." + } + ], + "responses": { + "200": { + "description": "The feature flag was deleted successfully.", + "schema": { + "type": "object", + "properties": { + "status": { + "type": "string", + "description": "Confirmation that the flag was deleted." + } + } + } + } + } + } + }, + "/flags/{projectKey}/{featureFlagKey}/copy": { + "post": { + "operationId": "copyFlagConfiguration", + "summary": "Copy Flag Configuration", + "description": "Copy a feature flag's configuration from one environment to another.", + "tags": [ + "Feature Flags" + ], + "parameters": [ + { + "name": "projectKey", + "in": "path", + "type": "string", + "required": true, + "x-ms-summary": "Project Key", + "x-ms-url-encoding": "single", + "description": "The unique key of the project." + }, + { + "name": "featureFlagKey", + "in": "path", + "type": "string", + "required": true, + "x-ms-summary": "Flag Key", + "x-ms-url-encoding": "single", + "description": "The unique key of the feature flag." + }, + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object", + "required": [ + "source", + "target" + ], + "properties": { + "source": { + "type": "object", + "description": "The source environment.", + "x-ms-summary": "Source", + "required": [ + "key" + ], + "properties": { + "key": { + "type": "string", + "x-ms-summary": "Source Environment Key", + "description": "The environment key to copy from." + } + } + }, + "target": { + "type": "object", + "description": "The target environment.", + "x-ms-summary": "Target", + "required": [ + "key" + ], + "properties": { + "key": { + "type": "string", + "x-ms-summary": "Target Environment Key", + "description": "The environment key to copy to." + } + } + }, + "comment": { + "type": "string", + "x-ms-summary": "Comment", + "description": "An optional comment explaining the copy operation." + }, + "includedActions": { + "type": "array", + "items": { + "type": "string" + }, + "x-ms-summary": "Included Actions", + "description": "Actions to include in the copy (e.g., updateOn, updateTargets, updateRules).", + "x-ms-visibility": "advanced" + }, + "excludedActions": { + "type": "array", + "items": { + "type": "string" + }, + "x-ms-summary": "Excluded Actions", + "description": "Actions to exclude from the copy.", + "x-ms-visibility": "advanced" + } + } + } + } + ], + "responses": { + "200": { + "description": "The updated feature flag in the target environment.", + "schema": { + "$ref": "#/definitions/FlagDetail" + } + } + } + } + }, + "/flags-turnOn": { + "patch": { + "operationId": "turnFlagOn", + "summary": "Turn Flag On", + "description": "Turn on targeting for a feature flag in a specific environment.", + "tags": [ + "Feature Flags" + ], + "parameters": [ + { + "name": "projectKey", + "in": "query", + "type": "string", + "required": true, + "x-ms-summary": "Project Key", + "description": "The unique key of the project." + }, + { + "name": "featureFlagKey", + "in": "query", + "type": "string", + "required": true, + "x-ms-summary": "Flag Key", + "description": "The unique key of the feature flag." + }, + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object", + "required": [ + "environmentKey", + "instructions" + ], + "properties": { + "environmentKey": { + "type": "string", + "x-ms-summary": "Environment Key", + "description": "The environment key where the flag should be turned on." + }, + "comment": { + "type": "string", + "x-ms-summary": "Comment", + "description": "An optional comment explaining why the flag is being turned on." + }, + "instructions": { + "type": "array", + "items": { + "type": "object", + "properties": { + "kind": { + "type": "string", + "description": "The semantic patch instruction kind." + } + } + }, + "default": [ + { + "kind": "turnFlagOn" + } + ], + "x-ms-visibility": "internal", + "description": "The semantic patch instructions." + } + } + } + } + ], + "responses": { + "200": { + "description": "The updated feature flag.", + "schema": { + "$ref": "#/definitions/FlagDetail" + } + } + } + } + }, + "/flags-turnOff": { + "patch": { + "operationId": "turnFlagOff", + "summary": "Turn Flag Off", + "description": "Turn off targeting for a feature flag in a specific environment.", + "tags": [ + "Feature Flags" + ], + "parameters": [ + { + "name": "projectKey", + "in": "query", + "type": "string", + "required": true, + "x-ms-summary": "Project Key", + "description": "The unique key of the project." + }, + { + "name": "featureFlagKey", + "in": "query", + "type": "string", + "required": true, + "x-ms-summary": "Flag Key", + "description": "The unique key of the feature flag." + }, + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object", + "required": [ + "environmentKey", + "instructions" + ], + "properties": { + "environmentKey": { + "type": "string", + "x-ms-summary": "Environment Key", + "description": "The environment key where the flag should be turned off." + }, + "comment": { + "type": "string", + "x-ms-summary": "Comment", + "description": "An optional comment explaining why the flag is being turned off." + }, + "instructions": { + "type": "array", + "items": { + "type": "object", + "properties": { + "kind": { + "type": "string", + "description": "The semantic patch instruction kind." + } + } + }, + "default": [ + { + "kind": "turnFlagOff" + } + ], + "x-ms-visibility": "internal", + "description": "The semantic patch instructions." + } + } + } + } + ], + "responses": { + "200": { + "description": "The updated feature flag.", + "schema": { + "$ref": "#/definitions/FlagDetail" + } + } + } + } + }, + "/flag-statuses/{projectKey}/{environmentKey}/{featureFlagKey}": { + "get": { + "operationId": "getFlagStatus", + "summary": "Get Flag Status", + "description": "Get the status of a feature flag in a specific environment.", + "tags": [ + "Feature Flags" + ], + "parameters": [ + { + "name": "projectKey", + "in": "path", + "type": "string", + "required": true, + "x-ms-summary": "Project Key", + "x-ms-url-encoding": "single", + "description": "The unique key of the project." + }, + { + "name": "environmentKey", + "in": "path", + "type": "string", + "required": true, + "x-ms-summary": "Environment Key", + "x-ms-url-encoding": "single", + "description": "The unique key of the environment." + }, + { + "name": "featureFlagKey", + "in": "path", + "type": "string", + "required": true, + "x-ms-summary": "Flag Key", + "x-ms-url-encoding": "single", + "description": "The unique key of the feature flag." + } + ], + "responses": { + "200": { + "description": "The feature flag status.", + "schema": { + "$ref": "#/definitions/FlagStatusResponse" + } + } + } + } + }, + "/auditlog": { + "get": { + "operationId": "listAuditLogEntries", + "summary": "List Audit Log Entries", + "description": "List recent audit log entries for your LaunchDarkly account.", + "tags": [ + "Audit Log" + ], + "parameters": [ + { + "name": "limit", + "in": "query", + "type": "integer", + "required": false, + "x-ms-summary": "Limit", + "description": "Maximum number of entries to return (default 20).", + "x-ms-visibility": "advanced" + }, + { + "name": "after", + "in": "query", + "type": "integer", + "format": "int64", + "required": false, + "x-ms-summary": "After", + "description": "Return entries after this Unix epoch millisecond timestamp." + }, + { + "name": "before", + "in": "query", + "type": "integer", + "format": "int64", + "required": false, + "x-ms-summary": "Before", + "description": "Return entries before this Unix epoch millisecond timestamp." + }, + { + "name": "q", + "in": "query", + "type": "string", + "required": false, + "x-ms-summary": "Search Query", + "description": "Full-text search query across audit log entries." + }, + { + "name": "spec", + "in": "query", + "type": "string", + "required": false, + "x-ms-summary": "Resource Specifier", + "description": "Resource specifier filter (e.g., proj/my-project:env/production:flag/*).", + "x-ms-visibility": "advanced" + } + ], + "responses": { + "200": { + "description": "A list of audit log entries.", + "schema": { + "$ref": "#/definitions/AuditLogListResponse" + } + } + } + } + } + }, + "definitions": { + "ProjectListResponse": { + "type": "object", + "properties": { + "items": { + "type": "array", + "description": "Array of project objects.", + "items": { + "$ref": "#/definitions/Project" + } + }, + "totalCount": { + "type": "integer", + "description": "Total number of projects." + } + } + }, + "Project": { + "type": "object", + "properties": { + "key": { + "type": "string", + "description": "The project key (unique identifier)." + }, + "name": { + "type": "string", + "description": "The project display name." + }, + "tags": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Tags associated with the project." + }, + "_id": { + "type": "string", + "description": "The project's internal ID." + } + } + }, + "ProjectDetail": { + "type": "object", + "properties": { + "key": { + "type": "string", + "description": "The project key." + }, + "name": { + "type": "string", + "description": "The project display name." + }, + "tags": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Tags associated with the project." + }, + "_id": { + "type": "string", + "description": "The project's internal ID." + }, + "environments": { + "type": "object", + "description": "Environments in the project (only if expand=environments).", + "properties": { + "items": { + "type": "array", + "description": "Array of environment objects.", + "items": { + "$ref": "#/definitions/Environment" + } + }, + "totalCount": { + "type": "integer", + "description": "Total number of environments." + } + } + }, + "defaultClientSideAvailability": { + "type": "object", + "description": "Default client-side availability settings.", + "properties": { + "usingMobileKey": { + "type": "boolean", + "description": "Whether flags are available to mobile SDKs by default." + }, + "usingEnvironmentId": { + "type": "boolean", + "description": "Whether flags are available to client-side JS SDKs by default." + } + } + } + } + }, + "EnvironmentListResponse": { + "type": "object", + "properties": { + "items": { + "type": "array", + "description": "Array of environment objects.", + "items": { + "$ref": "#/definitions/Environment" + } + }, + "totalCount": { + "type": "integer", + "description": "Total number of environments." + } + } + }, + "Environment": { + "type": "object", + "properties": { + "key": { + "type": "string", + "description": "The environment key (unique identifier within the project)." + }, + "name": { + "type": "string", + "description": "The environment display name." + }, + "color": { + "type": "string", + "description": "The environment color (hex string, e.g., 417505)." + }, + "_id": { + "type": "string", + "description": "The environment's internal ID." + }, + "tags": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Tags associated with the environment." + } + } + }, + "EnvironmentDetail": { + "type": "object", + "properties": { + "key": { + "type": "string", + "description": "The environment key." + }, + "name": { + "type": "string", + "description": "The environment display name." + }, + "color": { + "type": "string", + "description": "The environment color (hex string)." + }, + "_id": { + "type": "string", + "description": "The environment's internal ID." + }, + "apiKey": { + "type": "string", + "description": "The SDK key for this environment." + }, + "mobileKey": { + "type": "string", + "description": "The mobile SDK key." + }, + "tags": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Tags associated with the environment." + }, + "defaultTtl": { + "type": "integer", + "description": "Default TTL in minutes." + }, + "secureMode": { + "type": "boolean", + "description": "Whether secure mode is enabled." + }, + "requireComments": { + "type": "boolean", + "description": "Whether comments are required for flag changes." + }, + "confirmChanges": { + "type": "boolean", + "description": "Whether change confirmation is required." + } + } + }, + "FlagListResponse": { + "type": "object", + "properties": { + "items": { + "type": "array", + "description": "Array of feature flag objects.", + "items": { + "$ref": "#/definitions/FlagSummary" + } + }, + "totalCount": { + "type": "integer", + "description": "Total number of flags matching the query." + } + } + }, + "FlagSummary": { + "type": "object", + "properties": { + "key": { + "type": "string", + "description": "The flag key (unique identifier within the project)." + }, + "name": { + "type": "string", + "description": "The flag display name." + }, + "description": { + "type": "string", + "description": "The flag description." + }, + "kind": { + "type": "string", + "description": "The flag kind (boolean or multivariate)." + }, + "temporary": { + "type": "boolean", + "description": "Whether the flag is marked as temporary." + }, + "tags": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Tags associated with the flag." + }, + "creationDate": { + "type": "integer", + "format": "int64", + "description": "Unix epoch milliseconds when the flag was created." + }, + "_version": { + "type": "integer", + "description": "The flag version number." + }, + "variations": { + "type": "array", + "description": "The flag's variation definitions.", + "items": { + "$ref": "#/definitions/Variation" + } + } + } + }, + "FlagDetail": { + "type": "object", + "properties": { + "key": { + "type": "string", + "description": "The flag key." + }, + "name": { + "type": "string", + "description": "The flag display name." + }, + "description": { + "type": "string", + "description": "The flag description." + }, + "kind": { + "type": "string", + "description": "The flag kind (boolean or multivariate)." + }, + "temporary": { + "type": "boolean", + "description": "Whether the flag is marked as temporary." + }, + "tags": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Tags associated with the flag." + }, + "creationDate": { + "type": "integer", + "format": "int64", + "description": "Unix epoch milliseconds when the flag was created." + }, + "_version": { + "type": "integer", + "description": "The flag version number." + }, + "variations": { + "type": "array", + "description": "The flag's variation definitions.", + "items": { + "$ref": "#/definitions/Variation" + } + }, + "environments": { + "type": "object", + "description": "Per-environment configuration keyed by environment key.", + "additionalProperties": { + "$ref": "#/definitions/EnvironmentConfig" + } + } + } + }, + "Variation": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "The variation display name." + }, + "description": { + "type": "string", + "description": "The variation description." + }, + "_id": { + "type": "string", + "description": "The variation unique identifier." + } + } + }, + "EnvironmentConfig": { + "type": "object", + "properties": { + "on": { + "type": "boolean", + "description": "Whether targeting is on in this environment." + }, + "_environmentName": { + "type": "string", + "description": "The environment display name." + }, + "lastModified": { + "type": "integer", + "format": "int64", + "description": "Unix epoch milliseconds of last modification in this environment." + }, + "_summary": { + "type": "object", + "description": "Summary of flag evaluations per variation." + } + } + }, + "FlagStatusResponse": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "The flag status: active, inactive, launched, or new." + }, + "lastRequested": { + "type": "string", + "description": "ISO 8601 timestamp of the last time the flag was evaluated by an SDK." + }, + "default": { + "type": "object", + "description": "The default variation being served." + }, + "_links": { + "type": "object", + "description": "HAL links for this resource." + } + } + }, + "AuditLogListResponse": { + "type": "object", + "properties": { + "items": { + "type": "array", + "description": "Array of audit log entry objects.", + "items": { + "$ref": "#/definitions/AuditLogEntry" + } + }, + "totalCount": { + "type": "integer", + "description": "Total number of matching entries." + } + } + }, + "AuditLogEntry": { + "type": "object", + "properties": { + "_id": { + "type": "string", + "description": "The audit log entry ID." + }, + "date": { + "type": "integer", + "format": "int64", + "description": "Unix epoch milliseconds when the action occurred." + }, + "kind": { + "type": "string", + "description": "The type of resource that was changed." + }, + "name": { + "type": "string", + "description": "The name of the changed resource." + }, + "description": { + "type": "string", + "description": "Human-readable description of the change." + }, + "shortDescription": { + "type": "string", + "description": "A shorter description of the change." + }, + "member": { + "$ref": "#/definitions/AuditLogMember" + }, + "titleVerb": { + "type": "string", + "description": "The action verb (e.g., turned on, created, updated)." + }, + "title": { + "type": "string", + "description": "The full change title." + }, + "target": { + "type": "object", + "description": "The target resource of the change.", + "properties": { + "name": { + "type": "string", + "description": "The target resource name." + }, + "resources": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Resource specifiers for the target." + } + } + } + } + }, + "AuditLogMember": { + "type": "object", + "properties": { + "email": { + "type": "string", + "description": "The member's email address." + }, + "firstName": { + "type": "string", + "description": "The member's first name." + }, + "lastName": { + "type": "string", + "description": "The member's last name." + } + } + } + }, + "parameters": {}, + "responses": {}, + "securityDefinitions": { + "api_key": { + "type": "apiKey", + "in": "header", + "name": "Authorization" + } + }, + "security": [ + { + "api_key": [] + } + ], + "tags": [ + { + "name": "Projects", + "description": "Manage LaunchDarkly projects." + }, + { + "name": "Environments", + "description": "Manage environments within projects." + }, + { + "name": "Feature Flags", + "description": "Manage feature flags." + }, + { + "name": "Audit Log", + "description": "View audit log entries." + } + ] +} diff --git a/independent-publisher-connectors/LaunchDarkly/apiProperties.json b/independent-publisher-connectors/LaunchDarkly/apiProperties.json new file mode 100644 index 0000000000..5182e8c01d --- /dev/null +++ b/independent-publisher-connectors/LaunchDarkly/apiProperties.json @@ -0,0 +1,61 @@ +{ + "properties": { + "connectionParameters": { + "api_key": { + "type": "securestring", + "uiDefinition": { + "displayName": "API Access Token", + "description": "Your LaunchDarkly personal access token (Writer or Admin role).", + "tooltip": "Go to Organization Settings > Authorization > Create token with Writer role.", + "constraints": { + "tabIndex": 2, + "clearText": false, + "required": "true" + } + } + } + }, + "iconBrandColor": "#da3b01", + "capabilities": [], + "policyTemplateInstances": [ + { + "templateId": "setheader", + "title": "Set Content-Type for semantic patch operations", + "parameters": { + "x-ms-apimTemplateParameter.name": "Content-Type", + "x-ms-apimTemplateParameter.value": "application/json; domain-model=launchdarkly.semanticpatch", + "x-ms-apimTemplateParameter.existsAction": "override", + "x-ms-apimTemplate-policySection": "Request", + "x-ms-apimTemplate-operationName": [ + "turnFlagOn", + "turnFlagOff" + ] + } + }, + { + "templateId": "routerequesttoendpoint", + "title": "Route turnFlagOn to PATCH flags endpoint", + "parameters": { + "x-ms-apimTemplateParameter.newPath": "/flags/@queryParameters('projectKey')/@queryParameters('featureFlagKey')", + "x-ms-apimTemplateParameter.httpMethod": "PATCH", + "x-ms-apimTemplate-operationName": [ + "turnFlagOn" + ] + } + }, + { + "templateId": "routerequesttoendpoint", + "title": "Route turnFlagOff to PATCH flags endpoint", + "parameters": { + "x-ms-apimTemplateParameter.newPath": "/flags/@queryParameters('projectKey')/@queryParameters('featureFlagKey')", + "x-ms-apimTemplateParameter.httpMethod": "PATCH", + "x-ms-apimTemplate-operationName": [ + "turnFlagOff" + ] + } + } + ], + "publisher": "Aaron Mah", + "stackOwner": "LaunchDarkly" + } +} diff --git a/independent-publisher-connectors/LaunchDarkly/test-evidence/all-operations-passed.png b/independent-publisher-connectors/LaunchDarkly/test-evidence/all-operations-passed.png new file mode 100644 index 0000000000..8d4052edfd Binary files /dev/null and b/independent-publisher-connectors/LaunchDarkly/test-evidence/all-operations-passed.png differ diff --git a/independent-publisher-connectors/LaunchDarkly/test-evidence/connection-ready.png b/independent-publisher-connectors/LaunchDarkly/test-evidence/connection-ready.png new file mode 100644 index 0000000000..495365e55a Binary files /dev/null and b/independent-publisher-connectors/LaunchDarkly/test-evidence/connection-ready.png differ diff --git a/independent-publisher-connectors/LaunchDarkly/test-evidence/connector-overview.png b/independent-publisher-connectors/LaunchDarkly/test-evidence/connector-overview.png new file mode 100644 index 0000000000..32dff90729 Binary files /dev/null and b/independent-publisher-connectors/LaunchDarkly/test-evidence/connector-overview.png differ diff --git a/independent-publisher-connectors/LaunchDarkly/test-evidence/scenario-1-run-success.png b/independent-publisher-connectors/LaunchDarkly/test-evidence/scenario-1-run-success.png new file mode 100644 index 0000000000..6154ed1480 Binary files /dev/null and b/independent-publisher-connectors/LaunchDarkly/test-evidence/scenario-1-run-success.png differ diff --git a/independent-publisher-connectors/LaunchDarkly/test-evidence/scenario-2-run-success.png b/independent-publisher-connectors/LaunchDarkly/test-evidence/scenario-2-run-success.png new file mode 100644 index 0000000000..8484fe3798 Binary files /dev/null and b/independent-publisher-connectors/LaunchDarkly/test-evidence/scenario-2-run-success.png differ diff --git a/independent-publisher-connectors/LaunchDarkly/test-evidence/scenario-3a-run-success.png b/independent-publisher-connectors/LaunchDarkly/test-evidence/scenario-3a-run-success.png new file mode 100644 index 0000000000..9d57ecb33e Binary files /dev/null and b/independent-publisher-connectors/LaunchDarkly/test-evidence/scenario-3a-run-success.png differ diff --git a/independent-publisher-connectors/LaunchDarkly/test-evidence/scenario-3b-run-success.png b/independent-publisher-connectors/LaunchDarkly/test-evidence/scenario-3b-run-success.png new file mode 100644 index 0000000000..bd513657b1 Binary files /dev/null and b/independent-publisher-connectors/LaunchDarkly/test-evidence/scenario-3b-run-success.png differ