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

* fix: fix default credentials when inserting nodes

* fix: update default without sharing

* fix: fix clearing credential bug, automatically selecting shared cred bug

* fix: include sharable creds in automatic selections

* fix: update getter

* fix: refactor subscribe logic, fix update bug

* fix: remove unnessary import

* format: prettier
This commit is contained in:
Mutasem Aldmour
2022-12-23 16:32:06 +01:00
committed by GitHub
parent 4651147096
commit 6a8448da5f
4 changed files with 94 additions and 44 deletions

View File

@@ -123,7 +123,7 @@ export default mixins(genericHelpers, nodeHelpers, restApi, showMessage).extend(
}; };
}, },
mounted() { mounted() {
this.listenForNewCredentials(); this.listenForCredentialUpdates();
}, },
computed: { computed: {
...mapStores( ...mapStores(
@@ -133,9 +133,6 @@ export default mixins(genericHelpers, nodeHelpers, restApi, showMessage).extend(
useUsersStore, useUsersStore,
useWorkflowsStore, useWorkflowsStore,
), ),
allCredentialsByType(): { [type: string]: ICredentialsResponse[] } {
return this.credentialsStore.allCredentialsByType;
},
currentUser(): IUser { currentUser(): IUser {
return this.usersStore.currentUser || ({} as IUser); return this.usersStore.currentUser || ({} as IUser);
}, },
@@ -182,13 +179,7 @@ export default mixins(genericHelpers, nodeHelpers, restApi, showMessage).extend(
methods: { methods: {
getCredentialOptions(type: string): ICredentialsResponse[] { getCredentialOptions(type: string): ICredentialsResponse[] {
return (this.allCredentialsByType as Record<string, ICredentialsResponse[]>)[type].filter( return this.credentialsStore.allUsableCredentialsByType[type];
(credential) => {
const permissions = getCredentialPermissions(this.currentUser, credential);
return permissions.use;
},
);
}, },
getSelectedId(type: string) { getSelectedId(type: string) {
if (this.isCredentialExisting(type)) { if (this.isCredentialExisting(type)) {
@@ -221,41 +212,71 @@ export default mixins(genericHelpers, nodeHelpers, restApi, showMessage).extend(
return styles; return styles;
}, },
// TODO: Investigate if this can be solved using only the store data (storing selected flag in credentials objects, ...) // Listen for credentials store changes so credential selection can be updated if creds are changed from the modal
listenForNewCredentials() { listenForCredentialUpdates() {
// Listen for credentials store changes so credential selection can be updated if creds are changed from the modal const getCounts = () => {
this.credentialsStore.$subscribe((mutation, state) => { 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 // This data pro stores credential type that the component is currently interested in
const credentialType = this.subscribedToCredentialType; const credentialType = this.subscribedToCredentialType;
let credentialsOfType = this.credentialsStore.allCredentialsByType[credentialType]; if (!credentialType) {
return;
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,
);
}
}
}
} }
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) { clearSelectedCredential(credentialType: string) {
const node: INodeUi = this.node; const node: INodeUi = this.node;
@@ -277,7 +298,6 @@ export default mixins(genericHelpers, nodeHelpers, restApi, showMessage).extend(
onCredentialSelected(credentialType: string, credentialId: string | null | undefined) { onCredentialSelected(credentialType: string, credentialId: string | null | undefined) {
if (credentialId === this.NEW_CREDENTIALS_TEXT) { if (credentialId === this.NEW_CREDENTIALS_TEXT) {
// this.listenForNewCredentials(credentialType);
this.subscribedToCredentialType = credentialType; this.subscribedToCredentialType = credentialType;
} }
if (!credentialId || credentialId === this.NEW_CREDENTIALS_TEXT) { if (!credentialId || credentialId === this.NEW_CREDENTIALS_TEXT) {

View File

@@ -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() { getCredentialTypeByName() {
return (type: string): ICredentialType => this.credentialTypes[type]; return (type: string): ICredentialType => this.credentialTypes[type];
}, },
@@ -89,6 +105,11 @@ export const useCredentialsStore = defineStore(STORES.CREDENTIALS, {
return this.allCredentialsByType[credentialType] || []; return this.allCredentialsByType[credentialType] || [];
}; };
}, },
getUsableCredentialByType() {
return (credentialType: string): ICredentialsResponse[] => {
return this.allUsableCredentialsByType[credentialType] || [];
};
},
getNodesWithAccess() { getNodesWithAccess() {
return (credentialTypeName: string) => { return (credentialTypeName: string) => {
const nodeTypesStore = useNodeTypesStore(); const nodeTypesStore = useNodeTypesStore();

View File

@@ -18,14 +18,16 @@ import {
validatePasswordToken, validatePasswordToken,
validateSignupToken, validateSignupToken,
} from '@/api/users'; } from '@/api/users';
import { PERSONALIZATION_MODAL_KEY, STORES } from '@/constants'; import { EnterpriseEditionFeature, PERSONALIZATION_MODAL_KEY, STORES } from '@/constants';
import { import {
ICredentialsResponse,
IInviteResponse, IInviteResponse,
IPersonalizationLatestVersion, IPersonalizationLatestVersion,
IUser, IUser,
IUserResponse, IUserResponse,
IUsersState, IUsersState,
} from '@/Interface'; } from '@/Interface';
import { getCredentialPermissions } from '@/permissions';
import { getPersonalizedNodeTypes, isAuthorized, PERMISSIONS, ROLE } from '@/utils'; import { getPersonalizedNodeTypes, isAuthorized, PERMISSIONS, ROLE } from '@/utils';
import { defineStore } from 'pinia'; import { defineStore } from 'pinia';
import Vue from 'vue'; import Vue from 'vue';
@@ -90,6 +92,13 @@ export const useUsersStore = defineStore(STORES.USERS, {
} }
return getPersonalizedNodeTypes(answers); return getPersonalizedNodeTypes(answers);
}, },
isResourceAccessible() {
return (resource: ICredentialsResponse): boolean => {
const permissions = getCredentialPermissions(this.currentUser, resource);
return permissions.use;
};
},
}, },
actions: { actions: {
addUsers(users: IUserResponse[]) { addUsers(users: IUserResponse[]) {

View File

@@ -1714,7 +1714,7 @@ export default mixins(
const credentialPerType = const credentialPerType =
nodeTypeData.credentials && nodeTypeData.credentials &&
nodeTypeData.credentials nodeTypeData.credentials
.map((type) => this.credentialsStore.getCredentialsByType(type.name)) .map((type) => this.credentialsStore.getUsableCredentialByType(type.name))
.flat(); .flat();
if (credentialPerType && credentialPerType.length === 1) { if (credentialPerType && credentialPerType.length === 1) {