import { EditorLanguage, ExtendedIntegrationPluginId, FormComponentType, FormItem, InputDataType, IntegrationAuthType, Plugin, PluginResponseType, PluginType } from '../../types/index.js'; import { PUBLIC_INTEGRATIONS_LOGO_URL, DOCS_BASE_URL } from './constants.js'; import { BASE_CLIENT_ID, BASE_CLIENT_SECRET, BASE_SCOPE, BASE_SUBJECT_TOKEN_SOURCE, BASE_SUBJECT_TOKEN_STATIC_TOKEN, BASE_SUBJECT_TOKEN_TYPE, BASE_TOKEN_URL } from './shared/index.js'; import { authSections } from './shared/index.js'; export const SalesforcePluginVersions = { V1: '0.0.1', V2: '0.0.2' }; const CONNECTION_TYPE_FIELD = 'connection.auth.method.case'; const ACTION_DROPDOWN: { label: string; value: string; children: { label: string; value: string }[] }[] = [ { label: 'Query with SOQL', value: 'soql', children: [{ label: 'SOQL', value: '1' }] }, { label: 'Single object actions', value: 'crud', children: [ { label: 'Create a single object', value: '1' }, { label: 'Update a single object', value: '2' }, { label: 'Delete a single object', value: '3' }, { label: 'Read a single object', value: '4' } ] }, { label: 'Bulk actions', value: 'bulk', children: [ { label: 'Create several objects', value: '1' }, { label: 'Update several objects', value: '2' }, { label: 'Delete several objects', value: '3' }, { label: 'Upsert (update or insert) several objects', value: '4' } ] } ]; export const SalesforcePlugin: Plugin = { id: 'salesforce', name: 'Salesforce', moduleName: 'SalesforcePlugin', modulePath: 'plugins/salesforce/SalesforcePlugin', iconLocation: `${PUBLIC_INTEGRATIONS_LOGO_URL}/salesforce.png`, docsUrl: `${DOCS_BASE_URL}/salesforce`, type: PluginType.API, responseType: PluginResponseType.JSON, hasRawRequest: true, hasMetadata: true, datasourceTemplate: { sections: [ { name: 'main', items: [ { label: 'Display name', name: 'name', startVersion: SalesforcePluginVersions.V1, componentType: FormComponentType.INPUT_TEXT, rules: [{ required: true, message: 'Display name is required' }] }, { label: 'Instance URL', name: 'connection.instanceUrl', startVersion: SalesforcePluginVersions.V1, componentType: FormComponentType.INPUT_TEXT, placeholder: 'https://DOMAIN.my.salesforce.com', rules: [{ required: true, message: 'Instance URL is required' }] }, { // it could be removed if we provide a way to pass token to plugin.metadata and plugin.test // plugin.execute have access to execution context, which already contain token label: 'Token', name: 'authConfig.authToken', startVersion: SalesforcePluginVersions.V1, componentType: FormComponentType.INPUT_TEXT, initialValue: '{{oauth.token}}', hidden: true, display: { show: { [CONNECTION_TYPE_FIELD]: ['clientCredentialsFlow'] } } }, { name: 'connection.auth.method.case', label: 'Authentication', startVersion: SalesforcePluginVersions.V2, componentType: FormComponentType.DROPDOWN, initialValue: 'clientCredentialsFlow', options: [ { displayName: 'OAuth 2.0 Client Credentials Grant', value: 'clientCredentialsFlow', key: 'clientCredentialsFlow' }, { displayName: 'OAuth 2.0 Token Exchange', value: IntegrationAuthType.OAUTH2_TOKEN_EXCHANGE_PROTO, key: IntegrationAuthType.OAUTH2_TOKEN_EXCHANGE_PROTO } ] }, // CLIENT CREDENTIALS FLOW { label: 'Token URL', name: 'connection.auth.method.value.tokenUrl', startVersion: SalesforcePluginVersions.V2, componentType: FormComponentType.DYNAMIC_INPUT_TEXT, rules: [{ required: true, message: 'Token URL is required' }], tooltip: { markdownText: `The endpoint for authorization server. This is used to get the access token.` }, singleLine: true, display: { show: { 'connection.auth.method.case': ['clientCredentialsFlow'] } }, placeholder: 'https://login.salesforce.com/services/oauth2/token' }, { label: 'Consumer key', name: 'connection.auth.method.value.clientId', startVersion: SalesforcePluginVersions.V2, componentType: FormComponentType.DYNAMIC_INPUT_TEXT, placeholder: `my consumer key`, rules: [{ required: true, message: 'Consumer Key is required' }], tooltip: { markdownText: `A public identifier used to identify this Salesforce consumer to the authorization/token servers.` }, singleLine: true, display: { show: { 'connection.auth.method.case': ['clientCredentialsFlow'] } } }, { label: 'Consumer secret', name: 'connection.auth.method.value.clientSecret', startVersion: SalesforcePluginVersions.V2, componentType: FormComponentType.INPUT_TEXT, rules: [{ required: true, message: 'Consumer secret is required' }], placeholder: `my consumer secret`, dataType: InputDataType.PASSWORD, tooltip: { markdownText: `A secret shared between the Salesforce consumer and the authorizing/token servers to verify the Consumer Key.` }, singleLine: false, display: { show: { 'connection.auth.method.case': ['clientCredentialsFlow'] } } }, // OAUTH2 TOKEN EXCHANGE // This is a field that will always be hidden to users. // It exists to tell the agent WHERE to look on the datasource configuration for the authType. // For more info, see addTokenIfNeeded.go in the orchestrator. { label: '', name: 'authTypeField', hidden: true, initialValue: 'authTypeOnBehalfOfToken', componentType: FormComponentType.INPUT_TEXT, rules: [{ required: false }], startVersion: SalesforcePluginVersions.V2, // This is a newer convention in the agent that allows us to specify the field to look at // when determining authType. We only include this for OAUTH2_TOKEN_EXCHANGE so the // existing clientCredentialsFlow works as-is. // See: https://github.com/superblocksteam/orchestrator/blob/main/internal/auth/add_token_if_needed.go display: { show: { [CONNECTION_TYPE_FIELD]: [IntegrationAuthType.OAUTH2_TOKEN_EXCHANGE_PROTO] } } }, // Another field to send a value to the orchestrator. We need this because our current // templates make it difficult/impossible to change the value of a field programmatically. // Ideally the value for authType would be dependent upon which connection type the user // has selected. { label: '', name: 'authTypeOnBehalfOfToken', hidden: true, initialValue: 'oauth-token-exchange', componentType: FormComponentType.INPUT_TEXT, startVersion: SalesforcePluginVersions.V2, display: { show: { [CONNECTION_TYPE_FIELD]: [IntegrationAuthType.OAUTH2_TOKEN_EXCHANGE_PROTO] } } }, { ...BASE_SUBJECT_TOKEN_SOURCE, startVersion: SalesforcePluginVersions.V2, display: { show: { [CONNECTION_TYPE_FIELD]: [IntegrationAuthType.OAUTH2_TOKEN_EXCHANGE_PROTO] } } } as FormItem, { ...BASE_SUBJECT_TOKEN_STATIC_TOKEN, startVersion: SalesforcePluginVersions.V2, display: { show: { [CONNECTION_TYPE_FIELD]: [IntegrationAuthType.OAUTH2_TOKEN_EXCHANGE_PROTO], 'authConfig.subjectTokenSource': ['SUBJECT_TOKEN_SOURCE_STATIC_TOKEN'] } } } as FormItem, { ...BASE_SUBJECT_TOKEN_TYPE, startVersion: SalesforcePluginVersions.V2, placeholder: 'urn:ietf:params:oauth:token-type:access_token', display: { show: { [CONNECTION_TYPE_FIELD]: [IntegrationAuthType.OAUTH2_TOKEN_EXCHANGE_PROTO] } } } as FormItem, { ...BASE_TOKEN_URL, startVersion: SalesforcePluginVersions.V2, display: { show: { [CONNECTION_TYPE_FIELD]: [IntegrationAuthType.OAUTH2_TOKEN_EXCHANGE_PROTO] } } } as FormItem, { ...BASE_CLIENT_ID, startVersion: SalesforcePluginVersions.V2, rules: [{ required: false }], display: { show: { [CONNECTION_TYPE_FIELD]: [IntegrationAuthType.OAUTH2_TOKEN_EXCHANGE_PROTO] } } } as FormItem, { ...BASE_CLIENT_SECRET, startVersion: SalesforcePluginVersions.V2, rules: [{ required: false }], display: { show: { [CONNECTION_TYPE_FIELD]: [IntegrationAuthType.OAUTH2_TOKEN_EXCHANGE_PROTO] } } } as FormItem, { ...BASE_SCOPE, startVersion: SalesforcePluginVersions.V2, display: { show: { [CONNECTION_TYPE_FIELD]: [IntegrationAuthType.OAUTH2_TOKEN_EXCHANGE_PROTO] } } } as FormItem ] }, ...authSections({ startVersion: SalesforcePluginVersions.V1, endVersion: SalesforcePluginVersions.V1, pluginId: ExtendedIntegrationPluginId.SALESFORCE, pluginName: 'Salesforce', defaultMethod: 'clientCredentialsFlow', enabledMethods: { clientCredentialsFlow: true }, allowNone: false, fieldNamesToHide: [], isProto: true }) ] }, actionTemplate: { sections: [ { name: 'main', items: [ { label: 'Action', name: 'salesforceAction.value.action', // expects .value.action that is a enum secondaryName: 'salesforceAction.case', // expects proto top level oneof case startVersion: SalesforcePluginVersions.V1, componentType: FormComponentType.ONEOF_ACTION_DROPDOWN, actionOptions: ACTION_DROPDOWN, initialValue: '1', secondaryInitialValue: 'soql' }, { label: '', // Query name: 'salesforceAction.value.sqlBody', startVersion: SalesforcePluginVersions.V1, componentType: FormComponentType.CODE_EDITOR, language: EditorLanguage.SQL, initialValue: '', rules: [{ required: true, message: 'SOQL Query is required' }], display: { show: { 'salesforceAction.value.action': ['1'], 'salesforceAction.case': ['soql'] } } }, { label: 'Resource type', name: 'salesforceAction.value.resourceType', startVersion: SalesforcePluginVersions.V1, componentType: FormComponentType.DYNAMIC_INPUT_TEXT, initialValue: '', rules: [{ required: true, message: 'Resource type is required' }], display: { show: { 'salesforceAction.value.action': ['1', '2', '3', '4'], 'salesforceAction.case': ['crud'] } } }, { label: 'Resource ID', name: 'salesforceAction.value.resourceId', startVersion: SalesforcePluginVersions.V1, componentType: FormComponentType.DYNAMIC_INPUT_TEXT, rules: [{ required: true, message: 'Resource ID is required' }], display: { show: { 'salesforceAction.value.action': ['2', '3', '4'], 'salesforceAction.case': ['crud'] } } }, { label: 'Resource body', name: 'salesforceAction.value.resourceBody', startVersion: SalesforcePluginVersions.V1, componentType: FormComponentType.CODE_EDITOR, language: EditorLanguage.JSON, rules: [{ required: true, message: 'Resource body is required' }], display: { show: { 'salesforceAction.value.action': ['1', '2'], 'salesforceAction.case': ['crud'] } }, placeholder: '{"Name": "Example Company Name", "Industry": "Health Care", "Website": "www.example.com"}' }, { label: 'Resource type', name: 'salesforceAction.value.resourceType', startVersion: SalesforcePluginVersions.V1, componentType: FormComponentType.DYNAMIC_INPUT_TEXT, rules: [{ required: true, message: 'Resource Type is required' }], display: { show: { 'salesforceAction.value.action': ['1', '2', '3', '4'], 'salesforceAction.case': ['bulk'] } } }, { label: 'External ID (optional)', name: 'salesforceAction.value.externalId', startVersion: SalesforcePluginVersions.V1, componentType: FormComponentType.DYNAMIC_INPUT_TEXT, tooltip: { markdownText: 'External ID is a field that uniquely identifies records outside of Salesforce. The upsert command will use this field to determine if a record should be created or updated' }, placeholder: 'id', display: { show: { 'salesforceAction.value.action': ['4'], // upsert 'salesforceAction.case': ['bulk'] } } }, { label: 'Bulk body', name: 'salesforceAction.value.resourceBody', startVersion: SalesforcePluginVersions.V1, componentType: FormComponentType.CODE_EDITOR, language: EditorLanguage.JSON, rules: [{ required: true, message: 'Bulk body is required' }], display: { show: { 'salesforceAction.value.action': ['1', '2', '3', '4'], 'salesforceAction.case': ['bulk'] } }, placeholder: '[{"Name": "Example Company Name1", "Website": "www.example.com"}, {"Name": "Example Company Name2", "Website": "www.example2.com"}]' } ] } ] } };