Skip to content

Commit

Permalink
fix(schema): force schema metadata update even in production (#947)
Browse files Browse the repository at this point in the history
  • Loading branch information
Thenkei committed Feb 1, 2024
1 parent e5c5b83 commit 3ce68e0
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 167 deletions.
13 changes: 6 additions & 7 deletions packages/agent/src/agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,9 @@ export default class Agent<S extends TSchema = TSchema> extends FrameworkMounter
}

// Either load the schema from the file system or build it
let schema: ForestSchema;
let schema: Pick<ForestSchema, 'collections'>;

const { meta } = SchemaGenerator.buildMetadata(this.customizationService.buildFeatures());

// When using experimental no-code features even in production we need to build a new schema
if (!experimental?.webhookCustomActions && isProduction) {
Expand All @@ -239,16 +241,13 @@ export default class Agent<S extends TSchema = TSchema> extends FrameworkMounter
throw new Error(`Can't load ${schemaPath}. Providing a schema is mandatory in production.`);
}
} else {
schema = await SchemaGenerator.buildSchema(
dataSource,
this.customizationService.buildFeatures(),
);
schema = await SchemaGenerator.buildSchema(dataSource);

const pretty = stringify(schema, { maxLength: 100 });
const pretty = stringify({ ...schema, meta }, { maxLength: 100 });
await writeFile(schemaPath, pretty, { encoding: 'utf-8' });
}

// Send schema to forest servers
await this.options.forestAdminClient.postSchema(schema);
await this.options.forestAdminClient.postSchema({ ...schema, meta });
}
}
14 changes: 8 additions & 6 deletions packages/agent/src/utils/forest-schema/generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,20 @@ import { ForestSchema } from '@forestadmin/forestadmin-client';
import SchemaGeneratorCollection from './generator-collection';

