feat(core): Allow credential reuse on HTTP Request node (#3228)

*  Create controller

*  Mount controller

* ✏️ Add error messages

*  Create scopes fetcher

*  Account for non-existent credential type

* 📘 Type scopes request

*  Adjust error message

* 🧪 Add tests

*  Introduce simple node versioning

*  Add example how to read version in node-code for custom logic

* 🐛 Fix setting of parameters

* 🐛 Fix another instance where it sets the wrong parameter

*  Remove unnecessary TOODs

*  Re-version HTTP Request node

* 👕 Satisfy linter

*  Retrieve node version

*  Undo Jan's changes to Set node

* 🧪 Fix CI/CD for `/oauth2-credential` tests (#3230)

* 🐛 Fix notice warning missing background color (#3231)

* 🐛 Check for generic auth in node cred types

*  Refactor credentials dropdown for HTTP Request node (#3222)

*  Discoverability flow (#3229)

*  Added node credentials type proxy. Changed node credentials input order.

*  Add computed property from versioning branch

* 🐛 Fix cred ref lost and unsaved

*  Make options consistent with cred type names

*  Use prop to set component order

*  Use constant and version

*  Fix rendering for generic auth creds

*  Mark as required on first selection

*  Implement discoverability flow

*  Mark as required on subsequent selections

*  Fix marking as required after cred deletion

*  Refactor to clean up

*  Detect position automatically

*  Add i18n to option label

*  Hide subtitle for custom action

*  Detect active credential type

*  Prop drilling to re-render select

* 🔥 Remove unneeded property

* ✏️ Rename arg

* 🔥 Remove unused import

* 🔥 Remove unneeded getters

* 🔥 Remove unused import

*  Generalize cred component positioning

*  Set up request

* 🐛 Fix edge case in endpoint

*  Display scopes alert box

*  Revert "Generalize cred comp positioning"

This reverts commit 75eea89273b854110fa6d1f96c7c1d78dd3b0731.

*  Consolidate HTTPRN check

*  Fix hue percentage to degree

* 🔥 Remove unused import

* 🔥 Remove unused import

* 🔥 Remove unused class

* 🔥 Remove unused import

* 📘 Create type for HTTPRN v2 auth params

* ✏️ Rename check

* 🔥 Remove unused import

* ✏️ Add i18n to `reportUnsetCredential()`

*  Refactor Alex's spacing changes

*  Post-merge fixes

*  Add docs link

* 🔥 Exclude Notion OAuth cred

* ✏️ Update copy

* ✏️ Rename param

* 🎨 Reposition notice and simplify styling

* ✏️ Update copy

* ✏️ Update copy

*  Hide params during custom action

*  Show notice if any cred type supported

* 🐛 Prevent scopes text overflow

* 🔥 Remove superfluous check

* ✏️ Break up docstring

* 🎨 Tweak notice styling

*  Reorder cred param in Webhook node

* ✏️ Shorten cred name in scopes notice

* 🧪 Update Notice snapshots

* 🐛 Fix check when `globalRole` is `undefined`

*  Revert 3f2c4a6

*  Apply feedback from Product

* 🧪 Update snapshot

*  Adjust regex expansion pattern for singular

* 🔥 Remove unused import

* 🔥 Remove logging

*  Make `somethingElse` key more unique

*  Move something else to constants

*  Consolidate notice component

*  Apply latest feedback

* 🧪 Update tests

* 🧪 Update snapshot

* ✏️ Fix singular version

* 🧪 Finalize tests

* ✏️ Rename constant

* 🧪 Expand tests

* 🔥 Remove `truncate` prop

* 🚚 Move scopes fetching to store

* 🚚 Move method to component

*  Use constant

*  Refactor `Notice` component

* 🧪 Update tests

* 🔥 Remove unused keys

*  Inject custom API call option

* 🔥 Remove unused props

* 🎨 Use `compact` prop

* 🧪 Update snapshots

* 🚚 Move scopes to store

* 🚚 Move `nodeCredentialTypes` to parent

* ✏️ Rename cred types per branding

* 🐛 Clear scopes when none

*  Add default

* 🚚 Move `newHttpRequestNodeCredentialType` to parent

* 🔥 Remove test data

*  Separate lines for readability

*  Change reference from node to node name

* ✏️ Rename i18n keys

*  Refactor OAuth check

* 🔥 Remove unused key

* 🚚 Move `OAuth1/2 API` to i18n

*  Refactor `skipCheck`

*  Add `stopPropagation` and `preventDefault`

* 🚚 Move active credential scopes logic to store

* 🎨 Fix spacing for `NodeWebhooks` component

*  Implement feedback

*  Update HTTPRN default and issue copy

* Refactor to use `CredentialsSelect` param (#3304)

*  Refactor into cred type param

*  Componentize scopes notice

* 🔥 Remove unused data

* 🔥 Remove unused `loadOptions`

*  Componentize `NodeCredentialType`

* 🐛 Fix param validation

* 🔥 Remove dup methods

*  Refactor all references to `isHttpRequestNodeV2`

* 🎨 Fix styling

* 🔥 Remove unused import

* 🔥 Remove unused properties

* 🎨 Fix spacing for Pipedrive Trigger node

* 🎨 Undo Webhook node styling change

* 🔥 Remove unused style

*  Cover `httpHeaderAuth` edge case

* 🐛 Fix `this.node` reference

* 🚚 Rename to `credentialsSelect`

* 🐛 Fix mistaken renaming

*  Set one attribute per line

*  Move condition to instantiation site

* 🚚 Rename prop

*  Refactor away `prepareScopesNotice`

* ✏️ Rename i18n keys

* ✏️ Update i18n calls

* ✏️ Add more i18n keys

* 🔥 Remove unused props

* ✏️ Add explanatory comment

*  Adjust check in `hasProxyAuth`

*  Refactor `credentialSelected` from prop to event

*  Eventify `valueChanged`, `setFocus`, `onBlur`

*  Eventify `optionSelected`

*  Add `noDataExpression`

* 🔥 Remove logging

* 🔥 Remove URL from scopes

*  Disregard expressions for display

* 🎨 Use CSS modules

* 📘 Tigthen interface

* 🐛 Fix generic auth display

* 🐛 Fix generic auth validation

* 📘 Loosen type

* 🚚 Move event params to end

*  Generalize reference

*  Refactor generic auth as `credentialsSelect` param

*  Restore check for `httpHeaderAuth `

* 🚚 Rename `existing` to `predefined`

* Extend metrics for HTTP Request node (#3282)

*  Extend metrics

* 🧪 Add tests

*  Update param names

Co-authored-by: Alex Grozav <alex@grozav.com>

*  Update check per new branch

*  Include generic auth check

*  Adjust telemetry (#3359)

*  Filter credential types by label

Co-authored-by: Jan Oberhauser <jan.oberhauser@gmail.com>
Co-authored-by: Alex Grozav <alex@grozav.com>
This commit is contained in:
Iván Ovejero
2022-05-24 11:36:19 +02:00
committed by GitHub
parent 0212d65dae
commit 336fc9e2a8
43 changed files with 1396 additions and 228 deletions

View File

@@ -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>
@@ -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',
@@ -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: '',
},
@@ -67,7 +51,7 @@ export default Vue.extend({
},
data() {
return {
expanded: false,
showFullContent: false,
};
},
computed: {
@@ -79,22 +63,32 @@ 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') {
e.stopPropagation();
e.preventDefault();
this.showFullContent = false;
} else if (this.canTruncate && e.target.dataset.key === 'toggle-expand') {
e.stopPropagation();
e.preventDefault();
this.showFullContent = !this.showFullContent;
}
},
},
});
@@ -102,15 +96,17 @@ export default Vue.extend({
<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);

View File

@@ -11,6 +11,7 @@ describe('components', () => {
slots: {
default: 'This is a notice.',
},
stubs: ['n8n-text'],
});
expect(wrapper.html()).toMatchSnapshot();
});
@@ -23,28 +24,31 @@ 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();
@@ -52,44 +56,5 @@ describe('components', () => {
});
});
});
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();
});
});
});
});

View File

@@ -1,28 +1,33 @@
// Vitest Snapshot v1
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 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>
"<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 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>
</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>"
`;