Skip to content

Commit db1a406

Browse files
authored
fix(editor): Fix External secrets typecheck (no-changelog) (#9434)
1 parent 28e3e21 commit db1a406

File tree

9 files changed

+100
-63
lines changed

9 files changed

+100
-63
lines changed

packages/editor-ui/src/Interface.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1803,18 +1803,19 @@ export interface ExternalSecretsProviderSecret {
18031803

18041804
export type ExternalSecretsProviderData = Record<string, IUpdateInformation['value']>;
18051805

1806+
export type ExternalSecretsProviderProperty = INodeProperties;
1807+
1808+
export type ExternalSecretsProviderState = 'connected' | 'tested' | 'initializing' | 'error';
1809+
18061810
export interface ExternalSecretsProvider {
18071811
icon: string;
18081812
name: string;
18091813
displayName: string;
18101814
connected: boolean;
18111815
connectedAt: string | false;
1812-
state: 'connected' | 'tested' | 'initializing' | 'error';
1816+
state: ExternalSecretsProviderState;
18131817
data?: ExternalSecretsProviderData;
1814-
}
1815-
1816-
export interface ExternalSecretsProviderWithProperties extends ExternalSecretsProvider {
1817-
properties: INodeProperties[];
1818+
properties?: ExternalSecretsProviderProperty[];
18181819
}
18191820

18201821
export type CloudUpdateLinkSourceType =
@@ -1835,6 +1836,7 @@ export type CloudUpdateLinkSourceType =
18351836
| 'community-nodes'
18361837
| 'workflow-history'
18371838
| 'worker-view'
1839+
| 'external-secrets'
18381840
| 'rbac';
18391841

18401842
export type UTMCampaign =
@@ -1855,6 +1857,7 @@ export type UTMCampaign =
18551857
| 'upgrade-workflow-history'
18561858
| 'upgrade-advanced-permissions'
18571859
| 'upgrade-worker-view'
1860+
| 'upgrade-external-secrets'
18581861
| 'upgrade-rbac';
18591862

18601863
export type N8nBanners = {

packages/editor-ui/src/api/externalSecrets.ee.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,4 @@
1-
import type {
2-
IRestApiContext,
3-
ExternalSecretsProvider,
4-
ExternalSecretsProviderWithProperties,
5-
} from '@/Interface';
1+
import type { IRestApiContext, ExternalSecretsProvider } from '@/Interface';
62
import { makeRestApiRequest } from '@/utils/apiUtils';
73

84
export const getExternalSecrets = async (
@@ -20,7 +16,7 @@ export const getExternalSecretsProviders = async (
2016
export const getExternalSecretsProvider = async (
2117
context: IRestApiContext,
2218
id: string,
23-
): Promise<ExternalSecretsProviderWithProperties> => {
19+
): Promise<ExternalSecretsProvider> => {
2420
return await makeRestApiRequest(context, 'GET', `/external-secrets/providers/${id}`);
2521
};
2622

packages/editor-ui/src/components/ExternalSecretsProviderCard.ee.vue

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<script lang="ts" setup>
2-
import type { PropType, Ref } from 'vue';
2+
import type { PropType } from 'vue';
33
import type { ExternalSecretsProvider } from '@/Interface';
44
import ExternalSecretsProviderImage from '@/components/ExternalSecretsProviderImage.ee.vue';
55
import ExternalSecretsProviderConnectionSwitch from '@/components/ExternalSecretsProviderConnectionSwitch.ee.vue';
@@ -10,7 +10,8 @@ import { useI18n } from '@/composables/useI18n';
1010
import { useExternalSecretsProvider } from '@/composables/useExternalSecretsProvider';
1111
import { EXTERNAL_SECRETS_PROVIDER_MODAL_KEY } from '@/constants';
1212
import { DateTime } from 'luxon';
13-
import { computed, nextTick, onMounted, toRefs } from 'vue';
13+
import { computed, nextTick, onMounted, toRef } from 'vue';
14+
import { isDateObject } from '@/utils/typeGuards';
1415
1516
const props = defineProps({
1617
provider: {
@@ -24,15 +25,12 @@ const i18n = useI18n();
2425
const uiStore = useUIStore();
2526
const toast = useToast();
2627
27-
const { provider } = toRefs(props) as Ref<ExternalSecretsProvider>;
28-
const providerData = computed(() => provider.value.data);
29-
const {
30-
connectionState,
31-
initialConnectionState,
32-
normalizedProviderData,
33-
testConnection,
34-
setConnectionState,
35-
} = useExternalSecretsProvider(provider, providerData);
28+
const provider = toRef(props, 'provider');
29+
const providerData = computed(() => provider.value.data ?? {});
30+
const { connectionState, testConnection, setConnectionState } = useExternalSecretsProvider(
31+
provider,
32+
providerData,
33+
);
3634
3735
const actionDropdownOptions = computed(() => [
3836
{
@@ -50,11 +48,15 @@ const actionDropdownOptions = computed(() => [
5048
]);
5149
5250
const canConnect = computed(() => {
53-
return props.provider.connected || Object.keys(props.provider.data).length > 0;
51+
return props.provider.connected || Object.keys(providerData.value).length > 0;
5452
});
5553
56-
const formattedDate = computed((provider: ExternalSecretsProvider) => {
57-
return DateTime.fromISO(props.provider.connectedAt ?? new Date()).toFormat('dd LLL yyyy');
54+
const formattedDate = computed(() => {
55+
return DateTime.fromISO(
56+
isDateObject(provider.value.connectedAt)
57+
? provider.value.connectedAt.toISOString()
58+
: provider.value.connectedAt || new Date().toISOString(),
59+
).toFormat('dd LLL yyyy');
5860
});
5961
6062
onMounted(() => {

packages/editor-ui/src/components/ExternalSecretsProviderImage.ee.vue

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import type { PropType } from 'vue';
33
import type { ExternalSecretsProvider } from '@/Interface';
44
import { computed } from 'vue';
5+
56
import infisical from '../assets/images/infisical.webp';
67
import doppler from '../assets/images/doppler.webp';
78
import vault from '../assets/images/hashicorp.webp';

packages/editor-ui/src/components/ExternalSecretsProviderModal.ee.vue

Lines changed: 26 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,14 @@
22
import Modal from './Modal.vue';
33
import { EXTERNAL_SECRETS_PROVIDER_MODAL_KEY, MODAL_CONFIRM } from '@/constants';
44
import { computed, onMounted, ref } from 'vue';
5-
import type { PropType, Ref } from 'vue';
5+
import type { PropType } from 'vue';
66
import type { EventBus } from 'n8n-design-system/utils';
77
import { useExternalSecretsProvider } from '@/composables/useExternalSecretsProvider';
88
import { useI18n } from '@/composables/useI18n';
99
import { useMessage } from '@/composables/useMessage';
1010
import { useToast } from '@/composables/useToast';
1111
import { useExternalSecretsStore } from '@/stores/externalSecrets.ee.store';
1212
import { useUIStore } from '@/stores/ui.store';
13-
import { useRoute } from 'vue-router';
1413
import ParameterInputExpanded from '@/components/ParameterInputExpanded.vue';
1514
import type {
1615
IUpdateInformation,
@@ -29,7 +28,7 @@ const props = defineProps({
2928
},
3029
});
3130
32-
const defaultProviderData = {
31+
const defaultProviderData: Record<string, Partial<ExternalSecretsProviderData>> = {
3332
infisical: {
3433
siteURL: 'https://app.infisical.com',
3534
},
@@ -39,7 +38,6 @@ const externalSecretsStore = useExternalSecretsStore();
3938
const uiStore = useUIStore();
4039
const toast = useToast();
4140
const i18n = useI18n();
42-
const route = useRoute();
4341
const { confirm } = useMessage();
4442
4543
const saving = ref(false);
@@ -50,7 +48,7 @@ const labelSize: IParameterLabel = { size: 'medium' };
5048
5149
const provider = computed<ExternalSecretsProvider | undefined>(() =>
5250
externalSecretsStore.providers.find((p) => p.name === props.data.name),
53-
) as Ref<ExternalSecretsProvider>;
51+
);
5452
const providerData = ref<ExternalSecretsProviderData>({});
5553
const {
5654
connectionState,
@@ -64,15 +62,15 @@ const {
6462
const providerDataUpdated = computed(() => {
6563
return Object.keys(providerData.value).find((key) => {
6664
const value = providerData.value[key];
67-
const originalValue = provider.value.data[key];
65+
const originalValue = provider.value?.data?.[key];
6866
6967
return value !== originalValue;
7068
});
7169
});
7270
7371
const canSave = computed(
7472
() =>
75-
provider.value.properties
73+
provider.value?.properties
7674
?.filter((property) => property.required && shouldDisplayProperty(property))
7775
.every((property) => {
7876
const value = providerData.value[property.name];
@@ -82,21 +80,22 @@ const canSave = computed(
8280
8381
onMounted(async () => {
8482
try {
85-
const provider = await externalSecretsStore.getProvider(props.data.name);
83+
const fetchedProvider = await externalSecretsStore.getProvider(props.data.name);
84+
8685
providerData.value = {
8786
...(defaultProviderData[props.data.name] || {}),
88-
...provider.data,
87+
...fetchedProvider.data,
8988
};
9089
91-
setConnectionState(provider.state);
90+
setConnectionState(fetchedProvider.state);
9291
93-
if (provider.connected) {
94-
initialConnectionState.value = provider.state;
95-
} else if (Object.keys(provider.data).length) {
92+
if (fetchedProvider.connected) {
93+
initialConnectionState.value = fetchedProvider.state;
94+
} else if (Object.keys(fetchedProvider.data ?? {}).length) {
9695
await testConnection();
9796
}
9897
99-
if (provider.state === 'connected') {
98+
if (fetchedProvider.state === 'connected') {
10099
void externalSecretsStore.reloadProvider(props.data.name);
101100
}
102101
} catch (error) {
@@ -116,6 +115,10 @@ function onValueChange(updateInformation: IUpdateInformation) {
116115
}
117116
118117
async function save() {
118+
if (!provider.value) {
119+
return;
120+
}
121+
119122
try {
120123
saving.value = true;
121124
await externalSecretsStore.updateProvider(provider.value.name, {
@@ -143,7 +146,7 @@ async function onBeforeClose() {
143146
const confirmModal = await confirm(
144147
i18n.baseText('settings.externalSecrets.provider.closeWithoutSaving.description', {
145148
interpolate: {
146-
provider: provider.value.displayName,
149+
provider: provider.value?.displayName ?? '',
147150
},
148151
}),
149152
{
@@ -162,19 +165,23 @@ async function onBeforeClose() {
162165
163166
return true;
164167
}
168+
169+
async function onConnectionStateChange() {
170+
await testConnection();
171+
}
165172
</script>
166173

167174
<template>
168175
<Modal
169176
id="external-secrets-provider-modal"
170177
width="812px"
171-
:title="provider.displayName"
178+
:title="provider?.displayName"
172179
:event-bus="data.eventBus"
173180
:name="EXTERNAL_SECRETS_PROVIDER_MODAL_KEY"
174181
:before-close="onBeforeClose"
175182
>
176183
<template #header>
177-
<div :class="$style.header">
184+
<div v-if="provider" :class="$style.header">
178185
<div :class="$style.providerTitle">
179186
<ExternalSecretsProviderImage :provider="provider" class="mr-xs" />
180187
<span>{{ provider.displayName }}</span>
@@ -188,7 +195,7 @@ async function onBeforeClose() {
188195
"
189196
:event-bus="eventBus"
190197
:provider="provider"
191-
@change="testConnection"
198+
@change="onConnectionStateChange"
192199
/>
193200
<n8n-button
194201
type="primary"
@@ -207,7 +214,7 @@ async function onBeforeClose() {
207214
</template>
208215

209216
<template #content>
210-
<div :class="$style.container">
217+
<div v-if="provider" :class="$style.container">
211218
<hr class="mb-l" />
212219
<div v-if="connectionState !== 'initializing'" class="mb-l">
213220
<n8n-callout

packages/editor-ui/src/composables/useExternalSecretsProvider.ts

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,41 @@
11
import type {
2-
ExternalSecretsProviderWithProperties,
32
ExternalSecretsProvider,
43
IUpdateInformation,
54
ExternalSecretsProviderData,
5+
ExternalSecretsProviderProperty,
6+
ExternalSecretsProviderState,
67
} from '@/Interface';
7-
import type { Ref } from 'vue';
8+
import type { ComputedRef, Ref } from 'vue';
89
import { computed, ref } from 'vue';
910
import { useExternalSecretsStore } from '@/stores/externalSecrets.ee.store';
1011
import { useToast } from '@/composables/useToast';
1112

1213
export function useExternalSecretsProvider(
13-
provider: Ref<ExternalSecretsProvider>,
14+
provider:
15+
| Ref<ExternalSecretsProvider | undefined>
16+
| ComputedRef<ExternalSecretsProvider | undefined>,
1417
providerData: Ref<ExternalSecretsProviderData>,
1518
) {
1619
const toast = useToast();
1720
const externalSecretsStore = useExternalSecretsStore();
1821

19-
const initialConnectionState = ref<ExternalSecretsProviderWithProperties['state'] | undefined>(
20-
'initializing',
21-
);
22+
const initialConnectionState = ref<ExternalSecretsProvider['state'] | undefined>('initializing');
2223
const connectionState = computed(
23-
() => externalSecretsStore.connectionState[provider.value?.name],
24+
() => externalSecretsStore.connectionState[provider.value?.name ?? ''],
2425
);
25-
const setConnectionState = (state: ExternalSecretsProviderWithProperties['state']) => {
26-
externalSecretsStore.setConnectionState(provider.value?.name, state);
26+
const setConnectionState = (state: ExternalSecretsProvider['state']) => {
27+
if (!provider.value) {
28+
return;
29+
}
30+
31+
externalSecretsStore.setConnectionState(provider.value.name, state);
2732
};
2833

2934
const normalizedProviderData = computed(() => {
3035
return Object.entries(providerData.value).reduce(
3136
(acc, [key, value]) => {
32-
const property = provider.value?.properties?.find((property) => property.name === key);
33-
if (shouldDisplayProperty(property)) {
37+
const property = provider.value?.properties?.find((p) => p.name === key);
38+
if (property && shouldDisplayProperty(property)) {
3439
acc[key] = value;
3540
}
3641

@@ -40,31 +45,35 @@ export function useExternalSecretsProvider(
4045
);
4146
});
4247

43-
function shouldDisplayProperty(
44-
property: ExternalSecretsProviderWithProperties['properties'][0],
45-
): boolean {
48+
function shouldDisplayProperty(property: ExternalSecretsProviderProperty): boolean {
4649
let visible = true;
4750

4851
if (property.displayOptions?.show) {
4952
visible =
5053
visible &&
5154
Object.entries(property.displayOptions.show).every(([key, value]) => {
52-
return value?.includes(providerData.value[key]);
55+
return value?.includes(providerData.value[key] as string);
5356
});
5457
}
5558

5659
if (property.displayOptions?.hide) {
5760
visible =
5861
visible &&
5962
!Object.entries(property.displayOptions.hide).every(([key, value]) => {
60-
return value?.includes(providerData.value[key]);
63+
return value?.includes(providerData.value[key] as string);
6164
});
6265
}
6366

6467
return visible;
6568
}
6669

67-
async function testConnection(options: { showError?: boolean } = { showError: true }) {
70+
async function testConnection(
71+
options: { showError?: boolean } = { showError: true },
72+
): Promise<ExternalSecretsProviderState> {
73+
if (!provider.value) {
74+
return 'initializing';
75+
}
76+
6877
try {
6978
const { testState } = await externalSecretsStore.testProviderConnection(
7079
provider.value.name,

packages/editor-ui/src/shims.d.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,10 @@ declare global {
3535
findLast(predicate: (value: T, index: number, obj: T[]) => unknown, thisArg?: any): T;
3636
}
3737
}
38+
39+
declare module '*.svg';
40+
declare module '*.png';
41+
declare module '*.jpg';
42+
declare module '*.jpeg';
43+
declare module '*.gif';
44+
declare module '*.webp';

packages/editor-ui/src/utils/typeGuards.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,9 @@ export const isResourceMapperValue = (value: unknown): value is string | number
4343
export const isJSPlumbEndpointElement = (element: Node): element is HTMLElement => {
4444
return 'jtk' in element && 'endpoint' in (element.jtk as object);
4545
};
46+
47+
export function isDateObject(date: unknown): date is Date {
48+
return (
49+
!!date && Object.prototype.toString.call(date) === '[object Date]' && !isNaN(date as number)
50+
);
51+
}

0 commit comments

Comments
 (0)