Skip to content

Commit

Permalink
Refactor script and check if enum content is number
Browse files Browse the repository at this point in the history
  • Loading branch information
ErlingHauan committed Oct 4, 2024
1 parent 3c2b120 commit 656843e
Show file tree
Hide file tree
Showing 5 changed files with 198 additions and 135 deletions.
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}`);
});
};
47 changes: 43 additions & 4 deletions frontend/scripts/componentSchemas/languageUtils.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,43 @@
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 = [];

/**
* Adds text resource binding keys from a schema to the global list.
* @param schema The schema to extract keys from.
*/
export const addTextResourceBindingKeys = (schema: any) => {
if (schema.properties?.textResourceBindings) {
const textResourceBindingKeys = Object.keys(schema.properties.textResourceBindings.properties);
allTextResourceBindingKeys.push(...textResourceBindingKeys);
}
};

/**
* Sorts text resource bindings, placing 'title', 'description', and 'help' first.
* @param textResourceBindings The text resource bindings to sort.
* @returns The sorted text resource bindings.
*/
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 +49,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
107 changes: 14 additions & 93 deletions frontend/scripts/componentSchemas/run.ts
Original file line number Diff line number Diff line change
@@ -1,98 +1,19 @@
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 {
addTextResourceBindingKeys,
allTextResourceBindingKeys,
logComponentPropertyLabels,
logTextResourceLabels,
} from './languageUtils';
import { writeToFile } from './fileUtils';

/**
* Main function that runs the script.
* Fetches the layout schema, generates component schemas, and logs language keys.
*/
const run = async () => {
let version: string = process.argv.length > 2 ? process.argv[2] : '';
if (!isValidVersion(version)) {
Expand Down
157 changes: 121 additions & 36 deletions frontend/scripts/componentSchemas/schemaUtils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,17 @@
import jsonpointer from 'jsonpointer';
import { sortTextResourceBindings } from './languageUtils';

export const allPropertyKeys = [];

/**
* Expands a ref in a schema from a reference to the actual schema
* @param ref The ref to expand
* @param layoutSchema The full layout schema
* @returns The expanded schema
*/
export const expandRef = (ref: string, layoutSchema: any) => {
return jsonpointer.get(layoutSchema, ref.replace('#/', '/'));
};

/**
* Expands allOf node in schema by combining all properties together in one object.
Expand Down Expand Up @@ -52,24 +65,55 @@ export const expandAnyOf = (schema: any, layoutSchema: any) => {
};

/**
* Expands a ref in a schema from a reference to the actual schema
* @param ref The ref to expand
* Expands all refs in properties in a schema
* @param properties The properties to expand
* @param layoutSchema The full layout schema
* @returns The expanded schema
* @returns The expanded properties
*/
export const expandRef = (ref: string, layoutSchema: any) => {
return jsonpointer.get(layoutSchema, ref.replace('#/', '/'));
export const expandRefsInProperties = (properties: any, layoutSchema: any) => {
const expandedProperties = { ...properties };
for (const property in properties) {
if (expandedProperties[property].$ref && expandedProperties[property].$ref.startsWith('#/')) {
expandedProperties[property] = expandRef(expandedProperties[property].$ref, layoutSchema);
}
if (expandedProperties[property].items?.$ref) {
expandedProperties[property].items = expandRef(
expandedProperties[property].items.$ref,
layoutSchema,
);
}
if (expandedProperties[property].allOf) {
expandedProperties[property] = expandAllOf(expandedProperties[property], layoutSchema);
}

if (expandedProperties[property].anyOf) {
expandedProperties[property].anyOf = expandAnyOf(expandedProperties[property], layoutSchema);
}
ensureTypeWithEnums(expandedProperties[property]);
}

return expandedProperties;
};

/**
* Ensures that a schema with enum values has type string
* @param schema The schema to ensure string type for
* Ensures that a schema with enum values has correct type (string or number)
* @param schema The schema to ensure type for
*/
export const ensureStringTypeWithEnums = (schema: any) => {
if (schema.enum) {
schema.type = 'string';
} else if (schema.items?.enum) {
schema.items.type = 'string';
export const ensureTypeWithEnums = (schema: any) => {
if (schema.enum && schema.enum.length > 0) {
const firstEnumValue = schema.enum[0];
if (typeof firstEnumValue === 'string') {
schema.type = 'string';
} else if (typeof firstEnumValue === 'number') {
schema.type = 'number';
}
} else if (schema.items?.enum && schema.items.enum.length > 0) {
const firstEnumValue = schema.items.enum[0];
if (typeof firstEnumValue === 'string') {
schema.items.type = 'string';
} else if (typeof firstEnumValue === 'number') {
schema.items.type = 'number';
}
}
};

Expand All @@ -88,37 +132,78 @@ export const verifySchema = (schema: any, expectedProperties: string[]) => {
console.log(`Missing properties: ${missingProperties.join(', ')}`);
return false;
}

return true;
};

/**
* Expands all refs in properties in a schema
* @param properties The properties to expand
* @param layoutSchema The full layout schema
* @returns The expanded properties
* Adds property keys to the global list.
* @param propertyKeys Array of property keys to add.
*/
export const expandRefsInProperties = (properties: any, layoutSchema: any) => {
const expandedProperties = { ...properties };
for (const property in properties) {
if (expandedProperties[property].$ref && expandedProperties[property].$ref.startsWith('#/')) {
expandedProperties[property] = expandRef(expandedProperties[property].$ref, layoutSchema);
}
if (expandedProperties[property].items?.$ref) {
expandedProperties[property].items = expandRef(
expandedProperties[property].items.$ref,
layoutSchema,
);
}
if (expandedProperties[property].allOf) {
expandedProperties[property] = expandAllOf(expandedProperties[property], layoutSchema);
}
const addProperties = (propertyKeys: string[]) => {
allPropertyKeys.push(...propertyKeys);
};

if (expandedProperties[property].anyOf) {
expandedProperties[property].anyOf = expandAnyOf(expandedProperties[property], layoutSchema);
/**
* Retrieves the component schema based on the version (the v4 schema has an external reference)
* @param definitionName The name of the component definition.
* @param layoutSchema The full layout schema.
* @param version The app frontend version.
* @returns The component schema.
*/

const getComponentSchema = (definitionName: string, layoutSchema: any, version: string) => {
if (version === 'v4') {
console.log('definitionName: ', definitionName + 'External');
return expandRef(layoutSchema.definitions[definitionName].$ref, layoutSchema);
}
console.log('definitionName: ', definitionName);
return layoutSchema.definitions[definitionName];
};

/**
* Generates a component schema by expanding definitions and resolving references.
* @param componentName The name of the component.
* @param layoutSchema The full layout schema.
* @param version The app frontend version.
* @returns The generated component schema.
*/
export const generateComponentSchema = (
componentName: string,
layoutSchema: any,
version: string,
) => {
const definitionName = `Comp${componentName}`;
const componentSchema = getComponentSchema(definitionName, layoutSchema, version);

let schema: any = {
$id: `https://altinncdn.no/schemas/json/component/${componentName}.schema.v1.json`,
$schema: layoutSchema.$schema,
};

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

if (!verifySchema(schema, expectedProperties)) {
return null;
}
ensureStringTypeWithEnums(expandedProperties[property]);
} else if (componentSchema.anyOf) {
schema.anyOf = expandAnyOf(componentSchema, layoutSchema);
}

return expandedProperties;
// 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 = `${componentName} component schema`;
return schema;
};
5 changes: 3 additions & 2 deletions frontend/scripts/componentSchemas/version.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ export const versionSettings = {
componentSchemaPath: '../../packages/ux-editor/src/testing/schemas/json/component',
},
};
export const isValidVersion = (version: string) =>
validVersions.includes(version as AppFrontendVersion);

export const validVersions = ['v3', 'v4'] as const;
export type AppFrontendVersion = (typeof validVersions)[number];
export const isValidVersion = (version: string) =>
validVersions.includes(version as AppFrontendVersion);

0 comments on commit 656843e

Please sign in to comment.