diff --git a/packages/cli/src/CredentialsHelper.ts b/packages/cli/src/CredentialsHelper.ts index d964daeb9b..99c92a5bc7 100644 --- a/packages/cli/src/CredentialsHelper.ts +++ b/packages/cli/src/CredentialsHelper.ts @@ -296,6 +296,9 @@ export class CredentialsHelper extends ICredentialsHelper { getScopes(type: string): string[] { const scopeProperty = this.getCredentialsProperties(type).find(({ name }) => name === 'scope'); + // edge case: scope property exists but is required to be empty string, e.g. GoToWebinar + if (scopeProperty?.default === '') return []; + if (!scopeProperty?.default || typeof scopeProperty.default !== 'string') { const errorMessage = `No \`scope\` property found for credential type: ${type}`; diff --git a/packages/editor-ui/src/Interface.ts b/packages/editor-ui/src/Interface.ts index 10457c8c41..b0b7ef2e79 100644 --- a/packages/editor-ui/src/Interface.ts +++ b/packages/editor-ui/src/Interface.ts @@ -184,6 +184,7 @@ export interface IRestApi { retryExecution(id: string, loadWorkflow?: boolean): Promise; getTimezones(): Promise; getBinaryBufferString(dataPath: string): Promise; + getScopes: (credentialType: string) => Promise; } export interface INodeTranslationHeaders { diff --git a/packages/editor-ui/src/components/NodeCredentials.vue b/packages/editor-ui/src/components/NodeCredentials.vue index d2012f1cf5..679b1cba53 100644 --- a/packages/editor-ui/src/components/NodeCredentials.vue +++ b/packages/editor-ui/src/components/NodeCredentials.vue @@ -77,6 +77,7 @@ import { showMessage } from '@/components/mixins/showMessage'; import { mapGetters } from "vuex"; import mixins from 'vue-typed-mixins'; +import { HTTP_REQUEST_NODE_TYPE } from '@/constants'; export default mixins( genericHelpers, @@ -126,6 +127,10 @@ export default mixins( credentialTypesNodeDescription (): INodeCredentialDescription[] { const node = this.node as INodeUi; + if (this.isHttpRequestNodeV2(this.node)) { + this.$emit('newHttpRequestNodeCredentialType', this.node.parameters.nodeCredentialType); + } + if (this.isGenericAuth) { const { genericAuthType } = this.node.parameters as { genericAuthType: string }; diff --git a/packages/editor-ui/src/components/NodeSettings.vue b/packages/editor-ui/src/components/NodeSettings.vue index 44b4dccb0b..2c1f539759 100644 --- a/packages/editor-ui/src/components/NodeSettings.vue +++ b/packages/editor-ui/src/components/NodeSettings.vue @@ -23,6 +23,20 @@
+
@@ -97,6 +112,7 @@ import { nodeHelpers } from '@/components/mixins/nodeHelpers'; import mixins from 'vue-typed-mixins'; import NodeExecuteButton from './NodeExecuteButton.vue'; +import { mapGetters } from 'vuex'; export default mixins( externalHooks, @@ -115,6 +131,7 @@ export default mixins( NodeExecuteButton, }, computed: { + ...mapGetters('credentials', [ 'getCredentialTypeByName' ]), customActionSelected (): boolean { return ( this.nodeValues.parameters !== undefined && @@ -210,6 +227,8 @@ export default mixins( parameters: {}, } as INodeParameters, isSupportedByHttpRequestNode: false, + activeCredential: '', + scopes: [] as string[], nodeSettings: [ { @@ -322,6 +341,21 @@ export default mixins( credentialType.authenticate !== undefined ); }, + async loadScopesNoticeData(activeCredentialType: string) { + if (!this.isHttpRequestNodeV2(this.node)) return; + + if ( + !activeCredentialType || + !activeCredentialType.endsWith('OAuth2Api') + ) { + this.scopes = []; + return; + } + + this.activeCredential = this.getCredentialTypeByName(activeCredentialType).displayName; + + this.scopes = await this.restApi().getScopes(activeCredentialType); + }, onNodeExecute () { this.$emit('execute'); }, @@ -620,6 +654,11 @@ export default mixins( height: 100%; overflow-y: auto; padding: 0 20px 200px 20px; + + // @TODO Revisit + > div > .notice[role=alert] { + margin-top: var(--spacing-s); + } } } diff --git a/packages/editor-ui/src/components/mixins/restApi.ts b/packages/editor-ui/src/components/mixins/restApi.ts index 7dad40044f..72e22cad09 100644 --- a/packages/editor-ui/src/components/mixins/restApi.ts +++ b/packages/editor-ui/src/components/mixins/restApi.ts @@ -195,6 +195,11 @@ export const restApi = Vue.extend({ getBinaryBufferString: (dataPath: string): Promise => { return self.restApi().makeRestApiRequest('GET', `/data/${dataPath}`); }, + + // Returns scopes for OAuth credential types + getScopes: (credentialType: string): Promise => { + return self.restApi().makeRestApiRequest('GET', '/oauth2-credential/scopes', { credentialType }); + }, }; }, }, diff --git a/packages/editor-ui/src/plugins/i18n/locales/en.json b/packages/editor-ui/src/plugins/i18n/locales/en.json index 481e3a403f..175759ee55 100644 --- a/packages/editor-ui/src/plugins/i18n/locales/en.json +++ b/packages/editor-ui/src/plugins/i18n/locales/en.json @@ -412,6 +412,7 @@ "nodeSettings.waitBetweenTries.description": "How long to wait between each attempt (in milliseconds)", "nodeSettings.waitBetweenTries.displayName": "Wait Between Tries (ms)", "nodeSettings.youCanUseTheHttpRequestNode": "You can use the HTTP Request node to make a custom API call with your {nodeTypeDisplayName} credential. Learn more", + "nodeSettings.scopes": "Your {activeCredential} credential has access to the following scope:
{scopes} | Your {activeCredential} credential has access to the following scopes:
{scopes}", "nodeView.addNode": "Add node", "nodeView.addSticky": "Click to add sticky note", "nodeView.confirmMessage.beforeRouteLeave.cancelButtonText": "Leave without saving",