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

fix: Fix automatic credential selection when credentials are shared #5020

Merged
merged 9 commits into from
Dec 23, 2022
104 changes: 62 additions & 42 deletions packages/editor-ui/src/components/NodeCredentials.vue
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ export default mixins(genericHelpers, nodeHelpers, restApi, showMessage).extend(
};
},
mounted() {
this.listenForNewCredentials();
this.listenForCredentialUpdates();
},
computed: {
...mapStores(
Expand All @@ -133,9 +133,6 @@ export default mixins(genericHelpers, nodeHelpers, restApi, showMessage).extend(
useUsersStore,
useWorkflowsStore,
),
allCredentialsByType(): { [type: string]: ICredentialsResponse[] } {
return this.credentialsStore.allCredentialsByType;
},
currentUser(): IUser {
return this.usersStore.currentUser || ({} as IUser);
},
Expand Down Expand Up @@ -182,13 +179,7 @@ export default mixins(genericHelpers, nodeHelpers, restApi, showMessage).extend(

methods: {
getCredentialOptions(type: string): ICredentialsResponse[] {
return (this.allCredentialsByType as Record<string, ICredentialsResponse[]>)[type].filter(
(credential) => {
const permissions = getCredentialPermissions(this.currentUser, credential);

return permissions.use;
},
);
return this.credentialsStore.allUsableCredentialsByType[type];
},
getSelectedId(type: string) {
if (this.isCredentialExisting(type)) {
Expand Down Expand Up @@ -221,41 +212,71 @@ export default mixins(genericHelpers, nodeHelpers, restApi, showMessage).extend(

return styles;
},
// TODO: Investigate if this can be solved using only the store data (storing selected flag in credentials objects, ...)
listenForNewCredentials() {
// Listen for credentials store changes so credential selection can be updated if creds are changed from the modal
this.credentialsStore.$subscribe((mutation, state) => {
// Listen for credentials store changes so credential selection can be updated if creds are changed from the modal
listenForCredentialUpdates() {
const getCounts = () => {
return Object.keys(this.credentialsStore.allUsableCredentialsByType).reduce(
(counts: { [key: string]: number }, key: string) => {
counts[key] = this.credentialsStore.allUsableCredentialsByType[key].length;
return counts;
},
{},
);
};

let previousCredentialCounts = getCounts();
const onCredentialMutation = () => {
// This data pro stores credential type that the component is currently interested in
const credentialType = this.subscribedToCredentialType;
let credentialsOfType = this.credentialsStore.allCredentialsByType[credentialType];

if (credentialsOfType) {
credentialsOfType = credentialsOfType.sort((a, b) => (a.id < b.id ? -1 : 1));
if (credentialsOfType.length > 0) {
// If nothing has been selected previously, select the first one (newly added)
if (!this.selected[credentialType]) {
this.onCredentialSelected(credentialType, credentialsOfType[0].id);
} else {
// Else, check id currently selected cred has been updated
const newSelected = credentialsOfType.find(
(cred) => cred.id === this.selected[credentialType].id,
);
// If it has changed, select it
if (newSelected && newSelected.name !== this.selected[credentialType].name) {
this.onCredentialSelected(credentialType, newSelected.id);
} else {
// Else select the last cred with that type since selected has been deleted or a new one has been added
this.onCredentialSelected(
credentialType,
credentialsOfType[credentialsOfType.length - 1].id,
);
}
}
}
if (!credentialType) {
return;
}
this.subscribedToCredentialType = '';

let credentialsOfType = [
...(this.credentialsStore.allUsableCredentialsByType[credentialType] || []),
];
// all credentials were deleted
if (credentialsOfType.length === 0) {
this.clearSelectedCredential(credentialType);
return;
}

credentialsOfType = credentialsOfType.sort((a, b) => (a.id < b.id ? -1 : 1));
const previousCredsOfType = previousCredentialCounts[credentialType] || 0;
const current = this.selected[credentialType];

// new credential was added
if (credentialsOfType.length > previousCredsOfType || !current) {
this.onCredentialSelected(
credentialType,
credentialsOfType[credentialsOfType.length - 1].id,
);
return;
}

const matchingCredential = credentialsOfType.find((cred) => cred.id === current.id);
// credential was deleted, select last one added to replace with
if (!matchingCredential) {
this.onCredentialSelected(
credentialType,
credentialsOfType[credentialsOfType.length - 1].id,
);
return;
}

// credential was updated
if (matchingCredential.name !== current.name) {
// credential name was changed, update it
this.onCredentialSelected(credentialType, current.id);
}
};

this.credentialsStore.$subscribe((mutation, state) => {
onCredentialMutation();
previousCredentialCounts = getCounts();
});
},

clearSelectedCredential(credentialType: string) {
const node: INodeUi = this.node;

Expand All @@ -277,7 +298,6 @@ export default mixins(genericHelpers, nodeHelpers, restApi, showMessage).extend(

onCredentialSelected(credentialType: string, credentialId: string | null | undefined) {
if (credentialId === this.NEW_CREDENTIALS_TEXT) {
// this.listenForNewCredentials(credentialType);
this.subscribedToCredentialType = credentialType;
}
if (!credentialId || credentialId === this.NEW_CREDENTIALS_TEXT) {
Expand Down
21 changes: 21 additions & 0 deletions packages/editor-ui/src/stores/credentials.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,22 @@ export const useCredentialsStore = defineStore(STORES.CREDENTIALS, {
{},
);
},
allUsableCredentialsByType(): { [type: string]: ICredentialsResponse[] } {
const credentials = this.allCredentials;
const types = this.allCredentialTypes;
const usersStore = useUsersStore();

return types.reduce(
(accu: { [type: string]: ICredentialsResponse[] }, type: ICredentialType) => {
accu[type.name] = credentials.filter((cred: ICredentialsResponse) => {
return cred.type === type.name && usersStore.isResourceAccessible(cred);
});

return accu;
},
{},
);
},
getCredentialTypeByName() {
return (type: string): ICredentialType => this.credentialTypes[type];
},
Expand All @@ -89,6 +105,11 @@ export const useCredentialsStore = defineStore(STORES.CREDENTIALS, {
return this.allCredentialsByType[credentialType] || [];
};
},
getUsableCredentialByType() {
return (credentialType: string): ICredentialsResponse[] => {
return this.allUsableCredentialsByType[credentialType] || [];
};
},
getNodesWithAccess() {
return (credentialTypeName: string) => {
const nodeTypesStore = useNodeTypesStore();
Expand Down
11 changes: 10 additions & 1 deletion packages/editor-ui/src/stores/users.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,16 @@ import {
validatePasswordToken,
validateSignupToken,
} from '@/api/users';
import { PERSONALIZATION_MODAL_KEY, STORES } from '@/constants';
import { EnterpriseEditionFeature, PERSONALIZATION_MODAL_KEY, STORES } from '@/constants';
import {
ICredentialsResponse,
IInviteResponse,
IPersonalizationLatestVersion,
IUser,
IUserResponse,
IUsersState,
} from '@/Interface';
import { getCredentialPermissions } from '@/permissions';
import { getPersonalizedNodeTypes, isAuthorized, PERMISSIONS, ROLE } from '@/utils';
import { defineStore } from 'pinia';
import Vue from 'vue';
Expand Down Expand Up @@ -90,6 +92,13 @@ export const useUsersStore = defineStore(STORES.USERS, {
}
return getPersonalizedNodeTypes(answers);
},
isResourceAccessible() {
return (resource: ICredentialsResponse): boolean => {
const permissions = getCredentialPermissions(this.currentUser, resource);

return permissions.use;
};
},
},
actions: {
addUsers(users: IUserResponse[]) {
Expand Down
2 changes: 1 addition & 1 deletion packages/editor-ui/src/views/NodeView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -1714,7 +1714,7 @@ export default mixins(
const credentialPerType =
nodeTypeData.credentials &&
nodeTypeData.credentials
.map((type) => this.credentialsStore.getCredentialsByType(type.name))
.map((type) => this.credentialsStore.getUsableCredentialByType(type.name))
.flat();

if (credentialPerType && credentialPerType.length === 1) {
Expand Down