export default class SchemaGenerator {
static async buildSchema(
dataSource: DataSource,
features: Record<string, string> | null,
): Promise<ForestSchema> {
const { version } = require('../../../package.json'); // eslint-disable-line @typescript-eslint/no-var-requires,global-require,max-len

static async buildSchema(dataSource: DataSource): Promise<Pick<ForestSchema, 'collections'>> {
return {
collections: await Promise.all(
[...dataSource.collections]
.sort((a, b) => a.name.localeCompare(b.name))
.map(c => SchemaGeneratorCollection.buildSchema(c)),
),
};
}

static buildMetadata(features: Record<string, string> | null): Pick<ForestSchema, 'meta'> {
const { version } = require('../../../package.json'); // eslint-disable-line @typescript-eslint/no-var-requires,global-require,max-len

return {
meta: {
liana: 'forest-nodejs-agent',
liana_version: version,
Expand Down
20 changes: 15 additions & 5 deletions packages/agent/test/agent.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ describe('Agent', () => {
expect(mockNocodeCustomizer.addDataSource).toHaveBeenCalledWith('factory');
});

test('start should upload apimap', async () => {
test('start should create new schema definition/meta and upload apimap', async () => {
const agent = new Agent(options);
await agent.start();

Expand Down Expand Up @@ -261,7 +261,7 @@ describe('Agent', () => {
);
});

test('start should upload apimap when unknown', async () => {
test('start should read existing definition and upload apimap with current meta', async () => {
const agent = new Agent(options);
await agent.start();

Expand All @@ -271,9 +271,18 @@ describe('Agent', () => {
expect(mockNocodeCustomizer.getDataSource).toHaveBeenCalledTimes(1);
expect(mockCustomizer.updateTypesOnFileSystem).not.toHaveBeenCalled();

const schemaContent = JSON.parse(await readFile(options.schemaPath, 'utf8'));

expect(mockPostSchema).toHaveBeenCalledWith(schemaContent);
const { collections } = JSON.parse(await readFile(options.schemaPath, 'utf8'));

expect(mockPostSchema).toHaveBeenCalledWith(
expect.objectContaining({
collections,
meta: expect.objectContaining({
liana: 'forest-nodejs-agent',
liana_features: null,
liana_version: expect.any(String),
}),
}),
);
});

test('start should not update schema when specified', async () => {
Expand All @@ -285,6 +294,7 @@ describe('Agent', () => {
expect(mockMakeRoutes).toHaveBeenCalledTimes(1);
expect(mockNocodeCustomizer.getDataSource).toHaveBeenCalledTimes(1);
expect(mockCustomizer.updateTypesOnFileSystem).not.toHaveBeenCalled();

expect(mockPostSchema).not.toHaveBeenCalled();
});
});
Expand Down
99 changes: 53 additions & 46 deletions packages/agent/test/utils/forest-schema/generator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,63 +2,70 @@ import SchemaGenerator from '../../../src/utils/forest-schema/generator';
import * as factories from '../../__factories__';

describe('SchemaGenerator', () => {
test('should serialize collections', async () => {
const dataSource = factories.dataSource.buildWithCollection(
factories.collection.build({ name: 'books' }),
);
describe('buildSchema', () => {
test('should serialize collections', async () => {
const dataSource = factories.dataSource.buildWithCollection(
factories.collection.build({ name: 'books' }),
);

const schema = await SchemaGenerator.buildSchema(dataSource, null);
const schema = await SchemaGenerator.buildSchema(dataSource);

expect(schema).toStrictEqual({
collections: [expect.objectContaining({ name: 'books' })],
meta: {
liana: 'forest-nodejs-agent',
liana_version: expect.any(String),
liana_features: null,
stack: {
engine: 'nodejs',
engine_version: expect.any(String),
},
},
expect(schema).toStrictEqual({
collections: [expect.objectContaining({ name: 'books' })],
});
});
});

test('should sort the collections by name', async () => {
const dataSource = factories.dataSource.buildWithCollections([
factories.collection.build({ name: 'B' }),
factories.collection.build({ name: 'ba' }),
factories.collection.build({ name: 'b' }),
factories.collection.build({ name: 'a' }),
]);
test('should sort the collections by name', async () => {
const dataSource = factories.dataSource.buildWithCollections([
factories.collection.build({ name: 'B' }),
factories.collection.build({ name: 'ba' }),
factories.collection.build({ name: 'b' }),
factories.collection.build({ name: 'a' }),
]);

const schema = await SchemaGenerator.buildSchema(dataSource, null);
const schema = await SchemaGenerator.buildSchema(dataSource);

expect(schema.collections.map(c => c.name)).toStrictEqual(['a', 'b', 'B', 'ba']);
expect(schema.collections.map(c => c.name)).toStrictEqual(['a', 'b', 'B', 'ba']);
});
});

test('it should serialize features', async () => {
const dataSource = factories.dataSource.buildWithCollection(
factories.collection.build({ name: 'books' }),
);
const schema = await SchemaGenerator.buildSchema(dataSource, {
'webhook-custom-actions': '1.0.0',
'awesome-feature': '3.0.0',
});
describe('buildMetadata', () => {
test('should serialize meta', async () => {
const schema = await SchemaGenerator.buildMetadata(null);

expect(schema).toStrictEqual({
collections: [expect.objectContaining({ name: 'books' })],
meta: {
liana: 'forest-nodejs-agent',
liana_version: expect.any(String),
liana_features: {
'webhook-custom-actions': '1.0.0',
'awesome-feature': '3.0.0',
expect(schema).toStrictEqual({
meta: {
liana: 'forest-nodejs-agent',
liana_version: expect.any(String),
liana_features: null,
stack: {
engine: 'nodejs',
engine_version: expect.any(String),
},
},
stack: {
engine: 'nodejs',
engine_version: expect.any(String),
});
});

test('it should serialize features', async () => {
const schema = await SchemaGenerator.buildMetadata({
'webhook-custom-actions': '1.0.0',
'awesome-feature': '3.0.0',
});

expect(schema).toStrictEqual({
meta: {
liana: 'forest-nodejs-agent',
liana_version: expect.any(String),
liana_features: {
'webhook-custom-actions': '1.0.0',
'awesome-feature': '3.0.0',
},
stack: {
engine: 'nodejs',
engine_version: expect.any(String),
},
},
},
});
});
});
});
103 changes: 0 additions & 103 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1362,109 +1362,6 @@
path-to-regexp "^6.1.0"
reusify "^1.0.4"

"@forestadmin/agent@1.36.11":
version "1.36.11"
resolved "https://registry.yarnpkg.com/@forestadmin/agent/-/agent-1.36.11.tgz#17b830f901b17a47d183906f721c11232fcf10b5"
integrity sha512-WB1L5EpreVzjR2YhlxcRAP2TAUzwy79pdeffTHzueAkTUf0r0yRFUn+0eK6QMbkNqdmJbxNqZlCtxZo9wRrPTg==
dependencies:
"@fast-csv/format" "^4.3.5"
"@fastify/express" "^1.1.0"
"@forestadmin/datasource-customizer" "1.39.3"
"@forestadmin/datasource-toolkit" "1.29.1"
"@forestadmin/forestadmin-client" "1.25.3"
"@koa/cors" "^5.0.0"
"@koa/router" "^12.0.0"
forest-ip-utils "^1.0.1"
json-api-serializer "^2.6.6"
json-stringify-pretty-compact "^3.0.0"
jsonwebtoken "^9.0.0"
koa "^2.14.1"
koa-bodyparser "^4.3.0"
koa-jwt "^4.0.4"
luxon "^3.2.1"
object-hash "^3.0.0"
superagent "^8.0.6"
uuid "^9.0.0"

"@forestadmin/datasource-customizer@1.39.3":
version "1.39.3"
resolved "https://registry.yarnpkg.com/@forestadmin/datasource-customizer/-/datasource-customizer-1.39.3.tgz#10365e79d3100f5a2e347d8cc39e3b98f8bc2856"
integrity sha512-gjFMqCSWU8uPYSETsC22Dkk3kbk9VV7V1FSNjhipkPyjO+duoM9IafuOb5AwJa8rFhF6y853xngXL8WBr71ugw==
dependencies:
"@forestadmin/datasource-toolkit" "1.29.1"
file-type "^16.5.4"
luxon "^3.2.1"
object-hash "^3.0.0"
uuid "^9.0.0"

"@forestadmin/datasource-customizer@1.40.0":
version "1.40.0"
resolved "https://registry.yarnpkg.com/@forestadmin/datasource-customizer/-/datasource-customizer-1.40.0.tgz#b7ab00c3fc13441567f1ff1924f9f147f6160517"
integrity sha512-WEKv6wIlYvmMq2CEQRiwxfPbLf99NAAZfpBFLct8Vjt3ofYydw+qEsjbRZqB9RruIdeF2I1tg6Xgr00pr27jYg==
dependencies:
"@forestadmin/datasource-toolkit" "1.29.1"
file-type "^16.5.4"
luxon "^3.2.1"
object-hash "^3.0.0"
uuid "^9.0.0"

"@forestadmin/datasource-dummy@1.0.90":
version "1.0.90"
resolved "https://registry.yarnpkg.com/@forestadmin/datasource-dummy/-/datasource-dummy-1.0.90.tgz#a4310f1417808a7f2c3d60452478cb54b352ac5e"
integrity sha512-n2/ls1/ms5qxTQst4HaCgh9Ol4po9oWQtdSxOPoFu0vOVywmy43GyP28OY4+Wir4oMmGXqXugd96R+XulUC3uA==
dependencies:
"@forestadmin/datasource-customizer" "1.40.0"
"@forestadmin/datasource-toolkit" "1.29.1"

"@forestadmin/datasource-mongoose@1.5.32":
version "1.5.32"
resolved "https://registry.yarnpkg.com/@forestadmin/datasource-mongoose/-/datasource-mongoose-1.5.32.tgz#dfdad4dd0d63f93ba256d1136f990c334e3b3007"
integrity sha512-qkgYlr9+wQc+O9gXrHaQ6gI29SOlFWvp1CsEZ/wkjeH0k5GRQXSnkRkiDL4lVH1xb6Lk6OeVKdQX9la6sHef/g==
dependencies:
"@forestadmin/datasource-toolkit" "1.29.1"
luxon "^3.2.1"

"@forestadmin/datasource-sequelize@1.5.26":
version "1.5.26"
resolved "https://registry.yarnpkg.com/@forestadmin/datasource-sequelize/-/datasource-sequelize-1.5.26.tgz#061b59d6c048c0642f8fd267d3f297a6abf8058d"
integrity sha512-wWaDi49NIJVxb2oglV/mIDK895Gi3A13o1ndt+9wuWMERlYQgOUEbySS5RntONzSsJ2OfdDsExIcKLmF6Btnrg==
dependencies:
"@forestadmin/datasource-toolkit" "1.29.1"

"@forestadmin/datasource-sql@1.7.43":
version "1.7.43"
resolved "https://registry.yarnpkg.com/@forestadmin/datasource-sql/-/datasource-sql-1.7.43.tgz#a6e2b7f90d2cb8a547888c48736a6322e8245772"
integrity sha512-Z0U5OTBtL6QWJgTLhzK9AurBhVqg/ZzRTMCLA/bOtV9zRuObz4WiqAT6O6L0og0KEtzYtmMioUfAhoOHYSIy0A==
dependencies:
"@forestadmin/datasource-sequelize" "1.5.26"
"@forestadmin/datasource-toolkit" "1.29.1"
"@types/ssh2" "^1.11.11"
pluralize "^8.0.0"
sequelize "^6.28.0"
socks "^2.7.1"
ssh2 "^1.14.0"

"@forestadmin/datasource-toolkit@1.29.1":
version "1.29.1"
resolved "https://registry.yarnpkg.com/@forestadmin/datasource-toolkit/-/datasource-toolkit-1.29.1.tgz#bf1cda9d3684208509a891367b39ec7ff112618c"
integrity sha512-JJnxRJyvZUIemp+mU0sZwIieiJys2RAAMb8JPNqmSWDmkXgVV0t0mbdmegF3xwk8lQvYbRqcTNOrT9Q0wjLXoQ==
dependencies:
luxon "^3.2.1"
object-hash "^3.0.0"
uuid "^9.0.0"

"@forestadmin/forestadmin-client@1.25.3":
version "1.25.3"
resolved "https://registry.yarnpkg.com/@forestadmin/forestadmin-client/-/forestadmin-client-1.25.3.tgz#1924a8c3e52e18282d633c2aa92137519ca9ce30"
integrity sha512-c/0igkHStVem+7SVgQGmiPR3HR6WGjQBknFdBlHUypb7qADdBeMh81meMsaq5rT74czafdb5u4VWUDu29i7f5Q==
dependencies:
eventsource "2.0.2"
json-api-serializer "^2.6.6"
jsonwebtoken "^9.0.0"
object-hash "^3.0.0"
openid-client "^5.3.1"
superagent "^8.0.6"

"@gar/promisify@^1.0.1", "@gar/promisify@^1.1.3":
version "1.1.3"
resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6"
Expand Down

0 comments on commit 3ce68e0

Please sign in to comment.