diff --git a/src/migration/index.ts b/src/migration/index.ts index 0ee6d57..d52f626 100644 --- a/src/migration/index.ts +++ b/src/migration/index.ts @@ -12,7 +12,7 @@ This is where the sitation migration comes in. ### Usage -{@link migrateSituation | `migrateSituation`} allows to migrate a siuation from +{@link migrateSituation | `migrateSituation`} allows to migrate a situation from an old version of a model to a new version according to the provided _migration instructions_. @@ -20,27 +20,28 @@ instructions_. ```typescript import { migrateSituation } from '@publicodes/tools/migration' -const oldSituation = { - "age": 25 +const situation = { + "age": 25, "job": "developer", + "city": "Paris" } -// In the new model version, the rule `age` has been renamed to `âge` and the -// value `developer` has been translated to `développeur`. -const migrationInstructions = { - keysToMigrate: { age: 'âge' } +const instructions = { + keysToMigrate: { + // The rule `age` has been renamed to `âge`. + age: 'âge', + // The rule `city` has been removed. + city: '' + }, valuesToMigrate: { - job: { developer: 'développeur' } + job: { + // The value `developer` has been translated to `développeur`. + developer: 'développeur' + } } } -console.log(migrateSituation(oldSituation, migrationInstructions)) - -// Output: -// { -// "âge": 25, -// "job": "développeur" -// } +migrateSituation(situation, instructions) // { "âge": 25, "job": "'développeur'" } ``` */ diff --git a/src/migration/migrateSituation.ts b/src/migration/migrateSituation.ts index 8f76d77..ed7031b 100644 --- a/src/migration/migrateSituation.ts +++ b/src/migration/migrateSituation.ts @@ -15,7 +15,7 @@ export type ValueMigration = Record * Migration instructions. It contains the rules and values to migrate. */ export type Migration = { - rulesToMigrate: Record + keysToMigrate: Record valuesToMigrate: Record } @@ -28,9 +28,35 @@ export type Migration = { * * @returns The migrated situation (and foldedSteps if specified). * - * TODO: exemple of instructions (empty string for deletion, new key name for renaming, new value for updating) + * @example + * ```typescript + * import { migrateSituation } from '@publicodes/tools/migration' + * + * const situation = { + * "age": 25 + * "job": "developer", + * "city": "Paris" + * } + * + * const instructions = { + * keysToMigrate: { + * // The rule `age` will be renamed to `âge`. + * age: 'âge', + * // The rule `city` will be removed. + * city: '' + * }, + * valuesToMigrate: { + * job: { + * // The value `developer` will be translated to `développeur`. + * developer: 'développeur' + * } + * } + * } + * + * migrateSituation(situation, instructions) // { "âge": 25, "job": "'développeur'" } + * ``` * - * An example of instructions can be found {@link https://github.com/incubateur-ademe/nosgestesclimat/blob/preprod/migration/migration.yaml | here}. + * @note An example of instructions can be found {@link https://github.com/incubateur-ademe/nosgestesclimat/blob/preprod/migration/migration.yaml | here}. */ export function migrateSituation( situation: Situation, @@ -44,7 +70,7 @@ export function migrateSituation( handleSpecialCases(rule, value, newSituation) if (currentRules.includes(rule)) { - updateKey(rule, value, newSituation, instructions.rulesToMigrate[rule]) + updateKey(rule, value, newSituation, instructions.keysToMigrate[rule]) } const formattedValue = getValueWithoutQuotes(value) ?? (value as string) @@ -54,13 +80,7 @@ export function migrateSituation( ] ?? {} const oldValuesName = Object.keys(valuesMigration) - if ( - // We check if the value of the non supported ruleName value is a value to migrate. - // Ex: answer "logement . chauffage . bois . type": "bûche" changed to "bûches" - // If a value is specified but empty, we consider it to be deleted (we need to ask the question again) - // Ex: answer "transport . boulot . commun . type": "vélo" - oldValuesName.includes(formattedValue) - ) { + if (oldValuesName.includes(formattedValue)) { updateValue(rule, valuesMigration[formattedValue], newSituation) } }) @@ -68,19 +88,13 @@ export function migrateSituation( return newSituation } -// Handle migration of old value format : an object { valeur: number, unité: string } /** - * Handles special cases during the migration of old value formats. + * Handle migration of old value format : an object { valeur: number, unité: string }. * * @example - * ```` -{ valeur: number, unité: string } -``` - * - * @param rule - The name of the rule. - * @param oldValue - The node value. - * @param situation - The situation object. - * @returns - The updated situation object. + * ```json + * { valeur: number, unité: string } + * ``` */ function handleSpecialCases( rule: RuleName, @@ -116,8 +130,6 @@ function handleSpecialCases( } } -/** - */ function updateKey( rule: RuleName, oldValue: Evaluation, @@ -136,9 +148,7 @@ function updateKey( } } -/** - */ -export function updateValue( +function updateValue( rule: RuleName, value: string, situation: Situation, @@ -148,31 +158,9 @@ export function updateValue( delete situation[rule] } else { // The value is renamed and needs to be migrated - situation[rule] = getMigratedValue(value) - } -} - -function getMigratedValue(value: string): string { - if (typeof value === 'string' && value !== 'oui' && value !== 'non') { - return `'${value}'` + situation[rule] = + typeof value === 'string' && value !== 'oui' && value !== 'non' + ? `'${value}'` + : value } - - // FIXME: I'm not sure if it's necessary to check if the value is a number, - // as valuesToMigrate is a ValueMigration object (Record). - // Is it possible to have objects in valuesToMigrate? - // if ( - // ( - // value as unknown as { - // valeur: number - // } - // )?.valeur !== undefined - // ) { - // return ( - // value as unknown as { - // valeur: number - // } - // ).valeur as unknown as string - // } - - return value } diff --git a/test/migration/migrateSituation.test.ts b/test/migration/migrateSituation.test.ts index b028ac5..97abb24 100644 --- a/test/migration/migrateSituation.test.ts +++ b/test/migration/migrateSituation.test.ts @@ -5,7 +5,7 @@ import { } from '../../src/migration/migrateSituation' const instructions: Migration = { - rulesToMigrate: { age: 'âge', 'année de naissance': '' }, + keysToMigrate: { age: 'âge', 'année de naissance': '' }, valuesToMigrate: { prénom: { jean: 'Jean avec un J', michel: '' } }, } @@ -15,47 +15,103 @@ const migrateSituationWithInstructions = (situation: Situation) => describe('migrateSituation', () => { it('should migrate key', () => { expect(migrateSituationWithInstructions({ age: 27 })).toEqual({ âge: 27 }) - }), - it('should migrate value', () => { - expect(migrateSituationWithInstructions({ prénom: 'jean' })).toEqual({ - prénom: "'Jean avec un J'", - }) - }), - it('should delete key', () => { - expect( - migrateSituationWithInstructions({ 'année de naissance': 1997 }), - ).toEqual({}) - }), - it('should delete value', () => { - expect( - migrateSituationWithInstructions({ - prénom: 'michel', - }), - ).toEqual({}) - }), - it('should support old situations (1)', () => { - expect( - migrateSituationWithInstructions({ - âge: { valeur: 27, unité: 'an' }, - }), - ).toEqual({ - âge: 27, - }) - }), - it('should support old situations (2)', () => { - expect( - migrateSituationWithInstructions({ - âge: { - type: 'number', - fullPrecision: true, - isNullable: false, - nodeValue: 27, - nodeKind: 'constant', - rawNode: 27, - }, - }), - ).toEqual({ - âge: 27, - }) + }) + + it('should migrate value', () => { + expect(migrateSituationWithInstructions({ prénom: 'jean' })).toEqual({ + prénom: "'Jean avec un J'", + }) + }) + + it('should delete key', () => { + expect( + migrateSituationWithInstructions({ 'année de naissance': 1997 }), + ).toEqual({}) + }) + + it('should delete value', () => { + expect( + migrateSituationWithInstructions({ + prénom: 'michel', + }), + ).toEqual({}) + }) + + it('should support old situations (1)', () => { + expect( + migrateSituationWithInstructions({ + âge: { valeur: 27, unité: 'an' }, + }), + ).toEqual({ + âge: 27, + }) + }) + + it('should support old situations (2)', () => { + expect( + migrateSituationWithInstructions({ + âge: { + type: 'number', + fullPrecision: true, + isNullable: false, + nodeValue: 27, + nodeKind: 'constant', + rawNode: 27, + }, + }), + ).toEqual({ + âge: 27, + }) + }) + + it('should migrate the API example', () => { + const oldSituation = { + age: 25, + job: 'developer', + city: 'Paris', + } + + const instructions = { + keysToMigrate: { + age: 'âge', + city: '', + }, + valuesToMigrate: { + job: { + developer: 'développeur', + }, + }, + } + expect(migrateSituation(oldSituation, instructions)).toEqual({ + âge: 25, + job: "'développeur'", + }) + }) + + it('should not modify the original situation', () => { + const situation = { + job: 'developer', + âge: { + type: 'number', + fullPrecision: true, + isNullable: false, + nodeValue: 27, + nodeKind: 'constant', + rawNode: 27, + }, + } + + migrateSituation(situation, instructions) + expect(situation).toEqual({ + âge: { + type: 'number', + fullPrecision: true, + isNullable: false, + nodeValue: 27, + nodeKind: 'constant', + rawNode: 27, + }, + job: 'developer', }) + }) })