Skip to content

Commit

Permalink
Add API route for schema static files
Browse files Browse the repository at this point in the history
  • Loading branch information
elsmr committed Jan 22, 2025
1 parent 56c93ca commit d85a519
Show file tree
Hide file tree
Showing 8 changed files with 203 additions and 6 deletions.
2 changes: 1 addition & 1 deletion packages/@n8n/nodes-langchain/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"clean": "rimraf dist .turbo",
"dev": "pnpm run watch",
"typecheck": "tsc --noEmit",
"build": "tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json && pnpm n8n-copy-icons && pnpm n8n-generate-metadata",
"build": "tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json && pnpm n8n-copy-static-files && pnpm n8n-generate-metadata",
"format": "biome format --write .",
"format:check": "biome ci .",
"lint": "eslint nodes credentials utils --quiet",
Expand Down
40 changes: 40 additions & 0 deletions packages/cli/src/__tests__/load-nodes-and-credentials.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -211,4 +211,44 @@ describe('LoadNodesAndCredentials', () => {
expect(result.description.displayName).toBe('Special @#$% Node Tool');
});
});

describe('resolveSchema', () => {
let instance: LoadNodesAndCredentials;

beforeEach(() => {
instance = new LoadNodesAndCredentials(mock(), mock(), mock(), mock());
instance.knownNodes['n8n-nodes-base.test'] = {
className: 'Test',
sourcePath: '/nodes-base/dist/nodes/Test/Test.node.js',
};
});

it('should return undefined if the node is not known', () => {
const result = instance.resolveSchema({
node: 'n8n-nodes-base.doesNotExist',
version: '1.0.0',
resource: 'account',
operation: 'get',
});
expect(result).toBeUndefined();
});

it('should return the correct path if the node is known', () => {
const result = instance.resolveSchema({
node: 'n8n-nodes-base.test',
version: '1.0.0',
resource: 'account',
operation: 'get',
});
expect(result).toEqual('/nodes-base/dist/nodes/Test/__schema__/v1.0.0/account/get.json');
});

it('should return the correct path if there is no resource or operation', () => {
const result = instance.resolveSchema({
node: 'n8n-nodes-base.test',
version: '1.0.0',
});
expect(result).toEqual('/nodes-base/dist/nodes/Test/__schema__/v1.0.0.json');
});
});
});
23 changes: 23 additions & 0 deletions packages/cli/src/load-nodes-and-credentials.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,29 @@ export class LoadNodesAndCredentials {
return isContainedWithin(loader.directory, filePath) ? filePath : undefined;
}

resolveSchema({
node,
version,
resource,
operation,
}: {
node: string;
version: string;
resource?: string;
operation?: string;
}): string | undefined {
const nodePath = this.known.nodes[node]?.sourcePath;
if (!nodePath) {
return undefined;
}

const nodeParentPath = path.dirname(nodePath);
const schemaPath = ['__schema__', `v${version}`, resource, operation].filter(Boolean).join('/');
const filePath = path.resolve(nodeParentPath, schemaPath + '.json');

return isContainedWithin(nodeParentPath, filePath) ? filePath : undefined;
}

getCustomDirectories(): string[] {
const customDirectories = [this.instanceSettings.customExtensionDir];

Expand Down
19 changes: 19 additions & 0 deletions packages/cli/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -322,8 +322,27 @@ export class Server extends AbstractServer {
res.sendStatus(404);
};

const serveSchemas: express.RequestHandler = async (req, res) => {
const { node, version, resource, operation } = req.params;
const filePath = this.loadNodesAndCredentials.resolveSchema({
node,
resource,
operation,
version,
});

if (filePath) {
try {
await fsAccess(filePath);
return res.sendFile(filePath, cacheOptions);
} catch {}
}
res.sendStatus(404);
};

this.app.use('/icons/@:scope/:packageName/*/*.(svg|png)', serveIcons);
this.app.use('/icons/:packageName/*/*.(svg|png)', serveIcons);
this.app.use('/schemas/:node/:version/:resource?/:operation?.json', serveSchemas);

const isTLSEnabled =
this.globalConfig.protocol === 'https' && !!(this.sslKey && this.sslCert);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,18 @@ const { cp } = require('fs/promises');
const { packageDir } = require('./common');

const limiter = pLimit(20);
const icons = glob.sync('{nodes,credentials}/**/*.{png,svg}', { cwd: packageDir });
const staticFiles = glob.sync(
['{nodes,credentials}/**/*.{png,svg}', 'nodes/**/__schema__/**/*.json'],
{
cwd: packageDir,
},
);

(async () => {
await Promise.all(
icons.map((icon) =>
staticFiles.map((path) =>
limiter(() => {
return cp(icon, `dist/${icon}`, { recursive: true });
return cp(path, `dist/${path}`, { recursive: true });
}),
),
);
Expand Down
2 changes: 1 addition & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"main": "dist/index",
"types": "dist/index.d.ts",
"bin": {
"n8n-copy-icons": "./bin/copy-icons",
"n8n-copy-static-files": "./bin/copy-static-files",
"n8n-generate-translations": "./bin/generate-translations",
"n8n-generate-metadata": "./bin/generate-metadata"
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
{
"type": "object",
"value": [
{
"key": "account",
"type": "Object",
"value": [
{
"key": "accountUrl",
"type": "string",
"value": "",
"path": ".account.accountUrl"
},
{
"key": "createdTimestamp",
"type": "string",
"value": "",
"path": ".account.createdTimestamp"
},
{
"key": "id",
"type": "string",
"value": "",
"path": ".account.id"
},
{
"key": "links",
"type": "Object",
"value": [
{
"key": "accountContacts",
"type": "string",
"value": "",
"path": ".account.links.accountContacts"
},
{
"key": "accountCustomFieldData",
"type": "string",
"value": "",
"path": ".account.links.accountCustomFieldData"
},
{
"key": "contactEmails",
"type": "string",
"value": "",
"path": ".account.links.contactEmails"
},
{
"key": "emailActivities",
"type": "string",
"value": "",
"path": ".account.links.emailActivities"
},
{
"key": "notes",
"type": "string",
"value": "",
"path": ".account.links.notes"
},
{
"key": "owner",
"type": "string",
"value": "",
"path": ".account.links.owner"
},
{
"key": "required",
"type": "string",
"value": "",
"path": ".account.links.required"
}
],
"path": ".account.links"
},
{
"key": "name",
"type": "string",
"value": "",
"path": ".account.name"
},
{
"key": "owner",
"type": "string",
"value": "",
"path": ".account.owner"
},
{
"key": "updatedTimestamp",
"type": "string",
"value": "",
"path": ".account.updatedTimestamp"
},
{
"key": "required",
"type": "string",
"value": "",
"path": ".account.required"
}
],
"path": ".account"
},
{
"key": "required",
"type": "string",
"value": "",
"path": ".required"
}
],
"path": ""
}
2 changes: 1 addition & 1 deletion packages/nodes-base/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"clean": "rimraf dist .turbo",
"dev": "pnpm watch",
"typecheck": "tsc --noEmit",
"build": "tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json && pnpm n8n-copy-icons && pnpm n8n-generate-translations && pnpm n8n-generate-metadata",
"build": "tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json && pnpm n8n-copy-static-files && pnpm n8n-generate-translations && pnpm n8n-generate-metadata",
"format": "biome format --write .",
"format:check": "biome ci .",
"lint": "eslint nodes credentials utils test --quiet && node ./scripts/validate-load-options-methods.js",
Expand Down

0 comments on commit d85a519

Please sign in to comment.