Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Discoverability flow and scopes notice for HTTP Request node #3229

Merged
merged 116 commits into from
May 19, 2022
Merged
Show file tree
Hide file tree
Changes from 100 commits
Commits
Show all changes
116 commits
Select commit Hold shift + click to select a range
2808ee9
:sparkles: Added node credentials type proxy. Changed node credential…
alexgrozav Apr 26, 2022
d074e52
:zap: Add computed property from versioning branch
ivov Apr 28, 2022
403e570
:bug: Fix cred ref lost and unsaved
ivov Apr 29, 2022
59cb1a2
:zap: Make options consistent with cred type names
ivov May 2, 2022
ca0f0df
:zap: Use prop to set component order
ivov May 2, 2022
81aeefa
:zap: Use constant and version
ivov May 2, 2022
3d5b92b
:zap: Fix rendering for generic auth creds
ivov May 2, 2022
452139f
:zap: Mark as required on first selection
ivov May 2, 2022
c19df79
:zap: Implement discoverability flow
ivov May 2, 2022
9b3e9a2
:zap: Mark as required on subsequent selections
ivov May 2, 2022
bd02a61
:zap: Fix marking as required after cred deletion
ivov May 2, 2022
791ed95
:zap: Refactor to clean up
ivov May 3, 2022
aedbbe5
:twisted_rightwards_arrows: Merge parent branch
ivov May 3, 2022
cacd43f
:zap: Detect position automatically
ivov May 3, 2022
9bd62c7
:twisted_rightwards_arrows: Merge parent branch
ivov May 3, 2022
fa4f484
:zap: Add i18n to option label
ivov May 3, 2022
b290ad5
:twisted_rightwards_arrows: Merge parent branch
ivov May 3, 2022
4997569
:zap: Hide subtitle for custom action
ivov May 3, 2022
64e08c8
:zap: Detect active credential type
ivov May 3, 2022
aa986e1
:zap: Prop drilling to re-render select
ivov May 4, 2022
0d4dbdc
:fire: Remove unneeded property
ivov May 4, 2022
5743da3
:pencil2: Rename arg
ivov May 4, 2022
2904562
:fire: Remove unused import
ivov May 4, 2022
238565b
:fire: Remove unneeded getters
ivov May 4, 2022
9f15c93
:fire: Remove unused import
ivov May 4, 2022
75eea89
:zap: Generalize cred component positioning
ivov May 4, 2022
77747f8
:zap: Set up request
ivov May 4, 2022
04997c5
:bug: Fix edge case in endpoint
ivov May 4, 2022
1efedfd
:zap: Display scopes alert box
ivov May 4, 2022
2eaf05e
:rewind: Revert "Generalize cred comp positioning"
ivov May 5, 2022
fec2fc1
:zap: Consolidate HTTPRN check
ivov May 5, 2022
24b8a56
:zap: Fix hue percentage to degree
ivov May 5, 2022
cb96d78
:fire: Remove unused import
ivov May 5, 2022
b8357e0
:fire: Remove unused import
ivov May 5, 2022
9151dec
:fire: Remove unused class
ivov May 5, 2022
cf54e6a
:fire: Remove unused import
ivov May 5, 2022
f7cff03
:blue_book: Create type for HTTPRN v2 auth params
ivov May 5, 2022
bc4aaa6
:pencil2: Rename check
ivov May 5, 2022
43f7e2c
:fire: Remove unused import
ivov May 5, 2022
760ef5a
:pencil2: Add i18n to `reportUnsetCredential()`
ivov May 5, 2022
0a221f3
:zap: Refactor Alex's spacing changes
ivov May 5, 2022
5c11a1b
:twisted_rightwards_arrows: Merge parent branch
ivov May 5, 2022
d8de1cf
:twisted_rightwards_arrows: Merge parent branch
ivov May 5, 2022
af455e0
:twisted_rightwards_arrows: Merge parent branch
ivov May 5, 2022
eebc6dd
:twisted_rightwards_arrows: Merge parent branch
ivov May 5, 2022
e530bd4
:zap: Post-merge fixes
ivov May 5, 2022
a708b31
:twisted_rightwards_arrows: Merge scopes branch
ivov May 5, 2022
4450b6c
:zap: Add docs link
ivov May 5, 2022
6d2bb7e
:fire: Exclude Notion OAuth cred
ivov May 5, 2022
e983266
:pencil2: Update copy
ivov May 5, 2022
5501bc8
:pencil2: Rename param
ivov May 5, 2022
a294201
:art: Reposition notice and simplify styling
ivov May 5, 2022
aa89404
:pencil2: Update copy
ivov May 5, 2022
ba9c85e
:pencil2: Update copy
ivov May 6, 2022
216d2e0
:zap: Hide params during custom action
ivov May 9, 2022
e543b04
:zap: Show notice if any cred type supported
ivov May 9, 2022
83b4225
:twisted_rightwards_arrows: Merge parent branch
ivov May 9, 2022
dc8d266
:bug: Prevent scopes text overflow
ivov May 9, 2022
1420386
:fire: Remove superfluous check
ivov May 9, 2022
e6332a7
:pencil2: Break up docstring
ivov May 9, 2022
2c8805c
:art: Tweak notice styling
ivov May 9, 2022
6d07fc2
:zap: Reorder cred param in Webhook node
ivov May 9, 2022
4a6e78d
:pencil2: Shorten cred name in scopes notice
ivov May 9, 2022
0380403
:test_tube: Update Notice snapshots
ivov May 9, 2022
3f2c4a6
:bug: Fix check when `globalRole` is `undefined`
ivov May 9, 2022
1c0cb25
:rewind: Revert 3f2c4a6
ivov May 9, 2022
f06d77d
:zap: Apply feedback from Product
ivov May 10, 2022
517079f
:test_tube: Update snapshot
ivov May 10, 2022
7b67774
:zap: Adjust regex expansion pattern for singular
ivov May 10, 2022
c607997
:twisted_rightwards_arrows: Merge parent branch
ivov May 11, 2022
8e307c6
:fire: Remove unused import
ivov May 11, 2022
804896d
:fire: Remove logging
ivov May 11, 2022
dc743f8
:zap: Make `somethingElse` key more unique
ivov May 11, 2022
7e961a5
:zap: Move something else to constants
ivov May 11, 2022
638d93d
:zap: Consolidate notice component
ivov May 11, 2022
4ab10d0
:zap: Apply latest feedback
ivov May 11, 2022
f961612
:test_tube: Update tests
ivov May 11, 2022
baa4b08
:test_tube: Update snapshot
ivov May 11, 2022
93bfb7e
:pencil2: Fix singular version
ivov May 11, 2022
4b2ecf8
:test_tube: Finalize tests
ivov May 11, 2022
d6e2895
:pencil2: Rename constant
ivov May 11, 2022
4f3738d
:test_tube: Expand tests
ivov May 11, 2022
1640e9b
:fire: Remove `truncate` prop
ivov May 11, 2022
dac800d
:truck: Move scopes fetching to store
ivov May 12, 2022
9cbcf0b
:truck: Move method to component
ivov May 12, 2022
b445f72
:zap: Use constant
ivov May 12, 2022
6f0ed1c
:zap: Refactor `Notice` component
ivov May 12, 2022
7d7773e
:test_tube: Update tests
ivov May 12, 2022
e475399
:fire: Remove unused keys
ivov May 12, 2022
fc3fc95
:zap: Inject custom API call option
ivov May 12, 2022
4acf25b
:fire: Remove unused props
ivov May 12, 2022
bc58827
:art: Use `compact` prop
ivov May 12, 2022
56fd98b
:test_tube: Update snapshots
ivov May 12, 2022
989ddc1
:truck: Move scopes to store
ivov May 12, 2022
705d6cd
:truck: Move `nodeCredentialTypes` to parent
ivov May 12, 2022
04eff36
:pencil2: Rename cred types per branding
ivov May 12, 2022
9bf01d8
:bug: Clear scopes when none
ivov May 12, 2022
5f0d5b9
:zap: Add default
ivov May 12, 2022
9c7a086
:truck: Move `newHttpRequestNodeCredentialType` to parent
ivov May 12, 2022
f2e901a
:fire: Remove test data
ivov May 13, 2022
43d1ffe
:zap: Separate lines for readability
ivov May 13, 2022
64db4e4
:zap: Change reference from node to node name
ivov May 13, 2022
3d44b18
:pencil2: Rename i18n keys
ivov May 13, 2022
6fa4c9c
:zap: Refactor OAuth check
ivov May 13, 2022
7991e5a
:fire: Remove unused key
ivov May 13, 2022
9a7686f
:truck: Move `OAuth1/2 API` to i18n
ivov May 13, 2022
db8225c
:zap: Refactor `skipCheck`
ivov May 13, 2022
a6d9e26
:zap: Add `stopPropagation` and `preventDefault`
ivov May 13, 2022
7f304a3
:truck: Move active credential scopes logic to store
ivov May 13, 2022
959d57d
:art: Fix spacing for `NodeWebhooks` component
ivov May 13, 2022
4f1c1e9
:zap: Implement feedback
ivov May 16, 2022
6612848
:zap: Update HTTPRN default and issue copy
ivov May 16, 2022
343b148
:twisted_rightwards_arrows: Merge master
ivov May 16, 2022
333506a
:twisted_rightwards_arrows: Merge parent branch
ivov May 19, 2022
2ac022e
Refactor to use `CredentialsSelect` param (#3304)
ivov May 19, 2022
9389a32
Extend metrics for HTTP Request node (#3282)
ivov May 19, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions packages/cli/src/CredentialsHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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}`;

Expand Down
72 changes: 32 additions & 40 deletions packages/design-system/src/components/N8nNotice/Notice.vue
Original file line number Diff line number Diff line change
@@ -1,24 +1,14 @@
<template>
<div :id="id" :class="classes" role="alert">
<div :id="id" :class="classes" role="alert" @click=onClick>
<div class="notice-content">
<n8n-text size="small">
<n8n-text size="small" :compact="true">
<slot>
<span
:class="expanded ? $style['expanded'] : $style['truncated']"
:class="showFullContent ? $style['expanded'] : $style['truncated']"
:id="`${id}-content`"
role="region"
v-html="sanitizedContent"
v-html="sanitizeHtml(showFullContent ? fullContent : content)"
/>
<span v-if="canTruncate">
<a
role="button"
:aria-controls="`${id}-content`"
:aria-expanded="canTruncate && !expanded ? 'false' : 'true'"
@click="toggleExpanded"
>
{{ t(expanded ? 'notice.showLess' : 'notice.showMore') }}
</a>
</span>
</slot>
</n8n-text>
</div>
Expand All @@ -30,9 +20,7 @@ import Vue from 'vue';
import sanitizeHtml from 'sanitize-html';
import N8nText from "../../components/N8nText";
import Locale from "../../mixins/locale";
import {uid} from "../../utils";

const DEFAULT_TRUNCATION_MAX_LENGTH = 150;
import { uid } from "../../utils";

export default Vue.extend({
name: 'n8n-notice',
Expand All @@ -49,15 +37,11 @@ export default Vue.extend({
type: String,
default: 'warning',
},
truncateAt: {
type: Number,
default: 150,
},
truncate: {
type: Boolean,
default: false,
},
content: {
required: true,
type: String,
},
fullContent: {
type: String,
default: '',
},
Expand All @@ -67,7 +51,7 @@ export default Vue.extend({
},
data() {
return {
expanded: false,
showFullContent: false,
};
},
computed: {
Expand All @@ -79,38 +63,46 @@ export default Vue.extend({
];
},
canTruncate(): boolean {
return this.truncate && this.content.length > this.truncateAt;
},
truncatedContent(): string {
if (!this.canTruncate || this.expanded) {
return this.content;
}

return this.content.slice(0, this.truncateAt as number) + '...';
},
sanitizedContent(): string {
return sanitizeHtml(this.truncatedContent);
return this.fullContent !== undefined;
},
},
methods: {
toggleExpanded() {
this.expanded = !this.expanded;
this.showFullContent = !this.showFullContent;
},
sanitizeHtml(text: string): string {
return sanitizeHtml(
text, {
allowedAttributes: { a: ['data-key', 'href', 'target'] },
}
);
},
onClick(e) {
if (e.target.localName !== 'a') return;

if (e.target.dataset.key === 'show-less') {
this.showFullContent = false;
} else if (e.target.dataset.key === 'toggle-expand') {
this.showFullContent = !this.showFullContent;
ivov marked this conversation as resolved.
Show resolved Hide resolved
}
},
},
});
</script>

<style lang="scss" module>
.notice {
font-size: var(--font-size-2xs);
display: flex;
color: var(--custom-font-black);
margin: 0;
padding: var(--spacing-xs);
margin: var(--spacing-s) 0;
padding: var(--spacing-2xs);
background-color: var(--background-color);
border-width: 1px 1px 1px 7px;
border-style: solid;
border-color: var(--border-color);
border-radius: var(--border-radius-small);
line-height: var(--font-line-height-compact);

a {
font-weight: var(--font-weight-bold);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ describe('components', () => {
slots: {
default: 'This is a notice.',
},
stubs: ['n8n-text'],
});
expect(wrapper.html()).toMatchSnapshot();
});
Expand All @@ -23,73 +24,37 @@ describe('components', () => {
id: 'notice',
content: 'This is a notice.',
},
stubs: ['n8n-text'],
});
expect(wrapper.html()).toMatchSnapshot();
});

it('should render html', () => {
it('should render HTML', () => {
const wrapper = render(N8nNotice, {
props: {
id: 'notice',
content: '<strong>Hello world!</strong> This is a notice.',
},
stubs: ['n8n-text'],
});

expect(wrapper.container.querySelectorAll('strong')).toHaveLength(1);
expect(wrapper.html()).toMatchSnapshot();
});

it('should sanitize rendered html', () => {
it('should sanitize rendered HTML', () => {
const wrapper = render(N8nNotice, {
props: {
id: 'notice',
content: '<script>alert(1);</script> This is a notice.',
},
stubs: ['n8n-text'],
});

expect(wrapper.container.querySelector('script')).not.toBeTruthy();
expect(wrapper.html()).toMatchSnapshot();
});
});
});

describe('truncation', () => {
it('should truncate content longer than 150 characters', async () => {
const wrapper = render(N8nNotice, {
props: {
id: 'notice',
truncate: true,
content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.',
},
});

const button = await wrapper.findByRole('button');
const region = await wrapper.findByRole('region');

expect(button).toBeVisible();
expect(button).toHaveTextContent('Show more');

expect(region).toBeVisible();
expect(region.textContent!.endsWith('...')).toBeTruthy();
});

it('should expand truncated text when clicking show more', async () => {
const wrapper = render(N8nNotice, {
props: {
id: 'notice',
truncate: true,
content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.',
},
});

const button = await wrapper.findByRole('button');
const region = await wrapper.findByRole('region');

await fireEvent.click(button);

expect(button).toHaveTextContent('Show less');
expect(region.textContent!.endsWith('...')).not.toBeTruthy();
});
});
});
});
Original file line number Diff line number Diff line change
@@ -1,28 +1,33 @@
// Vitest Snapshot v1

exports[`components > N8nNotice > props > content > should render correctly with content prop 1`] = `
"<div id=\\"notice\\" role=\\"alert\\" class=\\"notice _notice_4m4il_1 _warning_4m4il_16\\">
<div class=\\"notice-content\\"><span class=\\"_size-small_9dlpz_24 _regular_9dlpz_5\\"><span id=\\"notice-content\\" role=\\"region\\" class=\\"_truncated_4m4il_41\\">This is a notice.</span>
<!----></span></div>
exports[`components > N8nNotice > props > content > should render HTML 1`] = `
"<div id=\\"notice\\" role=\\"alert\\" class=\\"notice _notice_kaqw5_1 _warning_kaqw5_18\\">
<div class=\\"notice-content\\">
<n8n-text-stub size=\\"small\\" compact=\\"true\\" tag=\\"span\\"><span id=\\"notice-content\\" role=\\"region\\" class=\\"_truncated_kaqw5_43\\"><strong>Hello world!</strong> This is a notice.</span></n8n-text-stub>
</div>
</div>"
`;

exports[`components > N8nNotice > props > content > should render html 1`] = `
"<div id=\\"notice\\" role=\\"alert\\" class=\\"notice _notice_4m4il_1 _warning_4m4il_16\\">
<div class=\\"notice-content\\"><span class=\\"_size-small_9dlpz_24 _regular_9dlpz_5\\"><span id=\\"notice-content\\" role=\\"region\\" class=\\"_truncated_4m4il_41\\"><strong>Hello world!</strong> This is a notice.</span>
<!----></span></div>
exports[`components > N8nNotice > props > content > should render correctly with content prop 1`] = `
"<div id=\\"notice\\" role=\\"alert\\" class=\\"notice _notice_kaqw5_1 _warning_kaqw5_18\\">
<div class=\\"notice-content\\">
<n8n-text-stub size=\\"small\\" compact=\\"true\\" tag=\\"span\\"><span id=\\"notice-content\\" role=\\"region\\" class=\\"_truncated_kaqw5_43\\">This is a notice.</span></n8n-text-stub>
</div>
</div>"
`;

exports[`components > N8nNotice > props > content > should sanitize rendered html 1`] = `
"<div id=\\"notice\\" role=\\"alert\\" class=\\"notice _notice_4m4il_1 _warning_4m4il_16\\">
<div class=\\"notice-content\\"><span class=\\"_size-small_9dlpz_24 _regular_9dlpz_5\\"><span id=\\"notice-content\\" role=\\"region\\" class=\\"_truncated_4m4il_41\\"> This is a notice.</span>
<!----></span></div>
exports[`components > N8nNotice > props > content > should sanitize rendered HTML 1`] = `
"<div id=\\"notice\\" role=\\"alert\\" class=\\"notice _notice_kaqw5_1 _warning_kaqw5_18\\">
<div class=\\"notice-content\\">
<n8n-text-stub size=\\"small\\" compact=\\"true\\" tag=\\"span\\"><span id=\\"notice-content\\" role=\\"region\\" class=\\"_truncated_kaqw5_43\\"> This is a notice.</span></n8n-text-stub>
</div>
</div>"
`;

exports[`components > N8nNotice > should render correctly 1`] = `
"<div id=\\"notice\\" role=\\"alert\\" class=\\"notice _notice_4m4il_1 _warning_4m4il_16\\">
<div class=\\"notice-content\\"><span class=\\"_size-small_9dlpz_24 _regular_9dlpz_5\\">This is a notice.</span></div>
"<div id=\\"notice\\" role=\\"alert\\" class=\\"notice _notice_kaqw5_1 _warning_kaqw5_18\\">
<div class=\\"notice-content\\">
<n8n-text-stub size=\\"small\\" compact=\\"true\\" tag=\\"span\\">This is a notice.</n8n-text-stub>
</div>
</div>"
`;
8 changes: 8 additions & 0 deletions packages/editor-ui/src/Interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,9 @@ export interface IExternalHooks {
run(eventName: string, metadata?: IDataObject): Promise<void>;
}

/**
* @deprecated Do not add methods to this interface.
ivov marked this conversation as resolved.
Show resolved Hide resolved
*/
export interface IRestApi {
getActiveWorkflows(): Promise<string[]>;
getActivationError(id: string): Promise<IActivationError | undefined >;
Expand Down Expand Up @@ -822,9 +825,14 @@ export interface ICredentialMap {
[name: string]: ICredentialsResponse;
}

interface IScopesMap {
[credentialType: string]: string[];
}

export interface ICredentialsState {
credentialTypes: ICredentialTypeMap;
credentials: ICredentialMap;
scopes: IScopesMap;
}

export interface ITagsState {
Expand Down
4 changes: 4 additions & 0 deletions packages/editor-ui/src/api/credentials.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ export async function getAllCredentials(context: IRestApiContext): Promise<ICred
return await makeRestApiRequest(context, 'GET', '/credentials');
}

export async function getScopes(context: IRestApiContext, credentialType: string): Promise<string[]> {
return await makeRestApiRequest(context, 'GET', '/oauth2-credential/scopes', { credentialType });
}

export async function createNewCredential(context: IRestApiContext, data: ICredentialsDecrypted): Promise<ICredentialsResponse> {
return makeRestApiRequest(context, 'POST', `/credentials`, data as unknown as IDataObject);
}
Expand Down
8 changes: 6 additions & 2 deletions packages/editor-ui/src/components/Node.vue
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@
<script lang="ts">

import Vue from 'vue';
import { WAIT_TIME_UNLIMITED } from '@/constants';
import { CUSTOM_API_CALL_KEY, WAIT_TIME_UNLIMITED } from '@/constants';
import { externalHooks } from '@/components/mixins/externalHooks';
import { nodeBase } from '@/components/mixins/nodeBase';
import { nodeHelpers } from '@/components/mixins/nodeHelpers';
Expand Down Expand Up @@ -336,7 +336,11 @@ export default mixins(externalHooks, nodeBase, nodeHelpers, workflowHelpers).ext
},
methods: {
setSubtitle() {
this.nodeSubtitle = this.getNodeSubtitle(this.data, this.nodeType, this.getWorkflow()) || '';
const nodeSubtitle = this.getNodeSubtitle(this.data, this.nodeType, this.getWorkflow()) || '';

this.nodeSubtitle = nodeSubtitle.includes(CUSTOM_API_CALL_KEY)
? ''
: nodeSubtitle;
},
disableNode () {
this.disableNodes([this.data]);
Expand Down
4 changes: 2 additions & 2 deletions packages/editor-ui/src/components/NodeCredentials.vue
Original file line number Diff line number Diff line change
Expand Up @@ -101,11 +101,11 @@ export default mixins(
}),
isProxyAuth(): boolean {
return this.isHttpRequestNodeV2(this.node) &&
ivov marked this conversation as resolved.
Show resolved Hide resolved
this.node.parameters.authenticateWith === 'nodeCredential';
this.node.parameters.authentication === 'existingCredentialType';
},
isGenericAuth(): boolean {
return this.isHttpRequestNodeV2(this.node) &&
this.node.parameters.authenticateWith === 'genericAuth';
this.node.parameters.authentication === 'genericCredentialType';
},
credentialTypesNode (): string[] {
return this.credentialTypesNodeDescription
Expand Down
Loading