Skip to content

Commit

Permalink
feat(editor): Display schema preview for unexecuted nodes (#12901)
Browse files Browse the repository at this point in the history
  • Loading branch information
elsmr authored and riascho committed Feb 5, 2025
1 parent 32960c0 commit cbb605f
Show file tree
Hide file tree
Showing 17 changed files with 1,558 additions and 345 deletions.
1 change: 1 addition & 0 deletions packages/editor-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@
"@types/dateformat": "^3.0.0",
"@types/file-saver": "^2.0.1",
"@types/humanize-duration": "^3.27.1",
"@types/json-schema": "^7.0.15",
"@types/jsonpath": "^0.2.0",
"@types/lodash-es": "^4.17.6",
"@types/luxon": "^3.2.0",
Expand Down
2 changes: 2 additions & 0 deletions packages/editor-ui/src/Interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -740,6 +740,8 @@ export interface CreateElementBase {
export interface NodeCreateElement extends CreateElementBase {
type: 'node';
subcategory: string;
resource?: string;
operation?: string;
properties: SimplifiedNodeType;
}

Expand Down
28 changes: 28 additions & 0 deletions packages/editor-ui/src/api/schemaPreview.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { request } from '@/utils/apiUtils';
import type { JSONSchema7 } from 'json-schema';

export type GetSchemaPreviewOptions = {
nodeType: string;
version: number;
resource?: string;
operation?: string;
};

const padVersion = (version: number) => {
return version.toString().split('.').concat(['0', '0']).slice(0, 3).join('.');
};

export const getSchemaPreview = async (
baseUrl: string,
options: GetSchemaPreviewOptions,
): Promise<JSONSchema7> => {
const { nodeType, version, resource, operation } = options;
const versionString = padVersion(version);
const path = ['schemas', nodeType, versionString, resource, operation].filter(Boolean).join('/');
return await request({
method: 'GET',
baseURL: baseUrl,
endpoint: `${path}.json`,
withCredentials: false,
});
};
46 changes: 42 additions & 4 deletions packages/editor-ui/src/components/RunData.vue
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import type {
} from '@/Interface';
import {
CORE_NODES_CATEGORY,
DATA_EDITING_DOCS_URL,
DATA_PINNING_DOCS_URL,
HTML_NODE_TYPE,
Expand All @@ -35,6 +36,7 @@ import {
MAX_DISPLAY_DATA_SIZE,
MAX_DISPLAY_DATA_SIZE_SCHEMA_VIEW,
NODE_TYPES_EXCLUDED_FROM_OUTPUT_NAME_APPEND,
SCHEMA_PREVIEW_EXPERIMENT,
TEST_PIN_DATA,
} from '@/constants';
Expand All @@ -60,7 +62,7 @@ import { useWorkflowsStore } from '@/stores/workflows.store';
import { executionDataToJson } from '@/utils/nodeTypesUtils';
import { getGenericHints } from '@/utils/nodeViewUtils';
import { searchInObject } from '@/utils/objectUtils';
import { clearJsonKey, isEmpty } from '@/utils/typesUtils';
import { clearJsonKey, isEmpty, isPresent } from '@/utils/typesUtils';
import { isEqual, isObject } from 'lodash-es';
import {
N8nBlockUi,
Expand All @@ -81,6 +83,9 @@ import { storeToRefs } from 'pinia';
import { useRoute } from 'vue-router';
import { useExecutionHelpers } from '@/composables/useExecutionHelpers';
import { useUIStore } from '@/stores/ui.store';
import { useSchemaPreviewStore } from '@/stores/schemaPreview.store';
import { asyncComputed } from '@vueuse/core';
import { usePostHog } from '@/stores/posthog.store';
const LazyRunDataTable = defineAsyncComponent(
async () => await import('@/components/RunDataTable.vue'),
Expand Down Expand Up @@ -181,6 +186,8 @@ const workflowsStore = useWorkflowsStore();
const sourceControlStore = useSourceControlStore();
const rootStore = useRootStore();
const uiStore = useUIStore();
const schemaPreviewStore = useSchemaPreviewStore();
const posthogStore = usePostHog();
const toast = useToast();
const route = useRoute();
Expand Down Expand Up @@ -544,6 +551,36 @@ const hasInputOverwrite = computed((): boolean => {
return Boolean(taskData?.inputOverride);
});
const isSchemaPreviewEnabled = computed(
() =>
props.paneType === 'input' &&
!(nodeType.value?.codex?.categories ?? []).some(
(category) => category === CORE_NODES_CATEGORY,
) &&
posthogStore.isFeatureEnabled(SCHEMA_PREVIEW_EXPERIMENT),
);
const hasPreviewSchema = asyncComputed(async () => {
if (!isSchemaPreviewEnabled.value || props.nodes.length === 0) return false;
const nodes = props.nodes
.filter((n) => n.depth === 1)
.map((n) => workflowsStore.getNodeByName(n.name))
.filter(isPresent);
for (const connectedNode of nodes) {
const { type, typeVersion, parameters } = connectedNode;
const hasPreview = await schemaPreviewStore.getSchemaPreview({
nodeType: type,
version: typeVersion,
resource: parameters.resource as string,
operation: parameters.operation as string,
});
if (hasPreview.ok) return true;
}
return false;
}, false);
watch(node, (newNode, prevNode) => {
if (newNode?.id === prevNode?.id) return;
init();
Expand Down Expand Up @@ -1346,7 +1383,8 @@ defineExpose({ enterEditMode });

<N8nRadioButtons
v-show="
hasNodeRun && (inputData.length || binaryData.length || search) && !editMode.enabled
hasPreviewSchema ||
(hasNodeRun && (inputData.length || binaryData.length || search) && !editMode.enabled)
"
:model-value="displayMode"
:options="displayModes"
Expand Down Expand Up @@ -1590,7 +1628,7 @@ defineExpose({ enterEditMode });
</div>

<div
v-else-if="!hasNodeRun && !(displaysMultipleNodes && node?.disabled)"
v-else-if="!hasNodeRun && !(displaysMultipleNodes && (node?.disabled || hasPreviewSchema))"
:class="$style.center"
>
<slot name="node-not-run"></slot>
Expand Down Expand Up @@ -1781,7 +1819,7 @@ defineExpose({ enterEditMode });
<LazyRunDataHtml :input-html="inputHtml" />
</Suspense>

<Suspense v-else-if="hasNodeRun && isSchemaView">
<Suspense v-else-if="(hasNodeRun || hasPreviewSchema) && isSchemaView">
<LazyRunDataSchema
:nodes="nodes"
:mapping-enabled="mappingEnabled"
Expand Down
Loading

0 comments on commit cbb605f

Please sign in to comment.