refactor: 🔥 migration code (#806)
This commit is contained in:
parent
5a4da7beed
commit
1b47ad22da
21 changed files with 0 additions and 898 deletions
|
@ -1,21 +0,0 @@
|
|||
import { z } from "zod";
|
||||
|
||||
const addressSchema = z.object({
|
||||
country: z.string(),
|
||||
zip: z.string(),
|
||||
state: z.string(),
|
||||
city: z.string(),
|
||||
street: z.string(),
|
||||
});
|
||||
|
||||
const channelSchema = z.object({
|
||||
providerInstanceId: z.string(),
|
||||
enabled: z.boolean(),
|
||||
address: addressSchema,
|
||||
});
|
||||
|
||||
export type ChannelV1 = z.infer<typeof channelSchema>;
|
||||
|
||||
const channelsV1Schema = z.record(channelSchema);
|
||||
|
||||
export type ChannelsV1 = z.infer<typeof channelsV1Schema>;
|
|
@ -1,6 +0,0 @@
|
|||
import { z } from "zod";
|
||||
import { channelsSchema } from "../../src/modules/channel-configuration/channel-config";
|
||||
|
||||
export const channelsV2Schema = channelsSchema;
|
||||
|
||||
export type ChannelsV2 = z.infer<typeof channelsV2Schema>;
|
|
@ -1,31 +0,0 @@
|
|||
/* eslint-disable turbo/no-undeclared-env-vars */
|
||||
|
||||
import { SaleorCloudAPL } from "@saleor/app-sdk/APL";
|
||||
import { createSettingsManager } from "../../src/modules/app/metadata-manager";
|
||||
import { createGraphQLClient } from "@saleor/apps-shared";
|
||||
|
||||
export const getMetadataManagerForEnv = (apiUrl: string, appToken: string, appId: string) => {
|
||||
const client = createGraphQLClient({
|
||||
saleorApiUrl: apiUrl,
|
||||
token: appToken,
|
||||
});
|
||||
|
||||
return createSettingsManager(client, appId);
|
||||
};
|
||||
|
||||
export const verifyRequiredEnvs = () => {
|
||||
const requiredEnvs = ["SALEOR_CLOUD_TOKEN", "SALEOR_CLOUD_RESOURCE_URL", "SECRET_KEY"];
|
||||
|
||||
if (!requiredEnvs.every((env) => process.env[env])) {
|
||||
throw new Error(`Missing envs: ${requiredEnvs.join(" | ")}`);
|
||||
}
|
||||
};
|
||||
|
||||
export const fetchCloudAplEnvs = () => {
|
||||
const saleorAPL = new SaleorCloudAPL({
|
||||
token: process.env.SALEOR_CLOUD_TOKEN!,
|
||||
resourceUrl: process.env.SALEOR_CLOUD_RESOURCE_URL!,
|
||||
});
|
||||
|
||||
return saleorAPL.getAll();
|
||||
};
|
|
@ -1,124 +0,0 @@
|
|||
import { saleorApp } from "../../saleor-app";
|
||||
import { Logger, createLogger } from "../../src/lib/logger";
|
||||
import { createSettingsManager } from "../../src/modules/app/metadata-manager";
|
||||
import { TaxProvidersV1 } from "./tax-providers-config-schema-v1";
|
||||
import { TaxProvidersPrivateMetadataManagerV1 } from "./tax-providers-metadata-manager-v1";
|
||||
import { ChannelsV1 } from "./channels-config-schema-v1";
|
||||
|
||||
import * as dotenv from "dotenv";
|
||||
import { TaxChannelsPrivateMetadataManagerV1 } from "./tax-channels-metadata-manager-v1";
|
||||
import { createGraphQLClient } from "@saleor/apps-shared";
|
||||
|
||||
dotenv.config();
|
||||
|
||||
export const dummyChannelsV1Config: ChannelsV1 = {
|
||||
"default-channel": {
|
||||
providerInstanceId: "24822834-1a49-4b51-8a59-579affdb772f",
|
||||
address: {
|
||||
city: "San Francisco",
|
||||
country: "US",
|
||||
state: "CA",
|
||||
street: "Sesame Street",
|
||||
zip: "10001",
|
||||
},
|
||||
enabled: true,
|
||||
},
|
||||
"channel-pln": {
|
||||
providerInstanceId: "d15d9907-a3cb-42d2-9336-366d2366e91b",
|
||||
address: {
|
||||
city: "San Francisco",
|
||||
country: "US",
|
||||
state: "CA",
|
||||
street: "Sesame Street",
|
||||
zip: "10001",
|
||||
},
|
||||
enabled: true,
|
||||
},
|
||||
};
|
||||
|
||||
export const dummyTaxProvidersV1Config: TaxProvidersV1 = [
|
||||
{
|
||||
provider: "avatax",
|
||||
id: "24822834-1a49-4b51-8a59-579affdb772f",
|
||||
config: {
|
||||
isAutocommit: true,
|
||||
isSandbox: true,
|
||||
name: "Avatalara1",
|
||||
password: "password",
|
||||
username: "username",
|
||||
companyCode: "companyCode",
|
||||
shippingTaxCode: "shippingTaxCode",
|
||||
},
|
||||
},
|
||||
{
|
||||
provider: "taxjar",
|
||||
id: "d15d9907-a3cb-42d2-9336-366d2366e91b",
|
||||
config: {
|
||||
isSandbox: true,
|
||||
apiKey: "apiKey",
|
||||
name: "TaxJar1",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
// This class is used to generate dummy config for the app to check if the runtime migrations work as expected.
|
||||
class DummyConfigGenerator {
|
||||
private logger: Logger;
|
||||
constructor(private domain: string) {
|
||||
this.logger = createLogger({
|
||||
name: "DummyConfigGenerator",
|
||||
});
|
||||
}
|
||||
private getFileApl = () => {
|
||||
return saleorApp.apl.getAll();
|
||||
};
|
||||
|
||||
private generateDummyTaxProvidersConfig = (): TaxProvidersV1 => dummyTaxProvidersV1Config;
|
||||
|
||||
private generateDummyTaxChannelsConfig = (): ChannelsV1 => dummyChannelsV1Config;
|
||||
|
||||
generate = async () => {
|
||||
console.log("Generating dummy config");
|
||||
const apls = await this.getFileApl();
|
||||
|
||||
console.log({ apls }, "Apls retrieved");
|
||||
|
||||
const target = apls.find((apl) => apl.domain === this.domain);
|
||||
|
||||
if (!target) {
|
||||
throw new Error(`Domain ${this.domain} not found in apls`);
|
||||
}
|
||||
|
||||
const dummyTaxProvidersConfig = this.generateDummyTaxProvidersConfig();
|
||||
const dummyTaxChannelsConfig = this.generateDummyTaxChannelsConfig();
|
||||
|
||||
console.log({ dummyTaxProvidersConfig, dummyTaxChannelsConfig }, "Dummy configs generated");
|
||||
|
||||
const client = createGraphQLClient({
|
||||
saleorApiUrl: target.saleorApiUrl,
|
||||
token: target.token,
|
||||
});
|
||||
|
||||
const metadataManager = createSettingsManager(client, target.appId);
|
||||
const taxProvidersManager = new TaxProvidersPrivateMetadataManagerV1(
|
||||
metadataManager,
|
||||
target.saleorApiUrl
|
||||
);
|
||||
|
||||
const taxChannelsManager = new TaxChannelsPrivateMetadataManagerV1(
|
||||
metadataManager,
|
||||
target.saleorApiUrl
|
||||
);
|
||||
|
||||
console.log("Setting dummy configs");
|
||||
|
||||
await taxProvidersManager.setConfig(dummyTaxProvidersConfig);
|
||||
await taxChannelsManager.setConfig(dummyTaxChannelsConfig);
|
||||
|
||||
console.log("Dummy config set");
|
||||
};
|
||||
}
|
||||
|
||||
// const dummyConfigGenerator = new DummyConfigGenerator("");
|
||||
|
||||
// dummyConfigGenerator.generate();
|
|
@ -1,78 +0,0 @@
|
|||
/* eslint-disable turbo/no-undeclared-env-vars */
|
||||
|
||||
import * as dotenv from "dotenv";
|
||||
import { fetchCloudAplEnvs, getMetadataManagerForEnv, verifyRequiredEnvs } from "./migration-utils";
|
||||
import { TaxProvidersV1toV2MigrationManager } from "./tax-providers-migration-v1-to-v2";
|
||||
import { TaxChannelsV1toV2MigrationManager } from "./tax-channels-migration-v1-to-v2";
|
||||
|
||||
dotenv.config();
|
||||
|
||||
const runMigration = async () => {
|
||||
console.log("Starting running migration");
|
||||
|
||||
verifyRequiredEnvs();
|
||||
|
||||
console.log("Envs verified, fetching envs");
|
||||
const allEnvs = await fetchCloudAplEnvs().catch((r) => {
|
||||
console.error(r);
|
||||
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
const report = {
|
||||
taxProviders: [] as string[],
|
||||
taxChannels: [] as string[],
|
||||
none: [] as string[],
|
||||
};
|
||||
|
||||
for (const env of allEnvs) {
|
||||
let isTaxProvidersMigrated = false;
|
||||
let isTaxChannelsMigrated = false;
|
||||
|
||||
console.log("Working on env: ", env.saleorApiUrl);
|
||||
|
||||
const metadataManager = getMetadataManagerForEnv(env.saleorApiUrl, env.token, env.appId);
|
||||
|
||||
const taxProvidersMigrationManager = new TaxProvidersV1toV2MigrationManager(
|
||||
metadataManager,
|
||||
env.saleorApiUrl,
|
||||
{ mode: "migrate" }
|
||||
);
|
||||
|
||||
const taxProvidersMigratedConfig = await taxProvidersMigrationManager.migrateIfNeeded();
|
||||
|
||||
if (taxProvidersMigratedConfig) {
|
||||
console.log("Config migrated", taxProvidersMigratedConfig);
|
||||
isTaxProvidersMigrated = true;
|
||||
}
|
||||
|
||||
const taxChannelsMigrationManager = new TaxChannelsV1toV2MigrationManager(
|
||||
metadataManager,
|
||||
env.saleorApiUrl,
|
||||
{ mode: "migrate" }
|
||||
);
|
||||
|
||||
const taxChannelsMigratedConfig = await taxChannelsMigrationManager.migrateIfNeeded();
|
||||
|
||||
if (taxChannelsMigratedConfig) {
|
||||
console.log("Config migrated", taxChannelsMigratedConfig);
|
||||
isTaxChannelsMigrated = true;
|
||||
}
|
||||
|
||||
if (isTaxProvidersMigrated) {
|
||||
report.taxProviders.push(env.saleorApiUrl);
|
||||
}
|
||||
|
||||
if (isTaxChannelsMigrated) {
|
||||
report.taxChannels.push(env.saleorApiUrl);
|
||||
}
|
||||
|
||||
if (!isTaxProvidersMigrated && !isTaxChannelsMigrated) {
|
||||
report.none.push(env.saleorApiUrl);
|
||||
}
|
||||
}
|
||||
|
||||
console.log("Report", report);
|
||||
};
|
||||
|
||||
runMigration();
|
|
@ -1,78 +0,0 @@
|
|||
/* eslint-disable turbo/no-undeclared-env-vars */
|
||||
|
||||
import * as dotenv from "dotenv";
|
||||
import { fetchCloudAplEnvs, getMetadataManagerForEnv, verifyRequiredEnvs } from "./migration-utils";
|
||||
import { TaxProvidersV1toV2MigrationManager } from "./tax-providers-migration-v1-to-v2";
|
||||
import { TaxChannelsV1toV2MigrationManager } from "./tax-channels-migration-v1-to-v2";
|
||||
|
||||
dotenv.config();
|
||||
|
||||
const runReport = async () => {
|
||||
console.log("Starting running migration");
|
||||
|
||||
verifyRequiredEnvs();
|
||||
|
||||
console.log("Envs verified, fetching envs");
|
||||
const allEnvs = await fetchCloudAplEnvs().catch((r) => {
|
||||
console.error(r);
|
||||
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
const report = {
|
||||
taxProviders: [] as string[],
|
||||
taxChannels: [] as string[],
|
||||
none: [] as string[],
|
||||
};
|
||||
|
||||
for (const env of allEnvs) {
|
||||
let isTaxProvidersMigrated = false;
|
||||
let isTaxChannelsMigrated = false;
|
||||
|
||||
console.log("Working on env: ", env.saleorApiUrl);
|
||||
|
||||
const metadataManager = getMetadataManagerForEnv(env.saleorApiUrl, env.token, env.appId);
|
||||
|
||||
const taxProvidersMigrationManager = new TaxProvidersV1toV2MigrationManager(
|
||||
metadataManager,
|
||||
env.saleorApiUrl,
|
||||
{ mode: "report" }
|
||||
);
|
||||
|
||||
const taxProvidersMigratedConfig = await taxProvidersMigrationManager.migrateIfNeeded();
|
||||
|
||||
if (taxProvidersMigratedConfig) {
|
||||
console.log("Config migrated", taxProvidersMigratedConfig);
|
||||
isTaxProvidersMigrated = true;
|
||||
}
|
||||
|
||||
const taxChannelsMigrationManager = new TaxChannelsV1toV2MigrationManager(
|
||||
metadataManager,
|
||||
env.saleorApiUrl,
|
||||
{ mode: "report" }
|
||||
);
|
||||
|
||||
const taxChannelsMigratedConfig = await taxChannelsMigrationManager.migrateIfNeeded();
|
||||
|
||||
if (taxChannelsMigratedConfig) {
|
||||
console.log("Config migrated", taxChannelsMigratedConfig);
|
||||
isTaxChannelsMigrated = true;
|
||||
}
|
||||
|
||||
if (isTaxProvidersMigrated) {
|
||||
report.taxProviders.push(env.saleorApiUrl);
|
||||
}
|
||||
|
||||
if (isTaxChannelsMigrated) {
|
||||
report.taxChannels.push(env.saleorApiUrl);
|
||||
}
|
||||
|
||||
if (!isTaxProvidersMigrated && !isTaxChannelsMigrated) {
|
||||
report.none.push(env.saleorApiUrl);
|
||||
}
|
||||
}
|
||||
|
||||
console.log("Report", report);
|
||||
};
|
||||
|
||||
runReport();
|
|
@ -1,32 +0,0 @@
|
|||
// TODO: MIGRATION CODE FROM CONFIG VERSION V1. REMOVE THIS FILE AFTER MIGRATION
|
||||
|
||||
import { SettingsManager } from "@saleor/app-sdk/settings-manager";
|
||||
import { ChannelsV1 } from "./channels-config-schema-v1";
|
||||
|
||||
export class TaxChannelsPrivateMetadataManagerV1 {
|
||||
private metadataKey = "tax-channels";
|
||||
|
||||
constructor(private metadataManager: SettingsManager, private saleorApiUrl: string) {}
|
||||
|
||||
getConfig(): Promise<ChannelsV1 | undefined> {
|
||||
return this.metadataManager.get(this.metadataKey, this.saleorApiUrl).then((data) => {
|
||||
if (!data) {
|
||||
return data;
|
||||
}
|
||||
|
||||
try {
|
||||
return JSON.parse(data);
|
||||
} catch (e) {
|
||||
throw new Error("Invalid metadata value, cant be parsed");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
setConfig(config: ChannelsV1): Promise<void> {
|
||||
return this.metadataManager.set({
|
||||
key: this.metadataKey,
|
||||
value: JSON.stringify(config),
|
||||
domain: this.saleorApiUrl,
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
// TODO: MIGRATION CODE FROM CONFIG VERSION V1. REMOVE THIS FILE AFTER MIGRATION
|
||||
|
||||
import { SettingsManager } from "@saleor/app-sdk/settings-manager";
|
||||
import { ChannelsV2 } from "./channels-config-schema-v2";
|
||||
|
||||
export class TaxChannelsPrivateMetadataManagerV2 {
|
||||
private metadataKey = "tax-channels-v2";
|
||||
|
||||
constructor(private metadataManager: SettingsManager, private saleorApiUrl: string) {}
|
||||
|
||||
getConfig(): Promise<ChannelsV2 | undefined> {
|
||||
return this.metadataManager.get(this.metadataKey, this.saleorApiUrl).then((data) => {
|
||||
if (!data) {
|
||||
return data;
|
||||
}
|
||||
|
||||
try {
|
||||
return JSON.parse(data);
|
||||
} catch (e) {
|
||||
throw new Error("Invalid metadata value, cant be parsed");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
setConfig(config: ChannelsV2): Promise<void> {
|
||||
return this.metadataManager.set({
|
||||
key: this.metadataKey,
|
||||
value: JSON.stringify(config),
|
||||
domain: this.saleorApiUrl,
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,57 +0,0 @@
|
|||
import { SettingsManager } from "@saleor/app-sdk/settings-manager";
|
||||
import { Logger, createLogger } from "../../src/lib/logger";
|
||||
import { TaxChannelsPrivateMetadataManagerV1 } from "./tax-channels-metadata-manager-v1";
|
||||
import { TaxChannelsPrivateMetadataManagerV2 } from "./tax-channels-metadata-manager-v2";
|
||||
import { TaxChannelsTransformV1toV2 } from "./tax-channels-transform-v1-to-v2";
|
||||
|
||||
export class TaxChannelsV1toV2MigrationManager {
|
||||
private logger: Logger;
|
||||
constructor(
|
||||
private metadataManager: SettingsManager,
|
||||
private saleorApiUrl: string,
|
||||
private options: { mode: "report" | "migrate" } = { mode: "migrate" }
|
||||
) {
|
||||
this.logger = createLogger({
|
||||
name: "TaxChannelsV1toV2MigrationManager",
|
||||
});
|
||||
}
|
||||
|
||||
async migrateIfNeeded() {
|
||||
const taxChannelsManagerV1 = new TaxChannelsPrivateMetadataManagerV1(
|
||||
this.metadataManager,
|
||||
this.saleorApiUrl
|
||||
);
|
||||
|
||||
const taxChannelsManagerV2 = new TaxChannelsPrivateMetadataManagerV2(
|
||||
this.metadataManager,
|
||||
this.saleorApiUrl
|
||||
);
|
||||
|
||||
/*
|
||||
* Commenting this out, because we want to overwrite the previous migrations.
|
||||
* const currentConfig = await taxChannelsManagerV2.getConfig();
|
||||
* if (currentConfig) {
|
||||
* this.logger.info("Migration is not necessary, we have current config.");
|
||||
* return currentConfig;
|
||||
* }
|
||||
*/
|
||||
|
||||
const previousChannelConfig = await taxChannelsManagerV1.getConfig();
|
||||
|
||||
if (!previousChannelConfig) {
|
||||
this.logger.info("Previous config not found. Migration not possible.");
|
||||
return;
|
||||
}
|
||||
|
||||
this.logger.info("Previous config found. Migrating...");
|
||||
|
||||
const transformer = new TaxChannelsTransformV1toV2();
|
||||
const nextConfig = transformer.transform(previousChannelConfig);
|
||||
|
||||
if (this.options.mode === "migrate") {
|
||||
await taxChannelsManagerV2.setConfig(nextConfig);
|
||||
}
|
||||
|
||||
return nextConfig;
|
||||
}
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
import { dummyChannelsV1Config } from "./run-generate-dummy-data";
|
||||
import { TaxChannelsTransformV1toV2 } from "./tax-channels-transform-v1-to-v2";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
const transformer = new TaxChannelsTransformV1toV2();
|
||||
|
||||
describe("TaxChannelsTransformV1toV2", () => {
|
||||
it("should transform v1 to v2", () => {
|
||||
const result = transformer.transform(dummyChannelsV1Config);
|
||||
|
||||
expect(result).toEqual([
|
||||
{
|
||||
id: expect.any(String),
|
||||
config: {
|
||||
providerConnectionId: "24822834-1a49-4b51-8a59-579affdb772f",
|
||||
slug: "default-channel",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: expect.any(String),
|
||||
config: {
|
||||
providerConnectionId: "d15d9907-a3cb-42d2-9336-366d2366e91b",
|
||||
slug: "channel-pln",
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it("should return empty array if no channels are provided", () => {
|
||||
const result = transformer.transform({});
|
||||
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
});
|
|
@ -1,20 +0,0 @@
|
|||
import { createId } from "../../src/lib/utils";
|
||||
import { ChannelsV1 } from "./channels-config-schema-v1";
|
||||
import { ChannelsV2 } from "./channels-config-schema-v2";
|
||||
|
||||
export class TaxChannelsTransformV1toV2 {
|
||||
transform(channels: ChannelsV1): ChannelsV2 {
|
||||
return Object.keys(channels).map((slug) => {
|
||||
const channel = channels[slug];
|
||||
|
||||
return {
|
||||
// * There was no id in v1, so we need to generate it
|
||||
id: createId(),
|
||||
config: {
|
||||
providerConnectionId: channel.providerInstanceId,
|
||||
slug,
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
import { z } from "zod";
|
||||
|
||||
const avataxConfigSchema = z.object({
|
||||
name: z.string().min(1, { message: "Name requires at least one character." }),
|
||||
username: z.string().min(1, { message: "Username requires at least one character." }),
|
||||
password: z.string().min(1, { message: "Password requires at least one character." }),
|
||||
isSandbox: z.boolean(),
|
||||
companyCode: z.string().optional(),
|
||||
isAutocommit: z.boolean(),
|
||||
shippingTaxCode: z.string().optional(),
|
||||
});
|
||||
|
||||
const avataxInstanceConfigV1Schema = z.object({
|
||||
id: z.string(),
|
||||
provider: z.literal("avatax"),
|
||||
config: avataxConfigSchema,
|
||||
});
|
||||
|
||||
export type AvataxInstanceConfigV1 = z.infer<typeof avataxInstanceConfigV1Schema>;
|
||||
|
||||
const taxJarConfigSchema = z.object({
|
||||
name: z.string().min(1, { message: "Name requires at least one character." }),
|
||||
apiKey: z.string().min(1, { message: "API Key requires at least one character." }),
|
||||
isSandbox: z.boolean(),
|
||||
});
|
||||
|
||||
const taxJarInstanceConfigV1Schema = z.object({
|
||||
id: z.string(),
|
||||
provider: z.literal("taxjar"),
|
||||
config: taxJarConfigSchema,
|
||||
});
|
||||
|
||||
export type TaxJarInstanceConfigV1 = z.infer<typeof taxJarInstanceConfigV1Schema>;
|
||||
|
||||
const taxProviderV1Schema = taxJarInstanceConfigV1Schema.or(avataxInstanceConfigV1Schema);
|
||||
|
||||
export type TaxProviderV1 = z.infer<typeof taxProviderV1Schema>;
|
||||
const taxProvidersV1Schema = z.array(taxProviderV1Schema);
|
||||
|
||||
export type TaxProvidersV1 = z.infer<typeof taxProvidersV1Schema>;
|
|
@ -1,6 +0,0 @@
|
|||
import { z } from "zod";
|
||||
import { providerConnectionsSchema } from "../../src/modules/provider-connections/provider-connections";
|
||||
|
||||
const taxProvidersV2Schema = providerConnectionsSchema;
|
||||
|
||||
export type TaxProvidersV2 = z.infer<typeof taxProvidersV2Schema>;
|
|
@ -1,32 +0,0 @@
|
|||
// TODO: MIGRATION CODE FROM CONFIG VERSION V1. REMOVE THIS FILE AFTER MIGRATION
|
||||
|
||||
import { SettingsManager } from "@saleor/app-sdk/settings-manager";
|
||||
import { TaxProvidersV1 } from "./tax-providers-config-schema-v1";
|
||||
|
||||
export class TaxProvidersPrivateMetadataManagerV1 {
|
||||
private metadataKey = "tax-providers";
|
||||
|
||||
constructor(private metadataManager: SettingsManager, private saleorApiUrl: string) {}
|
||||
|
||||
getConfig(): Promise<TaxProvidersV1 | undefined> {
|
||||
return this.metadataManager.get(this.metadataKey, this.saleorApiUrl).then((data) => {
|
||||
if (!data) {
|
||||
return data;
|
||||
}
|
||||
|
||||
try {
|
||||
return JSON.parse(data);
|
||||
} catch (e) {
|
||||
throw new Error("Invalid metadata value, cant be parsed");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
setConfig(config: TaxProvidersV1): Promise<void> {
|
||||
return this.metadataManager.set({
|
||||
key: this.metadataKey,
|
||||
value: JSON.stringify(config),
|
||||
domain: this.saleorApiUrl,
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
// TODO: MIGRATION CODE FROM CONFIG VERSION V1. REMOVE THIS FILE AFTER MIGRATION
|
||||
|
||||
import { SettingsManager } from "@saleor/app-sdk/settings-manager";
|
||||
import { TaxProvidersV2 } from "./tax-providers-config-schema-v2";
|
||||
import { TAX_PROVIDER_KEY } from "../../src/modules/provider-connections/public-provider-connections.service";
|
||||
|
||||
export class TaxProvidersPrivateMetadataManagerV2 {
|
||||
private metadataKey = TAX_PROVIDER_KEY;
|
||||
|
||||
constructor(private metadataManager: SettingsManager, private saleorApiUrl: string) {}
|
||||
|
||||
getConfig(): Promise<TaxProvidersV2 | undefined> {
|
||||
return this.metadataManager.get(this.metadataKey, this.saleorApiUrl).then((data) => {
|
||||
if (!data) {
|
||||
return data;
|
||||
}
|
||||
|
||||
try {
|
||||
return JSON.parse(data);
|
||||
} catch (e) {
|
||||
throw new Error("Invalid metadata value, cant be parsed");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
setConfig(config: TaxProvidersV2): Promise<void> {
|
||||
return this.metadataManager.set({
|
||||
key: this.metadataKey,
|
||||
value: JSON.stringify(config),
|
||||
domain: this.saleorApiUrl,
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
import { SettingsManager } from "@saleor/app-sdk/settings-manager";
|
||||
import { Logger, createLogger } from "../../src/lib/logger";
|
||||
import { TaxProvidersPrivateMetadataManagerV1 } from "./tax-providers-metadata-manager-v1";
|
||||
import { TaxProvidersPrivateMetadataManagerV2 } from "./tax-providers-metadata-manager-v2";
|
||||
import { TaxProvidersV1ToV2Transformer } from "./tax-providers-transform-v1-to-v2";
|
||||
import { TaxChannelsPrivateMetadataManagerV1 } from "./tax-channels-metadata-manager-v1";
|
||||
|
||||
export class TaxProvidersV1toV2MigrationManager {
|
||||
private logger: Logger;
|
||||
constructor(
|
||||
private metadataManager: SettingsManager,
|
||||
private saleorApiUrl: string,
|
||||
private options: { mode: "report" | "migrate" } = { mode: "migrate" }
|
||||
) {
|
||||
this.logger = createLogger({
|
||||
name: "TaxProvidersV1toV2MigrationManager",
|
||||
});
|
||||
}
|
||||
|
||||
async migrateIfNeeded() {
|
||||
const taxProvidersManagerV1 = new TaxProvidersPrivateMetadataManagerV1(
|
||||
this.metadataManager,
|
||||
this.saleorApiUrl
|
||||
);
|
||||
const taxProvidersManagerV2 = new TaxProvidersPrivateMetadataManagerV2(
|
||||
this.metadataManager,
|
||||
this.saleorApiUrl
|
||||
);
|
||||
|
||||
const taxChannelsManagerV1 = new TaxChannelsPrivateMetadataManagerV1(
|
||||
this.metadataManager,
|
||||
this.saleorApiUrl
|
||||
);
|
||||
|
||||
/*
|
||||
* Commenting this out, because we want to overwrite the previous migrations.
|
||||
* const currentTaxProvidersConfig = await taxProvidersManagerV2.getConfig();
|
||||
* if (currentTaxProvidersConfig) {
|
||||
* this.logger.info("Migration is not necessary, the config is up to date.");
|
||||
* return;
|
||||
* }
|
||||
* this.logger.info("Current config not found.");
|
||||
*/
|
||||
|
||||
const previousTaxProvidersConfig = await taxProvidersManagerV1.getConfig();
|
||||
const previousChannelConfig = await taxChannelsManagerV1.getConfig();
|
||||
|
||||
if (!previousTaxProvidersConfig || !previousChannelConfig) {
|
||||
this.logger.info(
|
||||
{ previousChannelConfig, previousTaxProvidersConfig },
|
||||
"Previous config not found. Migration not possible."
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
this.logger.info("Previous config found. Migrating...");
|
||||
|
||||
const transformer = new TaxProvidersV1ToV2Transformer();
|
||||
const nextConfig = transformer.transform(previousTaxProvidersConfig, previousChannelConfig);
|
||||
|
||||
if (this.options.mode === "migrate") {
|
||||
await taxProvidersManagerV2.setConfig(nextConfig);
|
||||
}
|
||||
|
||||
return nextConfig;
|
||||
}
|
||||
}
|
|
@ -1,60 +0,0 @@
|
|||
import { dummyChannelsV1Config, dummyTaxProvidersV1Config } from "./run-generate-dummy-data";
|
||||
import { TaxProvidersV1ToV2Transformer } from "./tax-providers-transform-v1-to-v2";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
const transformer = new TaxProvidersV1ToV2Transformer();
|
||||
|
||||
describe("TaxProvidersV1ToV2Transformer", () => {
|
||||
it("should transform v1 to v2", () => {
|
||||
const result = transformer.transform(dummyTaxProvidersV1Config, dummyChannelsV1Config);
|
||||
|
||||
expect(result).toEqual([
|
||||
{
|
||||
id: "24822834-1a49-4b51-8a59-579affdb772f",
|
||||
provider: "avatax",
|
||||
config: {
|
||||
name: "Avatalara1",
|
||||
isSandbox: true,
|
||||
isAutocommit: true,
|
||||
credentials: {
|
||||
username: "username",
|
||||
password: "password",
|
||||
},
|
||||
companyCode: "companyCode",
|
||||
shippingTaxCode: "shippingTaxCode",
|
||||
address: {
|
||||
city: "San Francisco",
|
||||
country: "US",
|
||||
state: "CA",
|
||||
street: "Sesame Street",
|
||||
zip: "10001",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "d15d9907-a3cb-42d2-9336-366d2366e91b",
|
||||
provider: "taxjar",
|
||||
config: {
|
||||
name: "TaxJar1",
|
||||
isSandbox: true,
|
||||
credentials: {
|
||||
apiKey: "apiKey",
|
||||
},
|
||||
address: {
|
||||
city: "San Francisco",
|
||||
country: "US",
|
||||
state: "CA",
|
||||
street: "Sesame Street",
|
||||
zip: "10001",
|
||||
},
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it("should return empty array if no channels and providers are provided", () => {
|
||||
const result = transformer.transform([], {});
|
||||
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
});
|
|
@ -1,91 +0,0 @@
|
|||
import { AvataxConnection } from "../../src/modules/avatax/avatax-connection-schema";
|
||||
import { TaxJarConnection } from "../../src/modules/taxjar/taxjar-connection-schema";
|
||||
import { ChannelV1, ChannelsV1 } from "./channels-config-schema-v1";
|
||||
import {
|
||||
AvataxInstanceConfigV1,
|
||||
TaxJarInstanceConfigV1,
|
||||
TaxProvidersV1,
|
||||
} from "./tax-providers-config-schema-v1";
|
||||
import { TaxProvidersV2 } from "./tax-providers-config-schema-v2";
|
||||
|
||||
export class TaxProvidersV1ToV2Transformer {
|
||||
private findTaxProviderChannelConfig = (id: string, channelsConfig: ChannelsV1): ChannelV1 => {
|
||||
const channel = Object.values(channelsConfig).find(
|
||||
(channel) => channel.providerInstanceId === id
|
||||
);
|
||||
|
||||
if (!channel) {
|
||||
throw new Error(`Channel with id ${id} not found`);
|
||||
}
|
||||
|
||||
return channel;
|
||||
};
|
||||
|
||||
private transformAvataxInstance = (
|
||||
instance: AvataxInstanceConfigV1,
|
||||
channel: ChannelV1
|
||||
): AvataxConnection => {
|
||||
return {
|
||||
id: instance.id,
|
||||
provider: "avatax",
|
||||
config: {
|
||||
name: instance.config.name,
|
||||
address: {
|
||||
city: channel.address.city,
|
||||
country: channel.address.country,
|
||||
state: channel.address.state,
|
||||
street: channel.address.street,
|
||||
zip: channel.address.zip,
|
||||
},
|
||||
credentials: {
|
||||
password: instance.config.password,
|
||||
username: instance.config.username,
|
||||
},
|
||||
isAutocommit: instance.config.isAutocommit,
|
||||
isSandbox: instance.config.isSandbox,
|
||||
companyCode: instance.config.companyCode,
|
||||
shippingTaxCode: instance.config.shippingTaxCode,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
private transformTaxJarInstance = (
|
||||
instance: TaxJarInstanceConfigV1,
|
||||
channel: ChannelV1
|
||||
): TaxJarConnection => {
|
||||
return {
|
||||
id: instance.id,
|
||||
provider: "taxjar",
|
||||
config: {
|
||||
name: instance.config.name,
|
||||
address: {
|
||||
city: channel.address.city,
|
||||
country: channel.address.country,
|
||||
state: channel.address.state,
|
||||
street: channel.address.street,
|
||||
zip: channel.address.zip,
|
||||
},
|
||||
credentials: {
|
||||
apiKey: instance.config.apiKey,
|
||||
},
|
||||
isSandbox: instance.config.isSandbox,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
transform = (taxProvidersConfig: TaxProvidersV1, channelsConfig: ChannelsV1): TaxProvidersV2 => {
|
||||
return taxProvidersConfig.map((instance) => {
|
||||
const channel = this.findTaxProviderChannelConfig(instance.id, channelsConfig);
|
||||
|
||||
if (instance.provider === "avatax") {
|
||||
return this.transformAvataxInstance(instance, channel);
|
||||
}
|
||||
|
||||
if (instance.provider === "taxjar") {
|
||||
return this.transformTaxJarInstance(instance, channel);
|
||||
}
|
||||
|
||||
throw new Error(`Unknown provider `);
|
||||
});
|
||||
};
|
||||
}
|
|
@ -1,5 +1,4 @@
|
|||
import { EncryptedMetadataManager } from "@saleor/app-sdk/settings-manager";
|
||||
import { TaxProvidersV1toV2MigrationManager } from "../../../../scripts/migrations/tax-providers-migration-v1-to-v2";
|
||||
import { createLogger, Logger } from "../../../lib/logger";
|
||||
import { CrudSettingsManager } from "../../crud-settings/crud-settings.service";
|
||||
import {
|
||||
|
@ -38,26 +37,6 @@ export class AvataxConnectionRepository {
|
|||
|
||||
async getAll(): Promise<AvataxConnection[]> {
|
||||
const { data } = await this.crudSettingsManager.readAll();
|
||||
/*
|
||||
* * migration logic start
|
||||
* // todo: remove after migration
|
||||
*/
|
||||
const migrationManager = new TaxProvidersV1toV2MigrationManager(
|
||||
this.settingsManager,
|
||||
this.saleorApiUrl
|
||||
);
|
||||
|
||||
const migratedConfig = await migrationManager.migrateIfNeeded();
|
||||
|
||||
if (migratedConfig) {
|
||||
this.logger.info("Config migrated", migratedConfig);
|
||||
return this.filterAvataxConnections(migratedConfig);
|
||||
}
|
||||
|
||||
this.logger.info("Config is up to date, no need to migrate.");
|
||||
/*
|
||||
* * migration logic end
|
||||
*/
|
||||
|
||||
const connections = providerConnectionsSchema.parse(data);
|
||||
|
||||
|
|
|
@ -3,7 +3,6 @@ import { ChannelConfigProperties } from "./channel-config";
|
|||
import { ChannelConfigurationRepository } from "./channel-configuration-repository";
|
||||
import { ChannelsFetcher } from "./channel-fetcher";
|
||||
import { ChannelConfigurationMerger } from "./channel-configuration-merger";
|
||||
import { TaxChannelsV1toV2MigrationManager } from "../../../scripts/migrations/tax-channels-migration-v1-to-v2";
|
||||
import { EncryptedMetadataManager } from "@saleor/app-sdk/settings-manager";
|
||||
import { Logger, createLogger } from "../../lib/logger";
|
||||
import { createSettingsManager } from "../app/metadata-manager";
|
||||
|
@ -30,19 +29,6 @@ export class ChannelConfigurationService {
|
|||
async getAll() {
|
||||
const channelsFetcher = new ChannelsFetcher(this.client);
|
||||
|
||||
const migrationManager = new TaxChannelsV1toV2MigrationManager(
|
||||
this.settingsManager,
|
||||
this.saleorApiUrl
|
||||
);
|
||||
|
||||
const migratedConfig = await migrationManager.migrateIfNeeded();
|
||||
|
||||
if (migratedConfig) {
|
||||
this.logger.info("Config migrated", migratedConfig);
|
||||
return migratedConfig;
|
||||
}
|
||||
|
||||
this.logger.info("Config is up to date, no need to migrate.");
|
||||
const channels = await channelsFetcher.fetchChannels();
|
||||
|
||||
const channelConfiguration = await this.configurationRepository.getAll();
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { EncryptedMetadataManager } from "@saleor/app-sdk/settings-manager";
|
||||
import { TaxProvidersV1toV2MigrationManager } from "../../../../scripts/migrations/tax-providers-migration-v1-to-v2";
|
||||
import { createLogger, Logger } from "../../../lib/logger";
|
||||
import { CrudSettingsManager } from "../../crud-settings/crud-settings.service";
|
||||
import {
|
||||
|
@ -34,26 +33,6 @@ export class TaxJarConnectionRepository {
|
|||
|
||||
async getAll(): Promise<TaxJarConnection[]> {
|
||||
const { data } = await this.crudSettingsManager.readAll();
|
||||
/*
|
||||
* * migration logic start
|
||||
* // todo: remove after migration
|
||||
*/
|
||||
const migrationManager = new TaxProvidersV1toV2MigrationManager(
|
||||
this.settingsManager,
|
||||
this.saleorApiUrl
|
||||
);
|
||||
|
||||
const migratedConfig = await migrationManager.migrateIfNeeded();
|
||||
|
||||
if (migratedConfig) {
|
||||
this.logger.info("Config migrated", migratedConfig);
|
||||
return this.filterTaxJarConnections(migratedConfig);
|
||||
}
|
||||
|
||||
this.logger.info("Config is up to date, no need to migrate.");
|
||||
/*
|
||||
* * migration logic end
|
||||
*/
|
||||
|
||||
const connections = providerConnectionsSchema.parse(data);
|
||||
|
||||
|
|
Loading…
Reference in a new issue