Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Refactor component script and check if enum contents are numbers #13694

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@
"default": false,
"$ref": "expression.schema.v1.json#/definitions/boolean"
},
"headingLevel": { "title": "HeadingLevel", "enum": [2, 3, 4, 5, 6], "type": "string" }
"headingLevel": { "title": "HeadingLevel", "enum": [2, 3, 4, 5, 6], "type": "number" }
},
"required": ["id", "type", "children"],
"title": "Accordion component schema"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@
"title": "Heading level",
"description": "The heading level of the group title.",
"enum": [2, 3, 4, 5, 6],
"type": "string"
"type": "number"
}
},
"required": ["id", "type", "children"],
Expand Down
17 changes: 17 additions & 0 deletions frontend/scripts/componentSchemas/fileUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import type { AppFrontendVersion } from './version';
import { versionSettings } from './version';
import path from 'path';
import fs from 'fs';

export const writeToFile = (name: string, data: any, version: AppFrontendVersion) => {
const dirPath = path.resolve(__dirname, versionSettings[version].componentSchemaPath);
const fileName = `${dirPath}/${name}.schema.v1.json`;

fs.writeFile(fileName, JSON.stringify(data), (err: any) => {
if (err) {
console.log(err);
return;
}
console.log(`Wrote ${fileName}`);
});
};
38 changes: 34 additions & 4 deletions frontend/scripts/componentSchemas/languageUtils.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,34 @@
import nb from '../../language/src/nb.json';

// Logs language keys and values related to the "Tekst" accordion in the component configuration.
// Use it to find missing entries in the language file(s).
export const allTextResourceBindingKeys = [];

export const pushTextResourceBindingKeys = (schema: any) => {
if (schema.properties?.textResourceBindings) {
const textResourceBindingKeys = Object.keys(schema.properties.textResourceBindings.properties);
allTextResourceBindingKeys.push(...textResourceBindingKeys);
}
};

export const sortTextResourceBindings = (textResourceBindings: any) => {
const { title, description, help, ...rest } = textResourceBindings;
const sorted: any = {};
if (title) {
sorted.title = title;
}
if (description) {
sorted.description = description;
}
if (help) {
sorted.help = help;
}
return { ...sorted, ...rest };
};

