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

feat: add new builders for metadata migration check #1732

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/it-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ jobs:
npx --yes verdaccio --config $GITHUB_WORKSPACE\\.verdaccio\\conf\\config-without-docker.yaml --listen http://127.0.0.1:4873 &
npx --yes wait-on http://127.0.0.1:4873 -t 180000
fi
yarn verdaccio:login
matthieu-crouzet marked this conversation as resolved.
Show resolved Hide resolved
shell: bash
- name: Test
id: it-tests
Expand Down
2 changes: 1 addition & 1 deletion .verdaccio/conf/.npmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
registry=http://127.0.0.1:4873
registry=http://127.0.0.1:4873/
118 changes: 116 additions & 2 deletions docs/cms-adapters/CMS_ADAPTERS.md
Original file line number Diff line number Diff line change
Expand Up @@ -163,8 +163,6 @@ __Note:__ The duplicate CSS Variable will be specified as warning and overridden

### Rules engine extractor

### Metadata

As for the other metadata retrieved, a bit of configuration is needed in order to extract metadata for facts and operators in rules engine scope.

#### How to install
Expand Down Expand Up @@ -225,3 +223,119 @@ Example:
...
}
```

## How to check for breaking changes on metadata

Version after version, it can be verified whether any breaking changes have been introduced, or if there is any metadata provided to document it.
To achieve this, simply configure the following builder in your `angular.json` file as shown below.

```json5
{
// ...,
"projects": {
// ...,
"<project-name>": {
// ...,
"architect": {
"check-config-migration-metadata": {
"builder": "@o3r/components:check-config-migration-metadata",
"options": {
"migrationDataPath": "./migration-scripts/MIGRATION-*.json", // Required
"granularity": "major", // Default value is minor
"allowBreakingChanges": true, // Default value is false
"packageManager": "npm", // If not provided, it will be determined based on the repository architecture
"metadataPath": "./component.config.metadata.json" // Default value
}
},
"check-style-migration-metadata": {
"builder": "@o3r/styling:check-style-migration-metadata",
"options": {
"migrationDataPath": "./migration-scripts/MIGRATION-*.json" // Required
}
},
"check-localization-migration-metadata": {
"builder": "@o3r/localization:check-localization-migration-metadata",
"options": {
"migrationDataPath": "./migration-scripts/MIGRATION-*.json" // Required
}
}
}
}
}
}
```

Example of migration file:
```json5
{
"$schema": "https://raw.githubusercontent.com/AmadeusITGroup/otter/main/packages/@o3r/extractors/schemas/migration.metadata.schema.json",
"version": "10.0.0",
"changes": [
{ // Move property to a new library and to a new config and rename property name
'contentType': 'CONFIG',
'before': {
'libraryName': '@old/lib',
'configName': 'OldConfig',
'propertyName': 'oldName'
},
'after': {
'libraryName': '@new/lib',
'configName': 'NewConfig',
'propertyName': 'newName'
}
},
{ // Rename configuration name for all properties
'contentType': 'CONFIG',
'before': {
'libraryName': '@o3r/lib',
'configName': 'OldConfig'
},
'after': {
'libraryName': '@o3r/lib',
'configName': 'NewConfig'
}
},
{ // Rename library name for all configurations
'contentType': 'CONFIG',
'before': {
'libraryName': '@o3r/lib2'
},
'after': {
'libraryName': '@o3r/lib3'
}
},
{ // Rename localization key
'contentType': 'LOCALIZATION',
'before': {
'key': 'old-localization.key'
},
'after': {
'key': 'new-localization.key'
}
},
{ // Rename CSS variable
'contentType': 'STYLE',
'before': {
'name': 'old-css-var-name'
},
'after': {
'name': 'new-css-var-name'
}
}
]
}
```

These migrations files are also useful in the CMS to automate the migration of the database associated to the metadata.

Make sure to expose them in the bundled application by adding them in the `files` field of your `package.json` and copy the files in the build process if needed

```json5
{
"files": [
"./migration-scripts/"
]
}
```

Also make sure to place them in a folder name `migration-scripts` in your packaged app or to set the `migrationScriptFolder` in your `cms.json`.
5 changes: 3 additions & 2 deletions jest.config.it.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ const getJestProjectConfig = require('./jest.config.ut').getJestProjectConfig;

/**
* @param rootDir {string}
* @param options.tsconfig {string}
* @returns {import('ts-jest/dist/types').JestConfigWithTsJest}
*/
module.exports.getJestConfig = (rootDir) => ({
...getJestProjectConfig(rootDir, false),
module.exports.getJestConfig = (rootDir, options) => ({
...getJestProjectConfig(rootDir, false, options),
rootDir: '..',
setupFilesAfterEnv: null,
testPathIgnorePatterns: [
Expand Down
7 changes: 4 additions & 3 deletions jest.config.ut.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ globalThis.ngJest = {
* Jest configuration that can be set at project level
* @param rootDir {string}
* @param isAngularSetup {boolean}
* @param options.tsconfig {string}
* @returns {import('ts-jest/dist/types').JestConfigWithTsJest}
*/
module.exports.getJestProjectConfig = (rootDir, isAngularSetup) => {
module.exports.getJestProjectConfig = (rootDir, isAngularSetup, options) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpicking:
isAngularSetup could be included in options
maybe rootdir too .

const relativePath = relative(rootDir, __dirname);
const moduleNameMapper = Object.fromEntries(
Object.entries(pathsToModuleNameMapper(compilerOptions.paths))
Expand All @@ -39,7 +40,7 @@ module.exports.getJestProjectConfig = (rootDir, isAngularSetup) => {
'^.+\\.[mc]?tsx?$': [
'ts-jest',
{
tsconfig: '<rootDir>/tsconfig.spec.json',
tsconfig: options?.tsconfig ?? '<rootDir>/tsconfig.spec.json',
stringifyContentPathRegex: '\\.html$'
}
]
Expand All @@ -56,7 +57,7 @@ module.exports.getJestProjectConfig = (rootDir, isAngularSetup) => {
'^.+\\.tsx?$': [
'jest-preset-angular',
{
tsconfig: '<rootDir>/tsconfig.spec.json',
tsconfig: options?.tsconfig ?? '<rootDir>/tsconfig.spec.json',
stringifyContentPathRegex: '\\.html$'
}
]
Expand Down
4 changes: 4 additions & 0 deletions packages/@o3r/application/schemas/cms.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@
"functionalContentsFolder": {
"type": "string",
"description": "The relative path to the functional contents metadata folder to import"
},
"migrationScriptFolder": {
"type": "string",
"description": "The relative path to the migration scripts"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export function updateCmsJsonFile(): Rule {
});

Object.entries(filesToUpdate).forEach(([path, contentObj]) => {
contentObj.$schema = 'https://github.com/AmadeusITGroup/otter/blob/main/packages/@o3r/application/schemas/cms.json';
contentObj.$schema = 'https://github.com/AmadeusITGroup/otter/blob/main/packages/@o3r/application/schemas/cms.schema.json';
tree.overwrite(path, JSON.stringify(contentObj, null, 2));
});
return tree;
Expand Down
5 changes: 5 additions & 0 deletions packages/@o3r/components/builders.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@
"implementation": "./builders/component-extractor/",
"schema": "./builders/component-extractor/schema.json",
"description": "Extract the component metadata (configuration and class) from an Otter project"
},
"check-config-migration-metadata": {
"implementation": "./builders/metadata-check/",
"schema": "./builders/metadata-check/schema.json",
"description": "Check for component metadata breaking changes"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import type { ComponentConfigOutput } from '@o3r/components';
matthieu-crouzet marked this conversation as resolved.
Show resolved Hide resolved
import type { MetadataComparator } from '@o3r/extractors';

/**
* Interface describing a config migration element
*/
export interface MigrationConfigData {
/** Library name */
libraryName: string;
matthieu-crouzet marked this conversation as resolved.
Show resolved Hide resolved
/**
* Configuration name
*/
configName?: string;
/**
* Configuration property name
*/
propertyName?: string;
}

/**
* Returns an array of config metadata from a metadata file.
* To be easily parseable, the properties will be split in separate items of the array.
* @param content Content of a migration metadata files
* @example Array conversion
* ```javascript
* [{ library: '@o3r/demo', properties: [{name : 'property1', type: 'string'}, {name : 'property2', type: 'number'}] }]
* ```
* will become :
* ```javascript
* [{ library: '@o3r/demo', properties: [{name : 'property1', type: 'string'}] }, { library: '@o3r/demo', properties: [{name : 'property2', type: 'number'}] }]
* ```
*/
const getConfigurationArray = (content: ComponentConfigOutput[]): ComponentConfigOutput[] => content.flatMap((config) =>
config.properties.length > 1
? config.properties.map((prop) => ({...config, properties: [prop]}))
: [config]
);

const getConfigurationPropertyName = (config: ComponentConfigOutput) => `${config.library}#${config.name}` + (config.properties.length ? ` ${config.properties[0].name}` : '');
matthieu-crouzet marked this conversation as resolved.
Show resolved Hide resolved
fpaul-1A marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing comment (not needed but provided in getConfigurationArray)


const isMigrationConfigurationDataMatch = (config: ComponentConfigOutput, migrationData: MigrationConfigData) =>
migrationData.libraryName === config.library
&& (!migrationData.configName || migrationData.configName === config.name)
&& (!migrationData.propertyName || config.properties[0]?.name === migrationData.propertyName);

/**
* Comparator used to compare one version of config metadata with another
*/
export const configMetadataComparator: MetadataComparator<ComponentConfigOutput, MigrationConfigData, ComponentConfigOutput[]> = {
getArray: getConfigurationArray,
getIdentifier: getConfigurationPropertyName,
isMigrationDataMatch: isMigrationConfigurationDataMatch
};
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './config-metadata-comparison.helper';
Loading
Loading