From 535b62ab6a6d04c1f0a788acf5827e35684279fb Mon Sep 17 00:00:00 2001 From: Emile Rolley Date: Mon, 3 Jun 2024 10:37:09 +0200 Subject: [PATCH 1/4] refactor: use the new refactored migrateSituation from @publicodes/tools --- src/app/_components/MainLayoutProviders.tsx | 5 +- .../modelFetching/getMigrationInstructions.ts | 4 +- src/helpers/simulation/generateSimulation.ts | 10 +- .../helpers/migrateSimulation.ts | 41 +++--- .../helpers/migration/convertSimulation.ts | 27 ---- .../helpers/migration/filterSimulation.ts | 137 ------------------ .../hooks/useUser/useSimulations.ts | 10 +- .../providers/userProvider/context.ts | 5 +- .../providers/userProvider/index.tsx | 5 +- .../userProvider/usePersistentSimulations.ts | 5 +- src/publicodes-state/types.d.ts | 5 - tsconfig.json | 18 ++- 12 files changed, 54 insertions(+), 218 deletions(-) delete mode 100644 src/publicodes-state/helpers/migration/convertSimulation.ts delete mode 100644 src/publicodes-state/helpers/migration/filterSimulation.ts diff --git a/src/app/_components/MainLayoutProviders.tsx b/src/app/_components/MainLayoutProviders.tsx index beaaae13e..2ee71b21c 100644 --- a/src/app/_components/MainLayoutProviders.tsx +++ b/src/app/_components/MainLayoutProviders.tsx @@ -2,7 +2,8 @@ import { IframeOptionsProvider } from '@/app/_components/mainLayoutProviders/IframeOptionsContext' import { UserProvider } from '@/publicodes-state' -import { MigrationType, RegionFromGeolocation } from '@/publicodes-state/types' +import { RegionFromGeolocation } from '@/publicodes-state/types' +import { Migration } from '@publicodes/tools/migration' import { PropsWithChildren } from 'react' import MainHooks from './mainLayoutProviders/MainHooks' import { PreventNavigationProvider } from './mainLayoutProviders/PreventNavigationProvider' @@ -11,7 +12,7 @@ import SimulationSyncProvider from './mainLayoutProviders/SimulationSyncProvider type Props = { region: RegionFromGeolocation - migrationInstructions: MigrationType + migrationInstructions: Migration } export default function MainLayoutProviders({ children, diff --git a/src/helpers/modelFetching/getMigrationInstructions.ts b/src/helpers/modelFetching/getMigrationInstructions.ts index 1c2dae72c..fd1c5fd3f 100644 --- a/src/helpers/modelFetching/getMigrationInstructions.ts +++ b/src/helpers/modelFetching/getMigrationInstructions.ts @@ -1,5 +1,5 @@ -import { MigrationType } from '@/publicodes-state/types' import migration from '@incubateur-ademe/nosgestesclimat/public/migration.json' +import { Migration } from '@publicodes/tools/migration' import { importPreviewFile } from './importPreviewFile' type Props = { @@ -10,7 +10,7 @@ type Props = { */ export async function getMigrationInstructions({ PRNumber, -}: Props = {}): Promise { +}: Props = {}): Promise { if (PRNumber) { const fileName = `migration.json` return importPreviewFile({ fileName, PRNumber }) diff --git a/src/helpers/simulation/generateSimulation.ts b/src/helpers/simulation/generateSimulation.ts index 68e11805e..ca1b1627c 100644 --- a/src/helpers/simulation/generateSimulation.ts +++ b/src/helpers/simulation/generateSimulation.ts @@ -1,5 +1,6 @@ import { migrateSimulation } from '@/publicodes-state/helpers/migrateSimulation' -import { MigrationType, Simulation } from '@/publicodes-state/types' +import { Simulation } from '@/publicodes-state/types' +import { Migration } from '@publicodes/tools/migration' import { captureException } from '@sentry/react' import { v4 as uuidv4 } from 'uuid' @@ -18,7 +19,7 @@ export function generateSimulation({ savedViaEmail, migrationInstructions, }: Partial & { - migrationInstructions?: MigrationType + migrationInstructions?: Migration } = {}): Simulation { let simulation = { id, @@ -37,10 +38,7 @@ export function generateSimulation({ if (migrationInstructions) { try { - simulation = migrateSimulation({ - simulation, - migrationInstructions, - }) + simulation = migrateSimulation(simulation, migrationInstructions) } catch (error) { console.warn('Error trying to migrate LocalStorage:', error) captureException(error) diff --git a/src/publicodes-state/helpers/migrateSimulation.ts b/src/publicodes-state/helpers/migrateSimulation.ts index 1fce06308..5c7a80967 100644 --- a/src/publicodes-state/helpers/migrateSimulation.ts +++ b/src/publicodes-state/helpers/migrateSimulation.ts @@ -1,28 +1,23 @@ -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-ignore -import { migrateSituation } from '@publicodes/tools/migration' -import { MigrationType, Simulation } from '../types' +import { Migration, migrateSituation } from '@publicodes/tools/migration' +import { Simulation } from '../types' -type Props = { - simulation: Simulation - migrationInstructions: MigrationType -} -type Return = Simulation - -export function migrateSimulation({ - simulation: oldSimulation, - migrationInstructions, -}: Props): Return { - const simulation = JSON.parse(JSON.stringify(oldSimulation)) as Simulation - - const { situationMigrated, foldedStepsMigrated } = migrateSituation({ - situation: simulation.situation, - foldedSteps: simulation.foldedSteps, - migrationInstructions, - }) +export function migrateSimulation( + simulation: Simulation, + migrationInstructions: Migration +): Simulation { + simulation.situation = migrateSituation( + simulation.situation, + migrationInstructions + ) - simulation.situation = situationMigrated - simulation.foldedSteps = foldedStepsMigrated + // NOTE: folded steps (i.e. answered rules) are can be map to a situation, + // where the keys are the rule names and the value is null. + simulation.foldedSteps = Object.keys( + migrateSituation( + Object.fromEntries(simulation.foldedSteps.map((step) => [step, null])), + migrationInstructions + ) + ) return simulation } diff --git a/src/publicodes-state/helpers/migration/convertSimulation.ts b/src/publicodes-state/helpers/migration/convertSimulation.ts deleted file mode 100644 index 771616725..000000000 --- a/src/publicodes-state/helpers/migration/convertSimulation.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { Simulation } from '@/publicodes-state/types' - -type Props = { - simulation: any -} -type Return = Simulation - -export function convertSimulation({ simulation }: Props): Return { - // If the value inside the situation is an object {valeur: value}, we want to convert it to value - Object.keys(simulation.situation).map((key) => { - if (simulation.situation[key]?.valeur !== undefined) { - simulation.situation[key] = simulation.situation[key].valeur - } - }) - - // If group or poll is defined, we convert it to groups or polls and delete it - if (simulation.group) { - simulation.groups = [simulation.group] - delete simulation.group - } - if (simulation.poll) { - simulation.polls = [simulation.poll] - delete simulation.poll - } - - return simulation -} diff --git a/src/publicodes-state/helpers/migration/filterSimulation.ts b/src/publicodes-state/helpers/migration/filterSimulation.ts deleted file mode 100644 index ed646ee42..000000000 --- a/src/publicodes-state/helpers/migration/filterSimulation.ts +++ /dev/null @@ -1,137 +0,0 @@ -import { - DottedName, - MigrationType, - NodeValue, - Simulation, - Situation, -} from '@/publicodes-state/types' - -function handleMigrationKey({ - ruleName, - nodeValue, - situation, - foldedSteps, - migrationInstructions, -}: { - ruleName: DottedName - nodeValue: NodeValue - situation: Situation - foldedSteps: DottedName[] - migrationInstructions: MigrationType -}) { - if (!migrationInstructions.keysToMigrate[ruleName]) { - return - } - - // The key is not a key to migrate but a key to delete - if (migrationInstructions.keysToMigrate[ruleName] === '') { - delete situation[ruleName] - const index = foldedSteps.indexOf(ruleName) - - if (index > -1) { - foldedSteps.splice(index, 1) - } - return - } - - // The key is renamed and needs to be migrated - situation[migrationInstructions.keysToMigrate[ruleName]] = nodeValue - - delete situation[ruleName] - const index = foldedSteps.indexOf(ruleName) - - if (index > -1) { - foldedSteps[index] = migrationInstructions.keysToMigrate[ruleName] - } -} - -function handleMigrationValue({ - ruleName, - nodeValue, - situation, - foldedSteps, - migrationInstructions, -}: { - ruleName: DottedName - nodeValue: NodeValue - situation: Situation - foldedSteps: DottedName[] - migrationInstructions: MigrationType -}) { - if (!migrationInstructions.valuesToMigrate[ruleName]) { - return - } - - // The value is not a value to migrate and the key has to be deleted - if ( - migrationInstructions.valuesToMigrate[ruleName][nodeValue as string] === '' - ) { - delete situation[ruleName] - const index = foldedSteps.indexOf(ruleName) - if (index > -1) { - foldedSteps.splice(index, 1) - } - return - } - - // The value is renamed and needs to be migrated - situation[ruleName] = - typeof migrationInstructions.valuesToMigrate[ruleName][ - nodeValue as string - ] === 'string' && - migrationInstructions.valuesToMigrate[ruleName][nodeValue as string] !== - 'oui' && - migrationInstructions.valuesToMigrate[ruleName][nodeValue as string] !== - 'non' - ? `'${ - migrationInstructions.valuesToMigrate[ruleName][nodeValue as string] - }'` - : migrationInstructions.valuesToMigrate[ruleName][nodeValue as string] -} - -type Props = { - simulation: Simulation - migrationInstructions: MigrationType -} -export default function filterSimulationSituation({ - simulation, - migrationInstructions, -}: Props): Simulation { - const situation = simulation.situation - const foldedSteps = simulation.foldedSteps - - Object.entries(situation).map(([ruleName, nodeValue]) => { - // We check if the non supported ruleName is a key to migrate. - // Ex: "logement . chauffage . bois . type . bûche . consommation": "xxx" which is now ""logement . chauffage . bois . type . bûches . consommation": "xxx" - if (Object.keys(migrationInstructions.keysToMigrate).includes(ruleName)) { - handleMigrationKey({ - ruleName, - nodeValue, - situation, - foldedSteps, - migrationInstructions, - }) - } - - 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" - Object.keys(migrationInstructions.valuesToMigrate).includes(ruleName) && - Object.keys(migrationInstructions.valuesToMigrate[ruleName]).includes( - nodeValue as string - ) - ) { - handleMigrationValue({ - ruleName, - nodeValue, - situation, - foldedSteps, - migrationInstructions, - }) - } - }) - - return simulation -} diff --git a/src/publicodes-state/hooks/useUser/useSimulations.ts b/src/publicodes-state/hooks/useUser/useSimulations.ts index 81a4c8cef..82b705e77 100644 --- a/src/publicodes-state/hooks/useUser/useSimulations.ts +++ b/src/publicodes-state/hooks/useUser/useSimulations.ts @@ -1,20 +1,18 @@ 'use client' import { generateSimulation } from '@/helpers/simulation/generateSimulation' +import { Migration } from '@publicodes/tools/migration' import { Dispatch, SetStateAction, useCallback, useMemo } from 'react' -import { - MigrationType, - Simulation, - UpdateCurrentSimulationProps, -} from '../../types' +import { Simulation, UpdateCurrentSimulationProps } from '../../types' type Props = { simulations: Simulation[] setSimulations: Dispatch> currentSimulationId: string setCurrentSimulationId: Dispatch> - migrationInstructions: MigrationType + migrationInstructions: Migration } + export default function useSimulations({ simulations, setSimulations, diff --git a/src/publicodes-state/providers/userProvider/context.ts b/src/publicodes-state/providers/userProvider/context.ts index 235c0817b..a3a75f927 100644 --- a/src/publicodes-state/providers/userProvider/context.ts +++ b/src/publicodes-state/providers/userProvider/context.ts @@ -1,8 +1,9 @@ 'use client' +import { Migration } from '@publicodes/tools/migration' import { Dispatch, SetStateAction, createContext } from 'react' import { v4 as uuid } from 'uuid' -import { MigrationType, Simulation, Tutorials, User } from '../../types' +import { Simulation, Tutorials, User } from '../../types' type UserContextType = { user: User @@ -13,7 +14,7 @@ type UserContextType = { setSimulations: Dispatch> currentSimulationId: string setCurrentSimulationId: Dispatch> - migrationInstructions: MigrationType + migrationInstructions: Migration } export default createContext({ diff --git a/src/publicodes-state/providers/userProvider/index.tsx b/src/publicodes-state/providers/userProvider/index.tsx index 4214578bb..94b6a463a 100644 --- a/src/publicodes-state/providers/userProvider/index.tsx +++ b/src/publicodes-state/providers/userProvider/index.tsx @@ -2,7 +2,8 @@ import { PropsWithChildren } from 'react' -import { MigrationType, RegionFromGeolocation } from '@/publicodes-state/types' +import { RegionFromGeolocation } from '@/publicodes-state/types' +import { Migration } from '@publicodes/tools/migration' import UserContext from './context' import useUpdateOldLocalStorage from './useOldLocalStorage' import usePersistentSimulations from './usePersistentSimulations' @@ -21,7 +22,7 @@ type Props = { /** * The migration instructions for old localstorage */ - migrationInstructions: MigrationType + migrationInstructions: Migration } export default function UserProvider({ children, diff --git a/src/publicodes-state/providers/userProvider/usePersistentSimulations.ts b/src/publicodes-state/providers/userProvider/usePersistentSimulations.ts index ae198ec84..c1ef423a0 100644 --- a/src/publicodes-state/providers/userProvider/usePersistentSimulations.ts +++ b/src/publicodes-state/providers/userProvider/usePersistentSimulations.ts @@ -1,13 +1,14 @@ import { generateSimulation } from '@/helpers/simulation/generateSimulation' import { getIsLocalStorageAvailable } from '@/utils/getIsLocalStorageAvailable' +import { Migration } from '@publicodes/tools/migration' import { useEffect, useState } from 'react' -import { MigrationType, Simulation } from '../../types' +import { Simulation } from '../../types' const isLocalStorageAvailable = getIsLocalStorageAvailable() type Props = { storageKey: string - migrationInstructions: MigrationType + migrationInstructions: Migration } export default function usePersistentSimulations({ storageKey, diff --git a/src/publicodes-state/types.d.ts b/src/publicodes-state/types.d.ts index 70f6fe4d3..3a4857e06 100644 --- a/src/publicodes-state/types.d.ts +++ b/src/publicodes-state/types.d.ts @@ -134,8 +134,3 @@ type MosaicInfos = { } type Formule = any - -export type MigrationType = { - keysToMigrate: Record - valuesToMigrate: Record> -} diff --git a/tsconfig.json b/tsconfig.json index 6016183d9..d40aab7e6 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,14 +1,18 @@ { "compilerOptions": { "target": "ES6", - "lib": ["dom", "dom.iterable", "esnext"], + "lib": [ + "dom", + "dom.iterable", + "esnext" + ], "allowJs": true, "skipLibCheck": true, "strict": true, "noEmit": true, "esModuleInterop": true, "module": "esnext", - "moduleResolution": "node", + "moduleResolution": "bundler", "resolveJsonModule": true, "isolatedModules": true, "jsx": "preserve", @@ -19,7 +23,9 @@ } ], "paths": { - "@/*": ["./src/*"] + "@/*": [ + "./src/*" + ] }, "forceConsistentCasingInFileNames": true }, @@ -32,5 +38,9 @@ "src/app/(layout-with-navigation)/statistiques/_components/StatsContent.js", "src/app/(layout-with-navigation)/statistiques/_components/content/Chart.js" ], - "exclude": ["node_modules", "cypress/**/*", "scripts/**/*"] + "exclude": [ + "node_modules", + "cypress/**/*", + "scripts/**/*" + ] } From a7839c7252c8f5924034f4cf0ec2a1f61b332bf0 Mon Sep 17 00:00:00 2001 From: Emile Rolley Date: Mon, 3 Jun 2024 17:37:37 +0200 Subject: [PATCH 2/4] fix: use the publicodes Situation type --- src/publicodes-state/helpers/migrateSimulation.ts | 6 ++++-- src/publicodes-state/types.d.ts | 3 ++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/publicodes-state/helpers/migrateSimulation.ts b/src/publicodes-state/helpers/migrateSimulation.ts index 5c7a80967..db1d5c238 100644 --- a/src/publicodes-state/helpers/migrateSimulation.ts +++ b/src/publicodes-state/helpers/migrateSimulation.ts @@ -11,10 +11,12 @@ export function migrateSimulation( ) // NOTE: folded steps (i.e. answered rules) are can be map to a situation, - // where the keys are the rule names and the value is null. + // where the keys are the rule names and the value is undefined. simulation.foldedSteps = Object.keys( migrateSituation( - Object.fromEntries(simulation.foldedSteps.map((step) => [step, null])), + Object.fromEntries( + simulation.foldedSteps.map((step) => [step, undefined]) + ), migrationInstructions ) ) diff --git a/src/publicodes-state/types.d.ts b/src/publicodes-state/types.d.ts index 3a4857e06..6e869b83b 100644 --- a/src/publicodes-state/types.d.ts +++ b/src/publicodes-state/types.d.ts @@ -4,6 +4,7 @@ import { EvaluatedNode, Evaluation, Engine as PublicodesEngine, + Situation as PublicodesSituation, RuleNode, } from 'publicodes' @@ -36,7 +37,7 @@ export type Rules = any export type Tutorials = Record -export type Situation = Record +export type Situation = PublicodesSituation export type Suggestion = { label: string From 96a8cd7aa712ff69437136724c4d829580236128 Mon Sep 17 00:00:00 2001 From: Emile Rolley Date: Mon, 3 Jun 2024 17:47:41 +0200 Subject: [PATCH 3/4] pkg: upgrade @publicodes/tools --- package.json | 2 +- yarn.lock | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index 60011d46e..6ff4d911c 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "@next/bundle-analyzer": "^14.1.0", "@next/mdx": "^14.0.2", "@publicodes/react-ui": "^1.2.0", - "@publicodes/tools": "^1.1.2", + "@publicodes/tools": "^1.2.0", "@sentry/nextjs": "^7.80.0", "@sentry/react": "^7.80.0", "@tanstack/react-query": "^5.28.4", diff --git a/yarn.lock b/yarn.lock index 39e1ee268..543dabe36 100644 --- a/yarn.lock +++ b/yarn.lock @@ -813,13 +813,13 @@ fuse.js "^7.0.0" styled-components "^6.1.1" -"@publicodes/tools@^1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@publicodes/tools/-/tools-1.1.2.tgz#ca3bc707b4be37b63cd1cd1460428d27eeda8368" - integrity sha512-MVbleFn2WFzuojygXU2Sjx2W48RK46DVGKRfCSuiKhbMNTYFp1IUoPD+TuC07/nA6R+igKq0HoTrooRm0ORkSQ== +"@publicodes/tools@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@publicodes/tools/-/tools-1.2.0.tgz#7cf6315f5ac6d5e4c96e03e7a7dbccc0bcced72b" + integrity sha512-QkqkFSWXG//BEz+2nKx973uWgpzkf4iH4cGjYnGzMzR157IUd4z8+U8nofBUa1FqlJXZij3omvv60H9xS6nUQA== dependencies: "@types/node" "^18.11.18" - publicodes "^1.1.1" + publicodes "^1.3.0" "@rollup/plugin-commonjs@24.0.0": version "24.0.0" @@ -6243,10 +6243,10 @@ publicodes@1.1.1: resolved "https://registry.yarnpkg.com/publicodes/-/publicodes-1.1.1.tgz#6bd283b3a0e7e41d5d3ab2454d639c27a46400e5" integrity sha512-PO5nwianBt3D3xfVgfb6thJ1WT/ZE44dw6PvdyK/yBZSi69kh03tc7TyBrkVZcfsC00kZTfnJFNiVvJSTySYCA== -publicodes@^1.1.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/publicodes/-/publicodes-1.2.0.tgz#2856891da07633315eff419402bb61109045504d" - integrity sha512-qit3KsTqwZct5Xt6uyJ83yXNXxxScKuSRvemUgCRWEczP/mPAX/tiDQUgwwOC2fDoiCuQuZ9EiQwU5HfzHFIJQ== +publicodes@^1.3.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/publicodes/-/publicodes-1.3.1.tgz#b30e5e82cdfafd38c00fb6432ceac66f4bff5f58" + integrity sha512-By464NDaCc3fwRgq6BDMqoyYiapcRCBiFuQsF1ctazOoxtJ3SVXb0AVsIHMPqKFFj5elhD/8IljF7iqQqDDZxA== pump@^3.0.0: version "3.0.0" From 7cf5757b9cbfbdde98859fa6fe38d27708ca3dff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment?= Date: Tue, 11 Jun 2024 09:04:22 +0200 Subject: [PATCH 4/4] fix: also convert simulation poll and group attr --- src/helpers/simulation/generateSimulation.ts | 12 +++--- .../helpers/migrateSimulation.ts | 42 ++++++++++++------- 2 files changed, 32 insertions(+), 22 deletions(-) diff --git a/src/helpers/simulation/generateSimulation.ts b/src/helpers/simulation/generateSimulation.ts index ca1b1627c..b240a92b9 100644 --- a/src/helpers/simulation/generateSimulation.ts +++ b/src/helpers/simulation/generateSimulation.ts @@ -36,13 +36,11 @@ export function generateSimulation({ savedViaEmail, } as Simulation - if (migrationInstructions) { - try { - simulation = migrateSimulation(simulation, migrationInstructions) - } catch (error) { - console.warn('Error trying to migrate LocalStorage:', error) - captureException(error) - } + try { + simulation = migrateSimulation(simulation, migrationInstructions) + } catch (error) { + console.warn('Error trying to migrate LocalStorage:', error) + captureException(error) } return simulation diff --git a/src/publicodes-state/helpers/migrateSimulation.ts b/src/publicodes-state/helpers/migrateSimulation.ts index db1d5c238..30954ec81 100644 --- a/src/publicodes-state/helpers/migrateSimulation.ts +++ b/src/publicodes-state/helpers/migrateSimulation.ts @@ -2,24 +2,36 @@ import { Migration, migrateSituation } from '@publicodes/tools/migration' import { Simulation } from '../types' export function migrateSimulation( - simulation: Simulation, - migrationInstructions: Migration + simulation: Simulation & { group?: string; poll?: string }, + migrationInstructions: Migration | undefined ): Simulation { - simulation.situation = migrateSituation( - simulation.situation, - migrationInstructions - ) - - // NOTE: folded steps (i.e. answered rules) are can be map to a situation, - // where the keys are the rule names and the value is undefined. - simulation.foldedSteps = Object.keys( - migrateSituation( - Object.fromEntries( - simulation.foldedSteps.map((step) => [step, undefined]) - ), + if (migrationInstructions) { + simulation.situation = migrateSituation( + simulation.situation, migrationInstructions ) - ) + + // NOTE: folded steps (i.e. answered rules) are can be map to a situation, + // where the keys are the rule names and the value is undefined. + simulation.foldedSteps = Object.keys( + migrateSituation( + Object.fromEntries( + simulation.foldedSteps.map((step) => [step, undefined]) + ), + migrationInstructions + ) + ) + } + // If group or poll is defined, we convert it to groups or polls and delete it + if (simulation.group) { + simulation.groups = [simulation.group] + delete simulation.group + } + + if (simulation.poll) { + simulation.polls = [simulation.poll] + delete simulation.poll + } return simulation }