[PM-4345] Ps/provide migration helpers for key definitions (#7050)
* Provide helpers to build keys from KeyDefinitions * Move usage comments to the helper class * `npm run prettier` 🤖 * Prefer setters and getters to key builders * Add documentation to migration helper methods * Fix migration helper tests * Prefer defined types to ad hoc * `npm run prettier` 🤖
This commit is contained in:
parent
ac899bebeb
commit
09d626bb4b
|
@ -18,6 +18,8 @@ const exampleJSON = {
|
|||
"23e61a5f-2ece-4f5e-b499-f0bc489482a9": {
|
||||
otherStuff: "otherStuff2",
|
||||
},
|
||||
global_serviceName_key: "global_serviceName_key",
|
||||
user_userId_serviceName_key: "user_userId_serviceName_key",
|
||||
};
|
||||
|
||||
describe("RemoveLegacyEtmKeyMigrator", () => {
|
||||
|
@ -64,6 +66,79 @@ describe("RemoveLegacyEtmKeyMigrator", () => {
|
|||
expect(accounts).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("getFromGlobal", () => {
|
||||
it("should return the correct value", async () => {
|
||||
sut.currentVersion = 10;
|
||||
const value = await sut.getFromGlobal({
|
||||
stateDefinition: { name: "serviceName" },
|
||||
key: "key",
|
||||
});
|
||||
expect(value).toEqual("global_serviceName_key");
|
||||
});
|
||||
|
||||
it("should throw if the current version is less than 10", () => {
|
||||
expect(() =>
|
||||
sut.getFromGlobal({ stateDefinition: { name: "serviceName" }, key: "key" }),
|
||||
).toThrowError("No key builder should be used for versions prior to 10.");
|
||||
});
|
||||
});
|
||||
|
||||
describe("setToGlobal", () => {
|
||||
it("should set the correct value", async () => {
|
||||
sut.currentVersion = 10;
|
||||
await sut.setToGlobal({ stateDefinition: { name: "serviceName" }, key: "key" }, "new_value");
|
||||
expect(storage.save).toHaveBeenCalledWith("global_serviceName_key", "new_value");
|
||||
});
|
||||
|
||||
it("should throw if the current version is less than 10", () => {
|
||||
expect(() =>
|
||||
sut.setToGlobal(
|
||||
{ stateDefinition: { name: "serviceName" }, key: "key" },
|
||||
"global_serviceName_key",
|
||||
),
|
||||
).toThrowError("No key builder should be used for versions prior to 10.");
|
||||
});
|
||||
});
|
||||
|
||||
describe("getFromUser", () => {
|
||||
it("should return the correct value", async () => {
|
||||
sut.currentVersion = 10;
|
||||
const value = await sut.getFromUser("userId", {
|
||||
stateDefinition: { name: "serviceName" },
|
||||
key: "key",
|
||||
});
|
||||
expect(value).toEqual("user_userId_serviceName_key");
|
||||
});
|
||||
|
||||
it("should throw if the current version is less than 10", () => {
|
||||
expect(() =>
|
||||
sut.getFromUser("userId", { stateDefinition: { name: "serviceName" }, key: "key" }),
|
||||
).toThrowError("No key builder should be used for versions prior to 10.");
|
||||
});
|
||||
});
|
||||
|
||||
describe("setToUser", () => {
|
||||
it("should set the correct value", async () => {
|
||||
sut.currentVersion = 10;
|
||||
await sut.setToUser(
|
||||
"userId",
|
||||
{ stateDefinition: { name: "serviceName" }, key: "key" },
|
||||
"new_value",
|
||||
);
|
||||
expect(storage.save).toHaveBeenCalledWith("user_userId_serviceName_key", "new_value");
|
||||
});
|
||||
|
||||
it("should throw if the current version is less than 10", () => {
|
||||
expect(() =>
|
||||
sut.setToUser(
|
||||
"userId",
|
||||
{ stateDefinition: { name: "serviceName" }, key: "key" },
|
||||
"new_value",
|
||||
),
|
||||
).toThrowError("No key builder should be used for versions prior to 10.");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
/** Helper to create well-mocked migration helpers in migration tests */
|
||||
|
|
|
@ -3,6 +3,12 @@ import { LogService } from "../platform/abstractions/log.service";
|
|||
// eslint-disable-next-line import/no-restricted-paths -- Needed to interface with storage locations
|
||||
import { AbstractStorageService } from "../platform/abstractions/storage.service";
|
||||
|
||||
export type StateDefinitionLike = { name: string };
|
||||
export type KeyDefinitionLike = {
|
||||
stateDefinition: StateDefinitionLike;
|
||||
key: string;
|
||||
};
|
||||
|
||||
export class MigrationHelper {
|
||||
constructor(
|
||||
public currentVersion: number,
|
||||
|
@ -10,19 +16,93 @@ export class MigrationHelper {
|
|||
public logService: LogService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Gets a value from the storage service at the given key.
|
||||
*
|
||||
* This is a brute force method to just get a value from the storage service. If you can use {@link getFromGlobal} or {@link getFromUser}, you should.
|
||||
* @param key location
|
||||
* @returns the value at the location
|
||||
*/
|
||||
get<T>(key: string): Promise<T> {
|
||||
return this.storageService.get<T>(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a value in the storage service at the given key.
|
||||
*
|
||||
* This is a brute force method to just set a value in the storage service. If you can use {@link setToGlobal} or {@link setToUser}, you should.
|
||||
* @param key location
|
||||
* @param value the value to set
|
||||
* @returns
|
||||
*/
|
||||
set<T>(key: string, value: T): Promise<void> {
|
||||
this.logService.info(`Setting ${key}`);
|
||||
return this.storageService.save(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a globally scoped value from a location derived through the key definition
|
||||
*
|
||||
* This is for use with the state providers framework, DO NOT use for values stored with {@link StateService},
|
||||
* use {@link get} for those.
|
||||
* @param keyDefinition unique key definition
|
||||
* @returns value from store
|
||||
*/
|
||||
getFromGlobal<T>(keyDefinition: KeyDefinitionLike): Promise<T> {
|
||||
return this.get<T>(this.getGlobalKey(keyDefinition));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a globally scoped value to a location derived through the key definition
|
||||
*
|
||||
* This is for use with the state providers framework, DO NOT use for values stored with {@link StateService},
|
||||
* use {@link set} for those.
|
||||
* @param keyDefinition unique key definition
|
||||
* @param value value to store
|
||||
* @returns void
|
||||
*/
|
||||
setToGlobal<T>(keyDefinition: KeyDefinitionLike, value: T): Promise<void> {
|
||||
return this.set(this.getGlobalKey(keyDefinition), value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a user scoped value from a location derived through the user id and key definition
|
||||
*
|
||||
* This is for use with the state providers framework, DO NOT use for values stored with {@link StateService},
|
||||
* use {@link get} for those.
|
||||
* @param userId userId to use in the key
|
||||
* @param keyDefinition unique key definition
|
||||
* @returns value from store
|
||||
*/
|
||||
getFromUser<T>(userId: string, keyDefinition: KeyDefinitionLike): Promise<T> {
|
||||
return this.get<T>(this.getUserKey(userId, keyDefinition));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a user scoped value to a location derived through the user id and key definition
|
||||
*
|
||||
* This is for use with the state providers framework, DO NOT use for values stored with {@link StateService},
|
||||
* use {@link set} for those.
|
||||
* @param userId userId to use in the key
|
||||
* @param keyDefinition unique key definition
|
||||
* @param value value to store
|
||||
* @returns void
|
||||
*/
|
||||
setToUser<T>(userId: string, keyDefinition: KeyDefinitionLike, value: T): Promise<void> {
|
||||
return this.set(this.getUserKey(userId, keyDefinition), value);
|
||||
}
|
||||
|
||||
info(message: string): void {
|
||||
this.logService.info(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to read all Account objects stored by the State Service.
|
||||
*
|
||||
* This is useful from creating migrations off of this paradigm, but should not be used once a value is migrated to a state provider.
|
||||
*
|
||||
* @returns a list of all accounts that have been authenticated with state service, cast the the expected type.
|
||||
*/
|
||||
async getAccounts<ExpectedAccountType>(): Promise<
|
||||
{ userId: string; account: ExpectedAccountType }[]
|
||||
> {
|
||||
|
@ -34,4 +114,66 @@ export class MigrationHelper {
|
|||
})),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a user storage key appropriate for the current version.
|
||||
*
|
||||
* @param userId userId to use in the key
|
||||
* @param keyDefinition state and key to use in the key
|
||||
* @returns
|
||||
*/
|
||||
private getUserKey(userId: string, keyDefinition: KeyDefinitionLike): string {
|
||||
if (this.currentVersion < 10) {
|
||||
return userKeyBuilderPre10();
|
||||
} else {
|
||||
return userKeyBuilder(userId, keyDefinition);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a global storage key appropriate for the current version.
|
||||
*
|
||||
* @param keyDefinition state and key to use in the key
|
||||
* @returns
|
||||
*/
|
||||
private getGlobalKey(keyDefinition: KeyDefinitionLike): string {
|
||||
if (this.currentVersion < 10) {
|
||||
return globalKeyBuilderPre10();
|
||||
} else {
|
||||
return globalKeyBuilder(keyDefinition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* When this is updated, rename this function to `userKeyBuilderXToY` where `X` is the version number it
|
||||
* became relevant, and `Y` prior to the version it was updated.
|
||||
*
|
||||
* Be sure to update the map in `MigrationHelper` to point to the appropriate function for the current version.
|
||||
* @param userId The userId of the user you want the key to be for.
|
||||
* @param keyDefinition the key definition of which data the key should point to.
|
||||
* @returns
|
||||
*/
|
||||
function userKeyBuilder(userId: string, keyDefinition: KeyDefinitionLike): string {
|
||||
return `user_${userId}_${keyDefinition.stateDefinition.name}_${keyDefinition.key}`;
|
||||
}
|
||||
|
||||
function userKeyBuilderPre10(): string {
|
||||
throw Error("No key builder should be used for versions prior to 10.");
|
||||
}
|
||||
|
||||
/**
|
||||
* When this is updated, rename this function to `globalKeyBuilderXToY` where `X` is the version number
|
||||
* it became relevant, and `Y` prior to the version it was updated.
|
||||
*
|
||||
* Be sure to update the map in `MigrationHelper` to point to the appropriate function for the current version.
|
||||
* @param keyDefinition the key definition of which data the key should point to.
|
||||
* @returns
|
||||
*/
|
||||
function globalKeyBuilder(keyDefinition: KeyDefinitionLike): string {
|
||||
return `global_${keyDefinition.stateDefinition.name}_${keyDefinition.key}`;
|
||||
}
|
||||
|
||||
function globalKeyBuilderPre10(): string {
|
||||
throw Error("No key builder should be used for versions prior to 10.");
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue