Skip to content

Commit

Permalink
feat: add new builders for metadata migration check (#1732)
Browse files Browse the repository at this point in the history
## Proposed change

<!-- Please include a summary of the changes and the related issue.
Please also include relevant motivation and context. List any
dependencies that is required for this change. -->

Add 3 new builders in @o3r/component, @o3r/styling and
@o3r/localization, check-config-migration-metadata,
check-localization-styling -metadata and
check-localization-migration-metadata
The goal of these builders is to retrieve a previous version of the app
(provided as an option or computed from the folder of migration
metadata) and compare the current metadata with the previous one. It
will raise some errors if breaking changes are detected and they are not
allowed, or if these changes are not documented in the provided
migration metadata.

## Related issues

- 🐛 Fixes #(issue)
- 🚀 Feature #(issue)

<!-- Please make sure to follow the contributing guidelines on
https://github.com/amadeus-digital/Otter/blob/main/CONTRIBUTING.md -->
  • Loading branch information
fpaul-1A authored Jul 19, 2024
2 parents f6941f6 + 255b3bf commit 07cb125
Show file tree
Hide file tree
Showing 51 changed files with 3,483 additions and 62 deletions.
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
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) => {
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';
import type { MetadataComparator } from '@o3r/extractors';

/**
* Interface describing a config migration element
*/
export interface MigrationConfigData {
/** Library name */
libraryName: string;
/**
* 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}` : '');

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

0 comments on commit 07cb125

Please sign in to comment.