From 4a2d828e03480008115e3fd194583fadda582db5 Mon Sep 17 00:00:00 2001 From: Vaishnavi Panchal Date: Wed, 24 Jun 2026 15:48:11 -0700 Subject: [PATCH] Add jiraIssueCreate capability to Jira manifest Adds the capability schema, parser, and Jira manifest declaration with the optional issueCreateUrl form variable. --- integrations/jira/manifest.json | 29 + manifest.schema.d.ts | 30 + manifest.schema.json | 717 +++++++++++++++++++++- schemas/base.json | 9 +- schemas/capabilities/jiraIssueCreate.json | 29 + schemas/capabilities/parser/parser.json | 20 + 6 files changed, 830 insertions(+), 4 deletions(-) create mode 100644 schemas/capabilities/jiraIssueCreate.json diff --git a/integrations/jira/manifest.json b/integrations/jira/manifest.json index c27cd89d..c0f367d3 100644 --- a/integrations/jira/manifest.json +++ b/integrations/jira/manifest.json @@ -25,6 +25,15 @@ "isSecret": false, "clearSecretsOnChange": true }, + { + "key": "issueCreateUrl", + "name": "Jira issue-create trigger URL", + "type": "uri", + "description": "Enter your Jira issue-create web trigger URL", + "isSecret": false, + "isOptional": true, + "defaultValue": "" + }, { "key": "secret", "name": "Secret", @@ -99,6 +108,26 @@ ], "templates": {}, "useStandardWebhookPayload": true + }, + "jiraIssueCreate": { + "name": "Jira issue", + "creationRequest": { + "endpoint": { + "url": "{{issueCreateUrl}}", + "method": "POST", + "headers": [{ "name": "Content-Type", "value": "application/json" }], + "hmacSignature": { + "headerName": "X-LD-Signature", + "hmacSecretFormVariableKey": "secret" + } + }, + "jsonBody": "{\"title\":\"{{{title}}}\",\"description\":\"{{{description}}}\",\"projectKey\":\"{{{projectKey}}}\",\"issueType\":\"{{{issueType}}}\",\"labels\":{{{labelsJson}}},\"sourceLink\":\"{{{sourceLink}}}\",\"sourceKind\":\"{{{sourceKind}}}\"}", + "parser": { + "issueKey": "/issueKey", + "issueUrl": "/issueUrl", + "self": "/self" + } + } } } } diff --git a/manifest.schema.d.ts b/manifest.schema.d.ts index e8f85dad..cad800ae 100644 --- a/manifest.schema.d.ts +++ b/manifest.schema.d.ts @@ -952,6 +952,7 @@ export interface Capabilities { eventsHook?: EventsHook; flagCleanup?: FlagCleanup; internalConfigurationURL?: InternalConfigurationURL; + jiraIssueCreate?: JiraIssueCreate; [k: string]: unknown; } /** @@ -1322,3 +1323,32 @@ export interface FlagCleanup { formVariables?: FlagCleanupFormVariables; [k: string]: unknown; } +/** + * Allows LaunchDarkly products to create an issue in Jira issue tracker and receive the resulting identifiers. + */ +export interface JiraIssueCreate { + name?: string; + issueCreateFormVariables?: FormVariable[]; + creationRequest: { + endpoint: Endpoint; + jsonBody?: JSONBody; + parser: JiraIssueCreateParser; + [k: string]: unknown; + }; + [k: string]: unknown; +} +export interface JiraIssueCreateParser { + /** + * JSON pointer to the created issue's key + */ + issueKey: string; + /** + * JSON pointer to the created issue's URL + */ + issueUrl: string; + /** + * JSON pointer to the Jira REST `self` link + */ + self?: string; + [k: string]: unknown; +} diff --git a/manifest.schema.json b/manifest.schema.json index e2dd56db..f480367f 100644 --- a/manifest.schema.json +++ b/manifest.schema.json @@ -23,7 +23,8 @@ "requiresOAuth", "hideOnIntegrationsPage", "oauthIntegrationKey", - "allowIntegrationConfigurations" + "allowIntegrationConfigurations", + "jiraIssueCreate" ] }, "properties": { @@ -817,7 +818,8 @@ "segmentImport", "eventsHook", "flagCleanup", - "internalConfigurationURL" + "internalConfigurationURL", + "jiraIssueCreate" ] }, "properties": { @@ -5818,6 +5820,717 @@ "type": "string", "maxLength": 2048, "pattern": "^(/[^/]+)+(?:\\?[^\\s]*)?$|^[^/]+(/[^/]+)*(?:\\?[^\\s]*)?$" + }, + "jiraIssueCreate": { + "$id": "#/capabilities/jiraIssueCreate", + "title": "Jira Issue Create", + "description": "Allows LaunchDarkly products to create an issue in Jira issue tracker and receive the resulting identifiers.", + "type": "object", + "propertyNames": { + "enum": [ + "name", + "creationRequest", + "issueCreateFormVariables" + ] + }, + "required": [ + "creationRequest" + ], + "properties": { + "name": { + "type": "string", + "maxLength": 100 + }, + "issueCreateFormVariables": { + "type": "array", + "items": { + "$id": "#/definitions/form-variable", + "title": "Form variable", + "description": "A form variable describes an object property that the LaunchDarkly admin will be prompted for when they configure an integration.", + "type": "object", + "propertyNames": { + "enum": [ + "key", + "name", + "type", + "description", + "placeholder", + "isSecret", + "isHidden", + "isOptional", + "defaultValue", + "allowedValues", + "dynamicOptions", + "dependsOn", + "multiselectOptions", + "multiselectDefaultOptions", + "disableAfterSaving", + "clearSecretsOnChange", + "hideEmpty", + "codeLanguage", + "instructionsHeading", + "instructions", + "divider" + ] + }, + "properties": { + "key": { + "$id": "#/definitions/form-variable/key", + "title": "Key", + "type": "string", + "description": "A key will be used as the token name when the variable is substituted", + "maxLength": 20, + "pattern": "^[^\\s]*$" + }, + "name": { + "$id": "#/definitions/form-variable/name", + "title": "Name", + "type": "string", + "description": "A descriptive name that will be used as the form label on the UI", + "maxLength": 50 + }, + "type": { + "$id": "#/definitions/form-variable/type", + "title": "Type", + "type": "string", + "description": "The type of the variable", + "enum": [ + "string", + "boolean", + "uri", + "enum", + "oauth", + "dynamicEnum", + "generated", + "environmentSelector", + "multiselect", + "publicKey", + "sectionHeading", + "codeEditor" + ] + }, + "description": { + "$id": "#/definitions/form-variable/description", + "title": "Description", + "type": "string", + "description": "Describes the variable in the UI. Markdown links allowed.", + "maxLength": 250 + }, + "placeholder": { + "$id": "#/definitions/form-variable/placeholder", + "title": "Description", + "type": "string", + "description": "Placeholder value to use in the form element if applicable" + }, + "isSecret": { + "$id": "#/definitions/form-variable/is-secret", + "title": "Is this variable a secret?", + "type": "boolean", + "description": "Secret variables will be masked in the UI", + "default": false + }, + "disableAfterSaving": { + "$id": "#/definitions/form-variable/disable-after-saving", + "title": "Disable after saving", + "type": "boolean", + "description": "Variables marked as disabled after saving won't be editable after they are saved", + "default": false + }, + "clearSecretsOnChange": { + "$id": "#/definitions/form-variable/clear-secrets-on-change", + "title": "Clear secrets on change", + "type": "boolean", + "description": "When true, changing this variable will require all secret variables in the same capability to be re-entered with new values", + "default": false + }, + "isOptional": { + "$id": "#/definitions/form-variable/is-optional", + "title": "Is this variable optional?", + "type": "boolean", + "description": "Variables marked as optional won't be required on the UI", + "default": false + }, + "isHidden": { + "$id": "#/definitions/form-variable/is-hidden", + "title": "Hide variable in the UI", + "type": "boolean", + "description": "Variables marked as hidden won't be displayed on the UI", + "default": false + }, + "defaultValue": { + "$id": "#/definitions/form-variable/default-value", + "title": "Default value", + "type": [ + "boolean", + "string" + ], + "description": "Default value for variable. Can be overridden by the user in the UI" + }, + "allowedValues": { + "$id": "#/definitions/form-variable/allowed-values", + "title": "Allowed values", + "type": "array", + "description": "Allowed string values for enum-type formVariables", + "items": { + "type": "string" + } + }, + "dynamicOptions": { + "$id": "#/definitions/form-variable/dynamic-options", + "title": "Dynamic options", + "type": "object", + "description": "Parser and endpoint for handling dynamicEnum", + "propertyNames": { + "enum": [ + "endpoint", + "parser" + ] + }, + "required": [ + "endpoint", + "parser" + ], + "properties": { + "endpoint": { + "$id": "#/definitions/endpoint", + "title": "Endpoint", + "description": "Properties that describe an HTTP request to an external endpoint", + "type": "object", + "propertyNames": { + "enum": [ + "url", + "method", + "headers", + "hmacSignature" + ] + }, + "properties": { + "url": { + "$id": "#/definitions/endpoint/url", + "title": "URL", + "type": "string", + "description": "URL to send the request to. You can use {{template markup}} to inject a formVariable into the url." + }, + "method": { + "$id": "#/definitions/endpoint/method", + "title": "HTTP method", + "description": "HTTP method to use when LaunchDarkly makes the request to your endpoint. You can use {{template markup}} to inject a formVariable for dynamic method resolution. Resolved value must be one of POST, PUT, PATCH, GET, or DELETE.", + "type": "string", + "anyOf": [ + { + "enum": [ + "POST", + "PUT", + "PATCH", + "GET", + "DELETE" + ] + }, + { + "pattern": "\\{\\{" + } + ] + }, + "headers": { + "$id": "#/definitions/endpoint/headers", + "title": "HTTP headers", + "description": "Headers to send with the webhook request", + "type": "array", + "items": { + "$id": "#/definitions/endpoint/header/items", + "title": "Header items", + "description": "A name and value pair to send as headers with the hook request", + "type": "object", + "propertyNames": { + "enum": [ + "name", + "value" + ] + }, + "properties": { + "name": { + "$id": "#/definitions/endpoint/header/items/name", + "title": "Name", + "type": "string", + "description": "Name of the header", + "maxLength": 50, + "pattern": "^[^\\s]*$" + }, + "value": { + "$id": "#/definitions/endpoint/header/items/value", + "title": "Value", + "type": "string", + "description": "Value of the header. Form variables can be substituted in using {{variableName}}" + } + }, + "required": [ + "name", + "value" + ] + }, + "default": [ + { + "name": "Content-Type", + "value": "application/json" + } + ], + "examples": [ + { + "name": "Content-Type", + "value": "application/json" + }, + { + "name": "Authorization", + "value": "Bearer {{apiToken}}" + } + ], + "minProperties": 1 + }, + "hmacSignature": { + "$id": "#/definitions/endpoint/hmac-signature", + "title": "HMAC signature", + "description": "Whether or not and how to configure HMAC validation on outgoing webhooks", + "type": "object", + "propertyNames": { + "enum": [ + "headerName", + "hmacSecretFormVariableKey" + ] + }, + "properties": { + "headerName": { + "$id": "#/definitions/endpoint/hmac-signature/header-name", + "title": "HMAC signature header name", + "description": "The name of the HMAC signature header", + "type": "string" + }, + "hmacSecretFormVariableKey": { + "$id": "#/definitions/endpoint/hmac-signature/secret-form-variable-field", + "title": "HMAC secret form variable key", + "description": "The name of the form variable field that corresponds to the HMAC encryption secret", + "type": "string" + } + } + } + }, + "required": [ + "url", + "method" + ] + }, + "parser": { + "$id": "#/definitions/form-variable/dynamic-options/parser", + "title": "Dynamic options parser", + "description": "Mapping of property names to location in JSON response", + "type": "object", + "required": [ + "optionsPath", + "optionsItems" + ], + "propertyNames": { + "enum": [ + "optionsPath", + "optionsItems" + ] + }, + "properties": { + "optionsPath": { + "$id": "#/definitions/form-variable/dynamic-options/parser/options-path", + "title": "Options array path", + "description": "JSON path to the array containing options for parsing", + "type": "string" + }, + "optionsItems": { + "$id": "#/definitions/form-variable/dynamic-options/parser/options-items", + "title": "Options array", + "propertyNames": { + "enum": [ + "label", + "value" + ] + }, + "required": [ + "label", + "value" + ], + "properties": { + "label": { + "$id": "#/definitions/form-variable/dynamic-options/parser/options-items/label", + "title": "Label", + "description": "Relative JSON path to values for each item in the options array to be used as UI dropdown labels", + "type": "string" + }, + "value": { + "$id": "#/definitions/form-variable/dynamic-options/parser/options-items/value", + "title": "Value", + "description": "Relative JSON path to values for each item in the options array to be used as dropdown values", + "type": "string" + } + } + } + } + } + } + }, + "dependsOn": { + "$id": "#/definitions/form-variable/depends-on", + "title": "Depends on", + "type": "array", + "description": "Dependency configuration to control the state and visibility of the form field.", + "items": { + "type": "object", + "propertyNames": { + "enum": [ + "variableKey", + "variableLocation", + "conditions", + "action" + ] + }, + "required": [ + "variableKey", + "variableLocation", + "conditions", + "action" + ], + "properties": { + "variableKey": { + "$id": "#/definitions/form-variable/depends-on/dependee-key", + "title": "Variable key", + "type": "string", + "description": "Key for the form variable that this form field depends on", + "maxLength": 20, + "pattern": "^[^\\s]*$" + }, + "variableLocation": { + "$id": "#/definitions/form-variable/depends-on/dependee-key-location", + "title": "Variable Location", + "type": "string", + "description": "Which form variable type is the variable key defined or located in?", + "enum": [ + "approvalFormVariables", + "environmentFormVariables", + "formVariables" + ] + }, + "action": { + "$id": "#/definitions/form-variable/depends-on/action", + "title": "Action", + "type": "string", + "enum": [ + "hideField", + "showField" + ], + "description": "Action to be taken when your defined conditions evaluates to true" + }, + "conditions": { + "$id": "#/definitions/form-variable/depends-on/conditions", + "title": "Conditions", + "type": "array", + "description": "Conditional configurations to be evaluated to decide whether an action should taken for the form field.", + "items": { + "type": "object", + "propertyNames": { + "enum": [ + "operator", + "value" + ] + }, + "required": [ + "operator", + "value" + ], + "properties": { + "operator": { + "$id": "#/definitions/form-variable/depends-on/conditions/operator", + "title": "Operator", + "type": "string", + "description": "Name of the operator for evaluating a condition.", + "enum": [ + "lessThan", + "lessThanOrEqualTo", + "greaterThan", + "greaterThanOrEqualTo", + "equalTo", + "notEqual", + "contains", + "startsWith", + "endsWith" + ] + }, + "value": { + "$id": "#/definitions/form-variable/depends-on/conditions/value", + "title": "Value", + "type": "string", + "maxLength": 250, + "description": "Target value that conditions are evaluated against" + } + } + } + } + } + } + }, + "multiselectOptions": { + "$id": "#/definitions/form-variable/multi-select-options", + "title": "Options array", + "type": "array", + "items": { + "$id": "#/definitions/multiselect-option-item", + "title": "Multi select option item", + "description": "A multi select option item", + "type": "object", + "propertyNames": { + "enum": [ + "label", + "value" + ] + }, + "required": [ + "label", + "value" + ], + "properties": { + "label": { + "$id": "#/definitions/multiselect-option-item/label", + "title": "Label", + "description": "Label of the multi select option", + "type": "string" + }, + "value": { + "$id": "#/definitions/multiselect-option-item/value", + "title": "Value", + "description": "Value of the multi select option", + "type": "string" + } + } + } + }, + "multiselectDefaultOptions": { + "$id": "#/definitions/form-variable/multiselect-default-options", + "title": "Multi select default options", + "type": "array", + "description": "Default options to be selected when the multi select is first rendered", + "items": { + "type": "string" + } + }, + "hideEmpty": { + "$id": "#/definitions/form-variable/hide-empty", + "title": "Hide value in UI when empty", + "type": "boolean", + "description": "Variables marked as hideEmpty won't be shown in the UI if they are empty", + "default": false + }, + "codeLanguage": { + "$id": "#/definitions/form-variable/code-language", + "title": "Code language", + "type": "string", + "description": "Hint for syntax highlighting in codeEditor variables." + }, + "instructionsHeading": { + "$id": "#/definitions/form-variable/instructions-heading", + "title": "Instructions heading", + "type": "string", + "description": "Optional heading to display above the instructions list." + }, + "instructions": { + "$id": "#/definitions/form-variable/instructions", + "title": "Instructions", + "type": "array", + "description": "Optional list of setup instructions displayed with codeEditor variables.", + "items": { + "type": "string" + } + }, + "divider": { + "$id": "#/definitions/form-variable/divider", + "title": "Divider", + "type": "boolean", + "description": "Whether to show a divider after a sectionHeading variable.", + "default": false + } + }, + "required": [ + "key", + "name", + "type", + "description" + ] + }, + "default": [] + }, + "creationRequest": { + "required": [ + "endpoint", + "parser" + ], + "properties": { + "endpoint": { + "$id": "#/definitions/endpoint", + "title": "Endpoint", + "description": "Properties that describe an HTTP request to an external endpoint", + "type": "object", + "propertyNames": { + "enum": [ + "url", + "method", + "headers", + "hmacSignature" + ] + }, + "properties": { + "url": { + "$id": "#/definitions/endpoint/url", + "title": "URL", + "type": "string", + "description": "URL to send the request to. You can use {{template markup}} to inject a formVariable into the url." + }, + "method": { + "$id": "#/definitions/endpoint/method", + "title": "HTTP method", + "description": "HTTP method to use when LaunchDarkly makes the request to your endpoint. You can use {{template markup}} to inject a formVariable for dynamic method resolution. Resolved value must be one of POST, PUT, PATCH, GET, or DELETE.", + "type": "string", + "anyOf": [ + { + "enum": [ + "POST", + "PUT", + "PATCH", + "GET", + "DELETE" + ] + }, + { + "pattern": "\\{\\{" + } + ] + }, + "headers": { + "$id": "#/definitions/endpoint/headers", + "title": "HTTP headers", + "description": "Headers to send with the webhook request", + "type": "array", + "items": { + "$id": "#/definitions/endpoint/header/items", + "title": "Header items", + "description": "A name and value pair to send as headers with the hook request", + "type": "object", + "propertyNames": { + "enum": [ + "name", + "value" + ] + }, + "properties": { + "name": { + "$id": "#/definitions/endpoint/header/items/name", + "title": "Name", + "type": "string", + "description": "Name of the header", + "maxLength": 50, + "pattern": "^[^\\s]*$" + }, + "value": { + "$id": "#/definitions/endpoint/header/items/value", + "title": "Value", + "type": "string", + "description": "Value of the header. Form variables can be substituted in using {{variableName}}" + } + }, + "required": [ + "name", + "value" + ] + }, + "default": [ + { + "name": "Content-Type", + "value": "application/json" + } + ], + "examples": [ + { + "name": "Content-Type", + "value": "application/json" + }, + { + "name": "Authorization", + "value": "Bearer {{apiToken}}" + } + ], + "minProperties": 1 + }, + "hmacSignature": { + "$id": "#/definitions/endpoint/hmac-signature", + "title": "HMAC signature", + "description": "Whether or not and how to configure HMAC validation on outgoing webhooks", + "type": "object", + "propertyNames": { + "enum": [ + "headerName", + "hmacSecretFormVariableKey" + ] + }, + "properties": { + "headerName": { + "$id": "#/definitions/endpoint/hmac-signature/header-name", + "title": "HMAC signature header name", + "description": "The name of the HMAC signature header", + "type": "string" + }, + "hmacSecretFormVariableKey": { + "$id": "#/definitions/endpoint/hmac-signature/secret-form-variable-field", + "title": "HMAC secret form variable key", + "description": "The name of the form variable field that corresponds to the HMAC encryption secret", + "type": "string" + } + } + } + }, + "required": [ + "url", + "method" + ] + }, + "jsonBody": { + "$id": "#/definitions/json-body", + "title": "JSON Body", + "description": "Template string used to render the JSON request body", + "type": "string" + }, + "parser": { + "title": "Jira Issue Create Parser", + "type": "object", + "required": [ + "issueKey", + "issueUrl" + ], + "propertyNames": { + "enum": [ + "issueKey", + "issueUrl", + "self" + ] + }, + "properties": { + "issueKey": { + "type": "string", + "description": "JSON pointer to the created issue's key" + }, + "issueUrl": { + "type": "string", + "description": "JSON pointer to the created issue's URL" + }, + "self": { + "type": "string", + "description": "JSON pointer to the Jira REST `self` link" + } + } + } + } + } + } } } }, diff --git a/schemas/base.json b/schemas/base.json index 53eeb7ba..7b40580a 100644 --- a/schemas/base.json +++ b/schemas/base.json @@ -23,7 +23,8 @@ "requiresOAuth", "hideOnIntegrationsPage", "oauthIntegrationKey", - "allowIntegrationConfigurations" + "allowIntegrationConfigurations", + "jiraIssueCreate" ] }, "properties": { @@ -283,7 +284,8 @@ "segmentImport", "eventsHook", "flagCleanup", - "internalConfigurationURL" + "internalConfigurationURL", + "jiraIssueCreate" ] }, "properties": { @@ -337,6 +339,9 @@ }, "internalConfigurationURL": { "$ref": "schemas/capabilities/internalConfigurationUrl.json#/internalConfigurationURL" + }, + "jiraIssueCreate": { + "$ref": "schemas/capabilities/jiraIssueCreate.json#/jiraIssueCreate" } } }, diff --git a/schemas/capabilities/jiraIssueCreate.json b/schemas/capabilities/jiraIssueCreate.json new file mode 100644 index 00000000..f4ada3c2 --- /dev/null +++ b/schemas/capabilities/jiraIssueCreate.json @@ -0,0 +1,29 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "jiraIssueCreate": { + "$id": "#/capabilities/jiraIssueCreate", + "title": "Jira Issue Create", + "description": "Allows LaunchDarkly products to create an issue in Jira issue tracker and receive the resulting identifiers.", + "type": "object", + "propertyNames": { + "enum": ["name", "creationRequest", "issueCreateFormVariables"] + }, + "required": ["creationRequest"], + "properties": { + "name": { "type": "string", "maxLength": 100 }, + "issueCreateFormVariables": { + "type": "array", + "items": { "$ref": "../definitions.json#/formVariable" }, + "default": [] + }, + "creationRequest": { + "required": ["endpoint", "parser"], + "properties": { + "endpoint": { "$ref": "../definitions.json#/endpoint" }, + "jsonBody": { "$ref": "../definitions.json#/jsonBody" }, + "parser": { "$ref": "parser/parser.json#/jiraIssueCreateParser" } + } + } + } + } +} diff --git a/schemas/capabilities/parser/parser.json b/schemas/capabilities/parser/parser.json index 79203465..7cbc3941 100644 --- a/schemas/capabilities/parser/parser.json +++ b/schemas/capabilities/parser/parser.json @@ -145,5 +145,25 @@ "type": "string" } } + }, + "jiraIssueCreateParser": { + "title": "Jira Issue Create Parser", + "type": "object", + "required": ["issueKey", "issueUrl"], + "propertyNames": { "enum": ["issueKey", "issueUrl", "self"] }, + "properties": { + "issueKey": { + "type": "string", + "description": "JSON pointer to the created issue's key" + }, + "issueUrl": { + "type": "string", + "description": "JSON pointer to the created issue's URL" + }, + "self": { + "type": "string", + "description": "JSON pointer to the Jira REST `self` link" + } + } } }