/**
* Logs language keys and values displayed in the "Tekst" accordion in the component configuration column.
* Use it to find missing entries in the language file.
* @param textResourceBindingKeys Array of text resource binding keys.
*/
export const logTextResourceLabels = (textResourceBindingKeys: string[]) => {
textResourceBindingKeys.sort().forEach((key) => {
console.log(
Expand All @@ -13,8 +40,11 @@ export const logTextResourceLabels = (textResourceBindingKeys: string[]) => {
});
};

// Logs various language keys and values related to the component configuration.
// Use it to find missing entries in the language file(s).
/**
* Logs all language keys and values in the component configuration column, except for those in the "Tekst" accordion.
* Use it to find missing entries in the language file.
* @param componentPropertyKeys Array of component property keys.
*/
export const logComponentPropertyLabels = (componentPropertyKeys: string[]) => {
componentPropertyKeys.sort().forEach((key) => {
console.log(
Expand Down
103 changes: 10 additions & 93 deletions frontend/scripts/componentSchemas/run.ts
Original file line number Diff line number Diff line change
@@ -1,97 +1,14 @@
import { expandAllOf, expandAnyOf, expandRefsInProperties, verifySchema } from './schemaUtils';
import { allPropertyKeys, generateComponentSchema } from './schemaUtils';
import type { AppFrontendVersion } from './version';
import { isValidVersion, versionSettings } from './version';
import { isValidVersion } from './version';
import { getLayoutSchema } from './api';
import { logComponentPropertyLabels, logTextResourceLabels } from './languageUtils';

const allTextResourceBindingKeys = [];
const allPropertyKeys = [];

const writeToFile = (name: string, data: any, version: AppFrontendVersion) => {
const path = require('path');
const fs = require('fs');

const dirPath = path.resolve(__dirname, versionSettings[version].componentSchemaPath);
const fileName = `${dirPath}/${name}.schema.v1.json`;

fs.writeFile(fileName, JSON.stringify(data), function (err: any) {
if (err) return console.log(err);
console.log(`Wrote ${fileName}`);
});
};

const addTextResourceBindingKeys = (schema: any) => {
if (schema.properties?.textResourceBindings) {
const textResourceBindingKeys = Object.keys(schema.properties.textResourceBindings.properties);
allTextResourceBindingKeys.push(...textResourceBindingKeys);
}
};

const addProperties = (propertyKeys: string[]) => {
allPropertyKeys.push(...propertyKeys);
};

const generateComponentSchema = (name: string, layoutSchema: any, version: string) => {
const definitionName = `Comp${name}`;
console.log('definitionName: ', definitionName);
const componentSchema = layoutSchema.definitions[definitionName];
let schema: any = {
$id: `https://altinncdn.no/schemas/json/component/${name}.schema.v1.json`,
$schema: layoutSchema.$schema,
};

// The v4 schema has external definitions. This code block is needed to fetch v4 properties correctly.
const externalDefinitionName = definitionName + 'External';
if (version == 'v4' && layoutSchema.definitions[externalDefinitionName]?.allOf) {
componentSchema.allOf = layoutSchema.definitions[externalDefinitionName].allOf;
}

if (componentSchema.allOf) {
schema = { ...schema, ...expandAllOf(componentSchema, layoutSchema) };
const expectedProperties = Object.keys(
componentSchema.allOf[componentSchema.allOf.length - 1].properties,
);
addProperties(expectedProperties);

if (
!verifySchema(
schema,
Object.keys(componentSchema.allOf[componentSchema.allOf.length - 1].properties),
)
) {
return null;
}
} else if (componentSchema.anyOf) {
schema.anyOf = expandAnyOf(componentSchema, layoutSchema);
}

// Expand all refs in properties
schema.properties = expandRefsInProperties(schema.properties, layoutSchema);

// Sort text resource binding keys
if (schema.properties?.textResourceBindings) {
schema.properties.textResourceBindings.properties = sortTextResourceBindings(
schema.properties.textResourceBindings.properties,
);
}
schema.title = `${name} component schema`;
return schema;
};

const sortTextResourceBindings = (textResourceBindings: any) => {
const { title, description, help, ...rest } = textResourceBindings;
const sorted: any = {};
if (title) {
sorted.title = title;
}
if (description) {
sorted.description = description;
}
if (help) {
sorted.help = help;
}
return { ...sorted, ...rest };
};
import {
pushTextResourceBindingKeys,
allTextResourceBindingKeys,
logComponentPropertyLabels,
logTextResourceLabels,
} from './languageUtils';
import { writeToFile } from './fileUtils';

const run = async () => {
let version: string = process.argv.length > 2 ? process.argv[2] : '';
Expand All @@ -109,7 +26,7 @@ const run = async () => {
componentName = componentName === 'AddressComponent' ? 'Address' : componentName;

const schema = generateComponentSchema(componentName, layoutSchema, version);
addTextResourceBindingKeys(schema);
pushTextResourceBindingKeys(schema);
writeToFile(componentName, schema, version as AppFrontendVersion);
});

Expand Down
65 changes: 65 additions & 0 deletions frontend/scripts/componentSchemas/schemaUtils.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { ensureTypeWithEnums } from './schemaUtils';

describe('ensureTypeWithEnums', () => {
it('should set schema.type to "string" when schema.enum contains a string', () => {
const schema: any = {
enum: ['value1', 'value2'],
};
ensureTypeWithEnums(schema);
expect(schema.type).toBe('string');
});

it('should set schema.type to "number" when schema.enum contains a number', () => {
const schema: any = {
enum: [1, 2, 3],
};
ensureTypeWithEnums(schema);
expect(schema.type).toBe('number');
});

it('should set schema.items.type to "string" when schema.items.enum contains a string', () => {
const schema: any = {
items: {
enum: ['item1', 'item2'],
},
};
ensureTypeWithEnums(schema);
expect(schema.items.type).toBe('string');
});

it('should set schema.items.type to "number" when schema.items.enum contains a number', () => {
const schema: any = {
items: {
enum: [10, 20, 30],
},
};
ensureTypeWithEnums(schema);
expect(schema.items.type).toBe('number');
});

it('should not set schema.type when schema.enum is empty', () => {
const schema: any = {
enum: [],
};
ensureTypeWithEnums(schema);
expect(schema.type).toBeUndefined();
});

it('should not set schema.items.type when schema.items.enum is empty', () => {
const schema: any = {
items: {
enum: [],
},
};
ensureTypeWithEnums(schema);
expect(schema.items.type).toBeUndefined();
});

it('should not modify schema if there is no enum or items.enum', () => {
const schema: any = {
type: 'array',
};
ensureTypeWithEnums(schema);
expect(schema).toEqual({ type: 'array' });
});
});
Loading