📧 Add runtime migrations to schema v2 (#535)
* Add runtime migrations to schema v2 * V2 migration scripts (#536)
This commit is contained in:
parent
235a7d92b7
commit
86c2b7b10b
29 changed files with 1171 additions and 90 deletions
|
@ -34,6 +34,7 @@
|
|||
"@urql/exchange-auth": "^1.0.0",
|
||||
"@vitejs/plugin-react": "4.0.0",
|
||||
"clsx": "^1.2.1",
|
||||
"dotenv": "^16.0.3",
|
||||
"graphql": "^16.6.0",
|
||||
"graphql-tag": "^2.12.6",
|
||||
"handlebars": "^4.7.7",
|
||||
|
|
7
apps/emails-and-messages/scripts/migrations/README.md
Normal file
7
apps/emails-and-messages/scripts/migrations/README.md
Normal file
|
@ -0,0 +1,7 @@
|
|||
# Metadata migration scripts
|
||||
|
||||
To run dry-run (check migration without mutating the data):
|
||||
`npx tsx scripts/migrations/run-report.ts`
|
||||
|
||||
To update date and save it:
|
||||
`npx tsx scripts/migrations/run-migration.ts`
|
|
@ -0,0 +1,30 @@
|
|||
/* eslint-disable turbo/no-undeclared-env-vars */
|
||||
|
||||
import { createClient } from "../../src/lib/create-graphql-client";
|
||||
import { SaleorCloudAPL } from "@saleor/app-sdk/APL";
|
||||
import { createSettingsManager } from "../../src/lib/metadata-manager";
|
||||
|
||||
export const getMetadataManagerForEnv = (apiUrl: string, appToken: string, appId: string) => {
|
||||
const client = createClient(apiUrl, async () => ({
|
||||
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();
|
||||
};
|
76
apps/emails-and-messages/scripts/migrations/run-migration.ts
Normal file
76
apps/emails-and-messages/scripts/migrations/run-migration.ts
Normal file
|
@ -0,0 +1,76 @@
|
|||
/* eslint-disable turbo/no-undeclared-env-vars */
|
||||
|
||||
import * as dotenv from "dotenv";
|
||||
import { fetchCloudAplEnvs, getMetadataManagerForEnv, verifyRequiredEnvs } from "./migration-utils";
|
||||
|
||||
import { SendgridPrivateMetadataManager } from "../../src/modules/sendgrid/configuration/sendgrid-metadata-manager";
|
||||
import { SmtpPrivateMetadataManager } from "../../src/modules/smtp/configuration/smtp-metadata-manager";
|
||||
|
||||
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 = {
|
||||
smtp: [] as string[],
|
||||
sendgrid: [] as string[],
|
||||
none: [] as string[],
|
||||
};
|
||||
|
||||
for (const env of allEnvs) {
|
||||
let isSmtpMigrated = false;
|
||||
let isSendgridMigrated = false;
|
||||
|
||||
console.log("Working on env: ", env.saleorApiUrl);
|
||||
|
||||
const metadataManager = getMetadataManagerForEnv(env.saleorApiUrl, env.token, env.appId);
|
||||
|
||||
const sendgridMetadataManager = new SendgridPrivateMetadataManager(
|
||||
metadataManager,
|
||||
env.saleorApiUrl
|
||||
);
|
||||
|
||||
const sendgridUpdatedSchema = await sendgridMetadataManager.getConfig();
|
||||
|
||||
if (sendgridUpdatedSchema) {
|
||||
console.log("Migrated sendgrid configuration found, overriding");
|
||||
isSendgridMigrated = true;
|
||||
await sendgridMetadataManager.setConfig(sendgridUpdatedSchema);
|
||||
}
|
||||
|
||||
const smtpMetadataManager = new SmtpPrivateMetadataManager(metadataManager, env.saleorApiUrl);
|
||||
|
||||
const smtpUpdatedSchema = await smtpMetadataManager.getConfig();
|
||||
|
||||
if (smtpUpdatedSchema) {
|
||||
console.log("Migrated smtp configuration found, overriding");
|
||||
isSmtpMigrated = true;
|
||||
await smtpMetadataManager.setConfig(smtpUpdatedSchema);
|
||||
}
|
||||
|
||||
if (isSendgridMigrated) {
|
||||
report.sendgrid.push(env.saleorApiUrl);
|
||||
}
|
||||
|
||||
if (isSmtpMigrated) {
|
||||
report.smtp.push(env.saleorApiUrl);
|
||||
}
|
||||
|
||||
if (!isSmtpMigrated && !isSendgridMigrated) {
|
||||
report.none.push(env.saleorApiUrl);
|
||||
}
|
||||
}
|
||||
|
||||
console.log("Report", report);
|
||||
};
|
||||
|
||||
runMigration();
|
97
apps/emails-and-messages/scripts/migrations/run-report.ts
Normal file
97
apps/emails-and-messages/scripts/migrations/run-report.ts
Normal file
|
@ -0,0 +1,97 @@
|
|||
/* eslint-disable turbo/no-undeclared-env-vars */
|
||||
|
||||
import * as dotenv from "dotenv";
|
||||
import { fetchCloudAplEnvs, getMetadataManagerForEnv, verifyRequiredEnvs } from "./migration-utils";
|
||||
|
||||
import { AppConfigPrivateMetadataManager } from "../../src/modules/app-configuration/app-config-metadata-manager";
|
||||
import { SendgridPrivateMetadataManagerV1 } from "../../src/modules/sendgrid/configuration/sendgrid-metadata-manager-v1";
|
||||
import { MjmlPrivateMetadataManager } from "../../src/modules/smtp/configuration/mjml-metadata-manager";
|
||||
import { smtpTransformV1toV2 } from "../../src/modules/smtp/configuration/migrations/smtp-transform-v1-to-v2";
|
||||
import { sendgridTransformV1toV2 } from "../../src/modules/sendgrid/configuration/migrations/sendgrid-transform-v1-to-v2";
|
||||
|
||||
dotenv.config();
|
||||
|
||||
const runReport = async () => {
|
||||
console.log("Starting running report");
|
||||
|
||||
verifyRequiredEnvs();
|
||||
|
||||
console.log("Envs verified, fetching envs");
|
||||
const allEnvs = await fetchCloudAplEnvs().catch((r) => {
|
||||
console.error(r);
|
||||
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
const report = {
|
||||
smtp: [] as string[],
|
||||
sendgrid: [] as string[],
|
||||
none: [] as string[],
|
||||
};
|
||||
|
||||
for (const env of allEnvs) {
|
||||
console.log("Working on env: ", env.saleorApiUrl);
|
||||
let isSmtpMigrated = false;
|
||||
let isSendgridMigrated = false;
|
||||
|
||||
const metadataManager = getMetadataManagerForEnv(env.saleorApiUrl, env.token, env.appId);
|
||||
|
||||
const sendgridMetadataManagerV1 = new SendgridPrivateMetadataManagerV1(
|
||||
metadataManager,
|
||||
env.saleorApiUrl
|
||||
);
|
||||
|
||||
const appMetadataManager = new AppConfigPrivateMetadataManager(
|
||||
metadataManager,
|
||||
env.saleorApiUrl
|
||||
);
|
||||
|
||||
const appConfiguration = await appMetadataManager.getConfig();
|
||||
|
||||
const sendgridConfigurationV1 = await sendgridMetadataManagerV1.getConfig();
|
||||
|
||||
if (sendgridConfigurationV1) {
|
||||
console.log("Found old sendgrid config, migrating");
|
||||
isSendgridMigrated = true;
|
||||
const v2 = sendgridTransformV1toV2({
|
||||
configV1: sendgridConfigurationV1,
|
||||
appConfigV1: appConfiguration,
|
||||
});
|
||||
|
||||
console.log("Old config", sendgridConfigurationV1);
|
||||
console.log("New config", v2);
|
||||
}
|
||||
|
||||
const mjmlMetadataManagerV1 = new MjmlPrivateMetadataManager(metadataManager, env.saleorApiUrl);
|
||||
|
||||
const mjmlConfiguration = await mjmlMetadataManagerV1.getConfig();
|
||||
|
||||
if (mjmlConfiguration) {
|
||||
console.log("Found old mjml config, migrating");
|
||||
isSmtpMigrated = true;
|
||||
const v2 = smtpTransformV1toV2({
|
||||
configV1: mjmlConfiguration,
|
||||
appConfigV1: appConfiguration,
|
||||
});
|
||||
|
||||
console.log("Old config", mjmlConfiguration);
|
||||
console.log("New config", v2);
|
||||
}
|
||||
|
||||
if (isSendgridMigrated) {
|
||||
report.sendgrid.push(env.saleorApiUrl);
|
||||
}
|
||||
|
||||
if (isSmtpMigrated) {
|
||||
report.smtp.push(env.saleorApiUrl);
|
||||
}
|
||||
|
||||
if (!isSmtpMigrated && !isSendgridMigrated) {
|
||||
report.none.push(env.saleorApiUrl);
|
||||
}
|
||||
}
|
||||
|
||||
console.log("Report", report);
|
||||
};
|
||||
|
||||
runReport();
|
|
@ -0,0 +1,32 @@
|
|||
// TODO: MIGRATION CODE FROM CONFIG VERSION V1. REMOVE THIS FILE AFTER MIGRATION
|
||||
|
||||
import { SettingsManager } from "@saleor/app-sdk/settings-manager";
|
||||
import { AppConfig } from "./app-config-schema";
|
||||
|
||||
export class AppConfigPrivateMetadataManager {
|
||||
private metadataKey = "app-config";
|
||||
|
||||
constructor(private metadataManager: SettingsManager, private saleorApiUrl: string) {}
|
||||
|
||||
getConfig(): Promise<AppConfig | 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: AppConfig): Promise<void> {
|
||||
return this.metadataManager.set({
|
||||
key: this.metadataKey,
|
||||
value: JSON.stringify(config),
|
||||
domain: this.saleorApiUrl,
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
// TODO: MIGRATION CODE FROM CONFIG VERSION V1. REMOVE THIS FILE AFTER MIGRATION
|
||||
|
||||
export interface AppConfigurationPerChannel {
|
||||
active: boolean;
|
||||
mjmlConfigurationId?: string;
|
||||
sendgridConfigurationId?: string;
|
||||
}
|
||||
|
||||
export type AppConfigurationsChannelMap = Record<string, AppConfigurationPerChannel>;
|
||||
|
||||
export type AppConfig = {
|
||||
configurationsPerChannel: AppConfigurationsChannelMap;
|
||||
};
|
|
@ -0,0 +1,100 @@
|
|||
import { expect, describe, it } from "vitest";
|
||||
import { getChannelsAssignedToConfigId } from "./get-channels-assigned-to-config-id";
|
||||
|
||||
describe("getChannelsAssignedToConfigId", function () {
|
||||
it("Do not assign to any channel, when theres no app configuration", () => {
|
||||
const channels = getChannelsAssignedToConfigId("id", "sendgrid", undefined);
|
||||
|
||||
expect(channels).toEqual({
|
||||
channels: [],
|
||||
mode: "restrict",
|
||||
override: true,
|
||||
});
|
||||
});
|
||||
|
||||
it("Do not assign sendgrid configuration to any channel, when app configuration did not assigned it", () => {
|
||||
const channels = getChannelsAssignedToConfigId("id", "sendgrid", {
|
||||
configurationsPerChannel: {
|
||||
"default-channel": {
|
||||
active: true,
|
||||
sendgridConfigurationId: "other-id",
|
||||
mjmlConfigurationId: "id",
|
||||
},
|
||||
"other-channel": {
|
||||
active: true,
|
||||
mjmlConfigurationId: "id",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(channels).toEqual({
|
||||
channels: [],
|
||||
mode: "restrict",
|
||||
override: true,
|
||||
});
|
||||
});
|
||||
|
||||
it("Assign sendgrid configuration to channel, when app configuration has assigned it", () => {
|
||||
const channels = getChannelsAssignedToConfigId("id", "sendgrid", {
|
||||
configurationsPerChannel: {
|
||||
"default-channel": {
|
||||
active: true,
|
||||
sendgridConfigurationId: "id",
|
||||
},
|
||||
"other-channel": {
|
||||
active: true,
|
||||
sendgridConfigurationId: "id",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(channels).toEqual({
|
||||
channels: ["default-channel", "other-channel"],
|
||||
mode: "restrict",
|
||||
override: true,
|
||||
});
|
||||
});
|
||||
|
||||
it("Do not assign mjml configuration to any channel, when app configuration did not assigned it", () => {
|
||||
const channels = getChannelsAssignedToConfigId("id", "mjml", {
|
||||
configurationsPerChannel: {
|
||||
"default-channel": {
|
||||
active: true,
|
||||
mjmlConfigurationId: "other-id",
|
||||
sendgridConfigurationId: "id",
|
||||
},
|
||||
"other-channel": {
|
||||
active: true,
|
||||
sendgridConfigurationId: "id",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(channels).toEqual({
|
||||
channels: [],
|
||||
mode: "restrict",
|
||||
override: true,
|
||||
});
|
||||
});
|
||||
|
||||
it("Assign mjml configuration to channel, when app configuration has assigned it", () => {
|
||||
const channels = getChannelsAssignedToConfigId("id", "mjml", {
|
||||
configurationsPerChannel: {
|
||||
"default-channel": {
|
||||
active: true,
|
||||
mjmlConfigurationId: "id",
|
||||
},
|
||||
"other-channel": {
|
||||
active: true,
|
||||
mjmlConfigurationId: "id",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(channels).toEqual({
|
||||
channels: ["default-channel", "other-channel"],
|
||||
mode: "restrict",
|
||||
override: true,
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,37 @@
|
|||
import { ChannelConfiguration } from "../../channels/channel-configuration-schema";
|
||||
import { AppConfig } from "../app-config-schema";
|
||||
|
||||
export const getChannelsAssignedToConfigId = (
|
||||
configId: string,
|
||||
moduleName: "sendgrid" | "mjml",
|
||||
appConfig?: AppConfig
|
||||
): ChannelConfiguration => {
|
||||
if (!appConfig) {
|
||||
return {
|
||||
channels: [],
|
||||
mode: "restrict",
|
||||
override: true,
|
||||
};
|
||||
}
|
||||
|
||||
const channels = [];
|
||||
|
||||
if (moduleName === "sendgrid") {
|
||||
for (const key in appConfig.configurationsPerChannel) {
|
||||
if (appConfig.configurationsPerChannel[key].sendgridConfigurationId === configId) {
|
||||
channels.push(key);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (const key in appConfig.configurationsPerChannel) {
|
||||
if (appConfig.configurationsPerChannel[key].mjmlConfigurationId === configId) {
|
||||
channels.push(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
return {
|
||||
channels,
|
||||
mode: "restrict",
|
||||
override: true,
|
||||
};
|
||||
};
|
|
@ -0,0 +1,38 @@
|
|||
import { AppConfigPrivateMetadataManager } from "../../../app-configuration/app-config-metadata-manager";
|
||||
import { SendgridPrivateMetadataManagerV1 } from "../sendgrid-metadata-manager-v1";
|
||||
import { SettingsManager } from "@saleor/app-sdk/settings-manager";
|
||||
import { sendgridTransformV1toV2 } from "./sendgrid-transform-v1-to-v2";
|
||||
import { createLogger } from "@saleor/apps-shared";
|
||||
|
||||
const logger = createLogger({
|
||||
fn: "sendgridConfigMigrationV1ToV2",
|
||||
});
|
||||
|
||||
interface SendgridConfigMigrationV1ToV1Args {
|
||||
settingsManager: SettingsManager;
|
||||
saleorApiUrl: string;
|
||||
}
|
||||
|
||||
export const sendgridConfigMigrationV1ToV2 = async ({
|
||||
settingsManager,
|
||||
saleorApiUrl,
|
||||
}: SendgridConfigMigrationV1ToV1Args) => {
|
||||
logger.debug("Detect if theres data to migrate");
|
||||
|
||||
const appConfigManager = new AppConfigPrivateMetadataManager(settingsManager, saleorApiUrl);
|
||||
const metadataManagerV1 = new SendgridPrivateMetadataManagerV1(settingsManager, saleorApiUrl);
|
||||
|
||||
const configV1 = await metadataManagerV1.getConfig();
|
||||
|
||||
if (!configV1) {
|
||||
logger.debug("No migration required - no previous data");
|
||||
return undefined;
|
||||
}
|
||||
|
||||
logger.debug("Migrating data");
|
||||
const appConfigV1 = await appConfigManager.getConfig();
|
||||
const migratedConfigurationRoot = sendgridTransformV1toV2({ configV1, appConfigV1 });
|
||||
|
||||
logger.debug("Data transformed");
|
||||
return migratedConfigurationRoot;
|
||||
};
|
|
@ -0,0 +1,29 @@
|
|||
import { z } from "zod";
|
||||
import { messageEventTypes } from "../../../event-handlers/message-event-types";
|
||||
|
||||
export const sendgridEventConfigurationV1Schema = z.object({
|
||||
active: z.boolean().default(false),
|
||||
eventType: z.enum(messageEventTypes),
|
||||
template: z.string().optional(),
|
||||
});
|
||||
|
||||
export type SendgridEventConfigurationV1 = z.infer<typeof sendgridEventConfigurationV1Schema>;
|
||||
|
||||
export const sendgridConfigurationV1Schema = z.object({
|
||||
id: z.string().min(1),
|
||||
active: z.boolean().default(true),
|
||||
configurationName: z.string().min(1),
|
||||
sandboxMode: z.boolean().default(false),
|
||||
senderName: z.string().optional(),
|
||||
senderEmail: z.string().optional(),
|
||||
apiKey: z.string().min(1),
|
||||
events: z.array(sendgridEventConfigurationV1Schema),
|
||||
});
|
||||
|
||||
export type SendgridConfigurationV1 = z.infer<typeof sendgridConfigurationV1Schema>;
|
||||
|
||||
export const sendgridConfigV1Schema = z.object({
|
||||
configurations: z.array(sendgridConfigurationV1Schema),
|
||||
});
|
||||
|
||||
export type SendgridConfigV1 = z.infer<typeof sendgridConfigV1Schema>;
|
|
@ -0,0 +1,32 @@
|
|||
import { z } from "zod";
|
||||
import { messageEventTypes } from "../../../event-handlers/message-event-types";
|
||||
import { channelConfigurationSchema } from "../../../channels/channel-configuration-schema";
|
||||
|
||||
export const sendgridConfigurationEventV2Schema = z.object({
|
||||
active: z.boolean().default(false),
|
||||
eventType: z.enum(messageEventTypes),
|
||||
template: z.string().optional(),
|
||||
});
|
||||
|
||||
export type SendgridEventConfigurationV2 = z.infer<typeof sendgridConfigurationEventV2Schema>;
|
||||
|
||||
export const sendgridConfigurationV2Schema = z.object({
|
||||
id: z.string().min(1),
|
||||
active: z.boolean().default(true),
|
||||
name: z.string().min(1),
|
||||
sandboxMode: z.boolean().default(false),
|
||||
apiKey: z.string().min(1),
|
||||
sender: z.string().min(1).optional(),
|
||||
senderEmail: z.string().email().optional(),
|
||||
senderName: z.string().optional(),
|
||||
channels: channelConfigurationSchema,
|
||||
events: z.array(sendgridConfigurationEventV2Schema),
|
||||
});
|
||||
|
||||
export type SendgridConfigurationV2 = z.infer<typeof sendgridConfigurationV2Schema>;
|
||||
|
||||
export const sendgridConfigV2Schema = z.object({
|
||||
configurations: z.array(sendgridConfigurationV2Schema),
|
||||
});
|
||||
|
||||
export type SendgridConfigV2 = z.infer<typeof sendgridConfigV2Schema>;
|
|
@ -0,0 +1,128 @@
|
|||
import { expect, describe, it } from "vitest";
|
||||
import { sendgridTransformV1toV2 } from "./sendgrid-transform-v1-to-v2";
|
||||
|
||||
describe("sendgridTransformV1toV2", function () {
|
||||
it("No configurations, when no defined previously", () => {
|
||||
const migratedConfig = sendgridTransformV1toV2({
|
||||
configV1: {
|
||||
configurations: [],
|
||||
},
|
||||
appConfigV1: undefined,
|
||||
});
|
||||
|
||||
expect(migratedConfig).toEqual({
|
||||
configurations: [],
|
||||
});
|
||||
});
|
||||
|
||||
it("Migrate and do not assign to any channel, when no app configuration passed", () => {
|
||||
const migratedConfig = sendgridTransformV1toV2({
|
||||
configV1: {
|
||||
configurations: [
|
||||
{
|
||||
id: "id",
|
||||
configurationName: "name",
|
||||
active: true,
|
||||
apiKey: "key",
|
||||
sandboxMode: true,
|
||||
senderEmail: "email",
|
||||
senderName: "name",
|
||||
events: [
|
||||
{
|
||||
active: true,
|
||||
eventType: "ORDER_CREATED",
|
||||
template: "template",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
appConfigV1: undefined,
|
||||
});
|
||||
|
||||
expect(migratedConfig).toEqual({
|
||||
configurations: [
|
||||
{
|
||||
id: "id",
|
||||
name: "name",
|
||||
active: true,
|
||||
apiKey: "key",
|
||||
sandboxMode: true,
|
||||
senderEmail: "email",
|
||||
senderName: "name",
|
||||
channels: {
|
||||
override: true,
|
||||
mode: "restrict",
|
||||
channels: [],
|
||||
},
|
||||
events: [
|
||||
{
|
||||
active: true,
|
||||
eventType: "ORDER_CREATED",
|
||||
template: "template",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it("Migrate and assign to channel, when app configuration is passed", () => {
|
||||
const migratedConfig = sendgridTransformV1toV2({
|
||||
configV1: {
|
||||
configurations: [
|
||||
{
|
||||
id: "id",
|
||||
configurationName: "name",
|
||||
active: true,
|
||||
apiKey: "key",
|
||||
sandboxMode: true,
|
||||
senderEmail: "email",
|
||||
senderName: "name",
|
||||
events: [
|
||||
{
|
||||
active: true,
|
||||
eventType: "ORDER_CREATED",
|
||||
template: "template",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
appConfigV1: {
|
||||
configurationsPerChannel: {
|
||||
"default-channel": {
|
||||
active: true,
|
||||
sendgridConfigurationId: "id",
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(migratedConfig).toEqual({
|
||||
configurations: [
|
||||
{
|
||||
id: "id",
|
||||
name: "name",
|
||||
active: true,
|
||||
apiKey: "key",
|
||||
sandboxMode: true,
|
||||
senderEmail: "email",
|
||||
senderName: "name",
|
||||
channels: {
|
||||
override: true,
|
||||
mode: "restrict",
|
||||
channels: ["default-channel"],
|
||||
},
|
||||
events: [
|
||||
{
|
||||
active: true,
|
||||
eventType: "ORDER_CREATED",
|
||||
template: "template",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,33 @@
|
|||
import { AppConfig } from "../../../app-configuration/app-config-schema";
|
||||
import { getChannelsAssignedToConfigId } from "../../../app-configuration/migrations/get-channels-assigned-to-config-id";
|
||||
import { SendgridConfigV1 } from "./sendgrid-config-schema-v1";
|
||||
import { SendgridConfigV2 } from "./sendgrid-config-schema-v2";
|
||||
|
||||
interface SendgridTransformV1toV2Args {
|
||||
configV1: SendgridConfigV1;
|
||||
appConfigV1?: AppConfig;
|
||||
}
|
||||
|
||||
export const sendgridTransformV1toV2 = ({ configV1, appConfigV1 }: SendgridTransformV1toV2Args) => {
|
||||
const migratedConfigurationRoot: SendgridConfigV2 = {
|
||||
configurations: [],
|
||||
};
|
||||
|
||||
configV1.configurations.forEach((config) => {
|
||||
const channels = getChannelsAssignedToConfigId(config.id, "sendgrid", appConfigV1);
|
||||
|
||||
migratedConfigurationRoot.configurations.push({
|
||||
id: config.id,
|
||||
name: config.configurationName,
|
||||
active: config.active,
|
||||
apiKey: config.apiKey,
|
||||
channels,
|
||||
sandboxMode: config.sandboxMode,
|
||||
senderEmail: config.senderEmail,
|
||||
senderName: config.senderName,
|
||||
events: config.events,
|
||||
});
|
||||
});
|
||||
|
||||
return migratedConfigurationRoot;
|
||||
};
|
|
@ -1,32 +1,20 @@
|
|||
import { z } from "zod";
|
||||
import { messageEventTypes } from "../../event-handlers/message-event-types";
|
||||
import { channelConfigurationSchema } from "../../channels/channel-configuration-schema";
|
||||
import {
|
||||
SendgridConfigV2,
|
||||
SendgridConfigurationV2,
|
||||
SendgridEventConfigurationV2,
|
||||
sendgridConfigV2Schema,
|
||||
sendgridConfigurationEventV2Schema,
|
||||
sendgridConfigurationV2Schema,
|
||||
} from "./migrations/sendgrid-config-schema-v2";
|
||||
|
||||
export const sendgridConfigurationEventSchema = z.object({
|
||||
active: z.boolean().default(false),
|
||||
eventType: z.enum(messageEventTypes),
|
||||
template: z.string().optional(),
|
||||
});
|
||||
export const sendgridConfigurationEventSchema = sendgridConfigurationEventV2Schema;
|
||||
|
||||
export type SendgridEventConfiguration = z.infer<typeof sendgridConfigurationEventSchema>;
|
||||
export type SendgridEventConfiguration = SendgridEventConfigurationV2;
|
||||
|
||||
export const sendgridConfigurationSchema = z.object({
|
||||
id: z.string().min(1),
|
||||
active: z.boolean().default(true),
|
||||
name: z.string().min(1),
|
||||
sandboxMode: z.boolean().default(false),
|
||||
apiKey: z.string().min(1),
|
||||
sender: z.string().min(1).optional(),
|
||||
senderEmail: z.string().email().optional(),
|
||||
senderName: z.string().optional(),
|
||||
channels: channelConfigurationSchema,
|
||||
events: z.array(sendgridConfigurationEventSchema),
|
||||
});
|
||||
export const sendgridConfigurationSchema = sendgridConfigurationV2Schema;
|
||||
|
||||
export type SendgridConfiguration = z.infer<typeof sendgridConfigurationSchema>;
|
||||
export type SendgridConfiguration = SendgridConfigurationV2;
|
||||
|
||||
export const sendgridConfigSchema = z.object({
|
||||
configurations: z.array(sendgridConfigurationSchema),
|
||||
});
|
||||
export const sendgridConfigSchema = sendgridConfigV2Schema;
|
||||
|
||||
export type SendgridConfig = z.infer<typeof sendgridConfigSchema>;
|
||||
export type SendgridConfig = SendgridConfigV2;
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { SendgridPrivateMetadataManager } from "./sendgrid-metadata-manager";
|
||||
import { createLogger } from "@saleor/apps-shared";
|
||||
import { sendgridDefaultEmptyConfigurations } from "./sendgrid-default-empty-configurations";
|
||||
import {
|
||||
|
@ -9,6 +8,7 @@ import {
|
|||
import { MessageEventTypes } from "../../event-handlers/message-event-types";
|
||||
import { generateRandomId } from "../../../lib/generate-random-id";
|
||||
import { filterConfigurations } from "../../app-configuration/filter-configurations";
|
||||
import { SendgridPrivateMetadataManager } from "./sendgrid-metadata-manager";
|
||||
|
||||
const logger = createLogger({
|
||||
service: "SendgridConfigurationService",
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
// TODO: MIGRATION CODE FROM CONFIG VERSION V1. REMOVE THIS FILE AFTER MIGRATION
|
||||
|
||||
import { SettingsManager } from "@saleor/app-sdk/settings-manager";
|
||||
import { SendgridConfigV1 } from "./migrations/sendgrid-config-schema-v1";
|
||||
|
||||
export class SendgridPrivateMetadataManagerV1 {
|
||||
private metadataKey = "sendgrid-config";
|
||||
|
||||
constructor(private metadataManager: SettingsManager, private saleorApiUrl: string) {}
|
||||
|
||||
getConfig(): Promise<SendgridConfigV1 | 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: SendgridConfigV1): Promise<void> {
|
||||
return this.metadataManager.set({
|
||||
key: this.metadataKey,
|
||||
value: JSON.stringify(config),
|
||||
domain: this.saleorApiUrl,
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
import { SendgridConfig } from "./sendgrid-config-schema";
|
||||
import { SettingsManager } from "@saleor/app-sdk/settings-manager";
|
||||
import { SendgridConfigV2 } from "./migrations/sendgrid-config-schema-v2";
|
||||
|
||||
export class SendgridPrivateMetadataManagerV2 {
|
||||
private metadataKey = "sendgrid-config-v2";
|
||||
|
||||
constructor(private metadataManager: SettingsManager, private saleorApiUrl: string) {}
|
||||
|
||||
getConfig(): Promise<SendgridConfigV2 | 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: SendgridConfigV2): Promise<void> {
|
||||
return this.metadataManager.set({
|
||||
key: this.metadataKey,
|
||||
value: JSON.stringify(config),
|
||||
domain: this.saleorApiUrl,
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,23 +1,41 @@
|
|||
import { SendgridConfig } from "./sendgrid-config-schema";
|
||||
import { SettingsManager } from "@saleor/app-sdk/settings-manager";
|
||||
import { SendgridPrivateMetadataManagerV2 } from "./sendgrid-metadata-manager-v2";
|
||||
import { sendgridConfigMigrationV1ToV2 } from "./migrations/sendgrid-config-migration-v1-to-v2";
|
||||
import { createLogger } from "@saleor/apps-shared";
|
||||
|
||||
const logger = createLogger({
|
||||
fn: "SendgridPrivateMetadataManager",
|
||||
});
|
||||
|
||||
export class SendgridPrivateMetadataManager {
|
||||
private metadataKey = "sendgrid-config";
|
||||
private metadataKey = "sendgrid-config-v2";
|
||||
|
||||
constructor(private metadataManager: SettingsManager, private saleorApiUrl: string) {}
|
||||
|
||||
getConfig(): Promise<SendgridConfig | undefined> {
|
||||
return this.metadataManager.get(this.metadataKey, this.saleorApiUrl).then((data) => {
|
||||
if (!data) {
|
||||
return data;
|
||||
async getConfig() {
|
||||
logger.debug("Fetching config in the current version");
|
||||
|
||||
const currentVersionManager = new SendgridPrivateMetadataManagerV2(
|
||||
this.metadataManager,
|
||||
this.saleorApiUrl
|
||||
);
|
||||
|
||||
const currentVersionConfig = await currentVersionManager.getConfig();
|
||||
|
||||
if (currentVersionConfig) {
|
||||
// We have the current version, no need to migrate so we can return it
|
||||
return currentVersionConfig;
|
||||
}
|
||||
|
||||
try {
|
||||
return JSON.parse(data);
|
||||
} catch (e) {
|
||||
throw new Error("Invalid metadata value, cant be parsed");
|
||||
}
|
||||
logger.debug("No config in the current version, trying to migrate from v1");
|
||||
// TODO: MIGRATION CODE FROM CONFIG VERSION V1. REMOVE AFTER MIGRATION
|
||||
const migratedSchema = await sendgridConfigMigrationV1ToV2({
|
||||
saleorApiUrl: this.saleorApiUrl,
|
||||
settingsManager: this.metadataManager,
|
||||
});
|
||||
|
||||
return migratedSchema;
|
||||
}
|
||||
|
||||
setConfig(config: SendgridConfig): Promise<void> {
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
import { MessageEventTypes } from "../../../event-handlers/message-event-types";
|
||||
|
||||
export interface MjmlEventConfiguration {
|
||||
active: boolean;
|
||||
eventType: MessageEventTypes;
|
||||
template: string;
|
||||
subject: string;
|
||||
}
|
||||
|
||||
export const smtpEncryptionTypes = ["NONE", "TLS", "SSL"] as const;
|
||||
|
||||
export type SmtpEncryptionType = (typeof smtpEncryptionTypes)[number];
|
||||
|
||||
export interface MjmlConfiguration {
|
||||
id: string;
|
||||
active: boolean;
|
||||
configurationName: string;
|
||||
senderName: string;
|
||||
senderEmail: string;
|
||||
smtpHost: string;
|
||||
smtpPort: string;
|
||||
smtpUser: string;
|
||||
smtpPassword: string;
|
||||
encryption: SmtpEncryptionType;
|
||||
events: MjmlEventConfiguration[];
|
||||
}
|
||||
|
||||
export type MjmlConfig = {
|
||||
configurations: MjmlConfiguration[];
|
||||
};
|
|
@ -0,0 +1,45 @@
|
|||
import { AppConfigPrivateMetadataManager } from "../../../app-configuration/app-config-metadata-manager";
|
||||
import { AppConfig } from "../../../app-configuration/app-config-schema";
|
||||
import { ChannelConfiguration } from "../../../channels/channel-configuration-schema";
|
||||
import { SettingsManager } from "@saleor/app-sdk/settings-manager";
|
||||
import { MjmlPrivateMetadataManager } from "../mjml-metadata-manager";
|
||||
import { SmtpConfigV2 } from "./smtp-config-schema-v2";
|
||||
import { getChannelsAssignedToConfigId } from "../../../app-configuration/migrations/get-channels-assigned-to-config-id";
|
||||
import { smtpTransformV1toV2 } from "./smtp-transform-v1-to-v2";
|
||||
import { createLogger } from "@saleor/apps-shared";
|
||||
|
||||
const logger = createLogger({
|
||||
fn: "smtpConfigMigrationV1ToV2",
|
||||
});
|
||||
|
||||
interface SmtpConfigMigrationV1ToV1Args {
|
||||
settingsManager: SettingsManager;
|
||||
saleorApiUrl: string;
|
||||
}
|
||||
|
||||
export const smtpConfigMigrationV1ToV2 = async ({
|
||||
settingsManager,
|
||||
saleorApiUrl,
|
||||
}: SmtpConfigMigrationV1ToV1Args) => {
|
||||
logger.debug("Hello, I'm migrating smtp config from v1 to v2");
|
||||
|
||||
const appConfigManager = new AppConfigPrivateMetadataManager(settingsManager, saleorApiUrl);
|
||||
const metadataManagerV1 = new MjmlPrivateMetadataManager(settingsManager, saleorApiUrl);
|
||||
|
||||
const configV1 = await metadataManagerV1.getConfig();
|
||||
|
||||
if (!configV1) {
|
||||
logger.debug("No migration required - no previous data");
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const appConfigV1 = await appConfigManager.getConfig();
|
||||
|
||||
const migratedConfigurationRoot = smtpTransformV1toV2({
|
||||
configV1,
|
||||
appConfigV1,
|
||||
});
|
||||
|
||||
logger.debug("Migrated config v1 to v2!");
|
||||
return migratedConfigurationRoot;
|
||||
};
|
|
@ -0,0 +1,37 @@
|
|||
import { z } from "zod";
|
||||
import { messageEventTypes } from "../../../event-handlers/message-event-types";
|
||||
import { channelConfigurationSchema } from "../../../channels/channel-configuration-schema";
|
||||
|
||||
export const smtpEncryptionTypes = ["NONE", "TLS", "SSL"] as const;
|
||||
|
||||
export const smtpConfigurationEventV2Schema = z.object({
|
||||
active: z.boolean().default(false),
|
||||
eventType: z.enum(messageEventTypes),
|
||||
template: z.string(),
|
||||
subject: z.string(),
|
||||
});
|
||||
|
||||
export type SmtpEventConfigurationV2 = z.infer<typeof smtpConfigurationEventV2Schema>;
|
||||
|
||||
export const smtpConfigurationV2Schema = z.object({
|
||||
id: z.string().min(1),
|
||||
active: z.boolean().default(true),
|
||||
name: z.string().min(1),
|
||||
senderName: z.string().optional(),
|
||||
senderEmail: z.string().email().min(5).optional(),
|
||||
smtpHost: z.string().min(1),
|
||||
smtpPort: z.string().min(1),
|
||||
smtpUser: z.string().optional(),
|
||||
smtpPassword: z.string().optional(),
|
||||
encryption: z.enum(smtpEncryptionTypes).default("NONE"),
|
||||
channels: channelConfigurationSchema,
|
||||
events: z.array(smtpConfigurationEventV2Schema),
|
||||
});
|
||||
|
||||
export type SmtpConfigurationV2 = z.infer<typeof smtpConfigurationV2Schema>;
|
||||
|
||||
export const smtpConfigV2Schema = z.object({
|
||||
configurations: z.array(smtpConfigurationV2Schema),
|
||||
});
|
||||
|
||||
export type SmtpConfigV2 = z.infer<typeof smtpConfigV2Schema>;
|
|
@ -0,0 +1,144 @@
|
|||
import { expect, describe, it } from "vitest";
|
||||
import { smtpTransformV1toV2 } from "./smtp-transform-v1-to-v2";
|
||||
|
||||
describe("smtpTransformV1toV2", function () {
|
||||
it("No configurations, when no defined previously", () => {
|
||||
const migratedConfig = smtpTransformV1toV2({
|
||||
configV1: {
|
||||
configurations: [],
|
||||
},
|
||||
appConfigV1: undefined,
|
||||
});
|
||||
|
||||
expect(migratedConfig).toEqual({
|
||||
configurations: [],
|
||||
});
|
||||
});
|
||||
|
||||
it("Migrate and do not assign to any channel, when no app configuration passed", () => {
|
||||
const migratedConfig = smtpTransformV1toV2({
|
||||
configV1: {
|
||||
configurations: [
|
||||
{
|
||||
id: "id",
|
||||
configurationName: "name",
|
||||
active: true,
|
||||
encryption: "NONE",
|
||||
smtpHost: "host",
|
||||
smtpPort: "1234",
|
||||
smtpPassword: "password",
|
||||
smtpUser: "user",
|
||||
senderEmail: "email",
|
||||
senderName: "name",
|
||||
events: [
|
||||
{
|
||||
active: true,
|
||||
eventType: "ORDER_CREATED",
|
||||
template: "template",
|
||||
subject: "subject",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
appConfigV1: undefined,
|
||||
});
|
||||
|
||||
expect(migratedConfig).toEqual({
|
||||
configurations: [
|
||||
{
|
||||
id: "id",
|
||||
name: "name",
|
||||
active: true,
|
||||
encryption: "NONE",
|
||||
smtpHost: "host",
|
||||
smtpPort: "1234",
|
||||
smtpPassword: "password",
|
||||
smtpUser: "user",
|
||||
senderEmail: "email",
|
||||
senderName: "name",
|
||||
events: [
|
||||
{
|
||||
active: true,
|
||||
eventType: "ORDER_CREATED",
|
||||
template: "template",
|
||||
subject: "subject",
|
||||
},
|
||||
],
|
||||
channels: {
|
||||
override: true,
|
||||
mode: "restrict",
|
||||
channels: [],
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it("Migrate and assign to channel, when app configuration is passed", () => {
|
||||
const migratedConfig = smtpTransformV1toV2({
|
||||
configV1: {
|
||||
configurations: [
|
||||
{
|
||||
id: "id",
|
||||
configurationName: "name",
|
||||
active: true,
|
||||
encryption: "NONE",
|
||||
smtpHost: "host",
|
||||
smtpPort: "1234",
|
||||
smtpPassword: "password",
|
||||
smtpUser: "user",
|
||||
senderEmail: "email",
|
||||
senderName: "name",
|
||||
events: [
|
||||
{
|
||||
active: true,
|
||||
eventType: "ORDER_CREATED",
|
||||
template: "template",
|
||||
subject: "subject",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
appConfigV1: {
|
||||
configurationsPerChannel: {
|
||||
"default-channel": {
|
||||
active: true,
|
||||
mjmlConfigurationId: "id",
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(migratedConfig).toEqual({
|
||||
configurations: [
|
||||
{
|
||||
id: "id",
|
||||
name: "name",
|
||||
active: true,
|
||||
encryption: "NONE",
|
||||
smtpHost: "host",
|
||||
smtpPort: "1234",
|
||||
smtpPassword: "password",
|
||||
smtpUser: "user",
|
||||
senderEmail: "email",
|
||||
senderName: "name",
|
||||
events: [
|
||||
{
|
||||
active: true,
|
||||
eventType: "ORDER_CREATED",
|
||||
template: "template",
|
||||
subject: "subject",
|
||||
},
|
||||
],
|
||||
channels: {
|
||||
override: true,
|
||||
mode: "restrict",
|
||||
channels: ["default-channel"],
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,36 @@
|
|||
import { AppConfig } from "../../../app-configuration/app-config-schema";
|
||||
import { getChannelsAssignedToConfigId } from "../../../app-configuration/migrations/get-channels-assigned-to-config-id";
|
||||
import { MjmlConfig } from "./mjml-config-schema-v1";
|
||||
import { SmtpConfigV2 } from "./smtp-config-schema-v2";
|
||||
|
||||
interface SmtpTransformV1toV2Args {
|
||||
configV1: MjmlConfig;
|
||||
appConfigV1?: AppConfig;
|
||||
}
|
||||
|
||||
export const smtpTransformV1toV2 = ({ configV1, appConfigV1 }: SmtpTransformV1toV2Args) => {
|
||||
const migratedConfigurationRoot: SmtpConfigV2 = {
|
||||
configurations: [],
|
||||
};
|
||||
|
||||
configV1.configurations.forEach((config) => {
|
||||
const channels = getChannelsAssignedToConfigId(config.id, "mjml", appConfigV1);
|
||||
|
||||
migratedConfigurationRoot.configurations.push({
|
||||
id: config.id,
|
||||
name: config.configurationName,
|
||||
active: config.active,
|
||||
channels,
|
||||
senderEmail: config.senderEmail,
|
||||
senderName: config.senderName,
|
||||
events: config.events,
|
||||
encryption: config.encryption,
|
||||
smtpHost: config.smtpHost,
|
||||
smtpPort: config.smtpPort,
|
||||
smtpPassword: config.smtpPassword,
|
||||
smtpUser: config.smtpUser,
|
||||
});
|
||||
});
|
||||
|
||||
return migratedConfigurationRoot;
|
||||
};
|
|
@ -0,0 +1,32 @@
|
|||
// TODO: MIGRATION CODE FROM CONFIG VERSION V1. REMOVE THIS FILE AFTER MIGRATION
|
||||
|
||||
import { SettingsManager } from "@saleor/app-sdk/settings-manager";
|
||||
import { MjmlConfig } from "./migrations/mjml-config-schema-v1";
|
||||
|
||||
export class MjmlPrivateMetadataManager {
|
||||
private metadataKey = "mjml-config";
|
||||
|
||||
constructor(private metadataManager: SettingsManager, private saleorApiUrl: string) {}
|
||||
|
||||
getConfig(): Promise<MjmlConfig | 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, can't be parsed");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
setConfig(config: MjmlConfig): Promise<void> {
|
||||
return this.metadataManager.set({
|
||||
key: this.metadataKey,
|
||||
value: JSON.stringify(config),
|
||||
domain: this.saleorApiUrl,
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,37 +1,22 @@
|
|||
import { z } from "zod";
|
||||
import { messageEventTypes } from "../../event-handlers/message-event-types";
|
||||
import { channelConfigurationSchema } from "../../channels/channel-configuration-schema";
|
||||
import {
|
||||
SmtpConfigV2,
|
||||
SmtpConfigurationV2,
|
||||
SmtpEventConfigurationV2,
|
||||
smtpConfigV2Schema,
|
||||
smtpConfigurationEventV2Schema,
|
||||
smtpConfigurationV2Schema,
|
||||
} from "./migrations/smtp-config-schema-v2";
|
||||
|
||||
export const smtpEncryptionTypes = ["NONE", "TLS", "SSL"] as const;
|
||||
|
||||
export const smtpConfigurationEventSchema = z.object({
|
||||
active: z.boolean().default(false),
|
||||
eventType: z.enum(messageEventTypes),
|
||||
template: z.string(),
|
||||
subject: z.string(),
|
||||
});
|
||||
export const smtpConfigurationEventSchema = smtpConfigurationEventV2Schema;
|
||||
|
||||
export type SmtpEventConfiguration = z.infer<typeof smtpConfigurationEventSchema>;
|
||||
export type SmtpEventConfiguration = SmtpEventConfigurationV2;
|
||||
|
||||
export const smtpConfigurationSchema = z.object({
|
||||
id: z.string().min(1),
|
||||
active: z.boolean().default(true),
|
||||
name: z.string().min(1),
|
||||
senderName: z.string().optional(),
|
||||
senderEmail: z.string().email().min(5).optional(),
|
||||
smtpHost: z.string().min(1),
|
||||
smtpPort: z.string().min(1),
|
||||
smtpUser: z.string().optional(),
|
||||
smtpPassword: z.string().optional(),
|
||||
encryption: z.enum(smtpEncryptionTypes).default("NONE"),
|
||||
channels: channelConfigurationSchema,
|
||||
events: z.array(smtpConfigurationEventSchema),
|
||||
});
|
||||
export const smtpConfigurationSchema = smtpConfigurationV2Schema;
|
||||
|
||||
export type SmtpConfiguration = z.infer<typeof smtpConfigurationSchema>;
|
||||
export type SmtpConfiguration = SmtpConfigurationV2;
|
||||
|
||||
export const smtpConfigSchema = z.object({
|
||||
configurations: z.array(smtpConfigurationSchema),
|
||||
});
|
||||
export const smtpConfigSchema = smtpConfigV2Schema;
|
||||
|
||||
export type SmtpConfig = z.infer<typeof smtpConfigSchema>;
|
||||
export type SmtpConfig = SmtpConfigV2;
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
import { SettingsManager } from "@saleor/app-sdk/settings-manager";
|
||||
import { SmtpConfig } from "./smtp-config-schema";
|
||||
|
||||
export class SmtpPrivateMetadataManagerV2 {
|
||||
private metadataKey = "smtp-config-v2";
|
||||
|
||||
constructor(private metadataManager: SettingsManager, private saleorApiUrl: string) {}
|
||||
|
||||
getConfig(): Promise<SmtpConfig | 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, can't be parsed");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
setConfig(config: SmtpConfig): Promise<void> {
|
||||
return this.metadataManager.set({
|
||||
key: this.metadataKey,
|
||||
value: JSON.stringify(config),
|
||||
domain: this.saleorApiUrl,
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,23 +1,41 @@
|
|||
import { SettingsManager } from "@saleor/app-sdk/settings-manager";
|
||||
import { SmtpConfig } from "./smtp-config-schema";
|
||||
import { SmtpPrivateMetadataManagerV2 } from "./smtp-metadata-manager-v2";
|
||||
import { smtpConfigMigrationV1ToV2 } from "./migrations/smtp-config-migration-v1-to-v2";
|
||||
import { createLogger } from "@saleor/apps-shared";
|
||||
|
||||
const logger = createLogger({
|
||||
fn: "SmtpPrivateMetadataManager",
|
||||
});
|
||||
|
||||
export class SmtpPrivateMetadataManager {
|
||||
private metadataKey = "smtp-config";
|
||||
private metadataKey = "smtp-config-v2";
|
||||
|
||||
constructor(private metadataManager: SettingsManager, private saleorApiUrl: string) {}
|
||||
|
||||
getConfig(): Promise<SmtpConfig | undefined> {
|
||||
return this.metadataManager.get(this.metadataKey, this.saleorApiUrl).then((data) => {
|
||||
if (!data) {
|
||||
return data;
|
||||
async getConfig() {
|
||||
logger.debug("Fetching config in the current version");
|
||||
|
||||
const currentVersionManager = new SmtpPrivateMetadataManagerV2(
|
||||
this.metadataManager,
|
||||
this.saleorApiUrl
|
||||
);
|
||||
|
||||
const currentVersionConfig = await currentVersionManager.getConfig();
|
||||
|
||||
if (currentVersionConfig) {
|
||||
// We have the current version, no need to migrate so we can return it
|
||||
return currentVersionConfig;
|
||||
}
|
||||
|
||||
try {
|
||||
return JSON.parse(data);
|
||||
} catch (e) {
|
||||
throw new Error("Invalid metadata value, can't be parsed");
|
||||
}
|
||||
logger.debug("No config in the current version, trying to migrate from v1");
|
||||
// TODO: MIGRATION CODE FROM CONFIG VERSION V1. REMOVE AFTER MIGRATION
|
||||
const migratedSchema = await smtpConfigMigrationV1ToV2({
|
||||
saleorApiUrl: this.saleorApiUrl,
|
||||
settingsManager: this.metadataManager,
|
||||
});
|
||||
|
||||
return migratedSchema;
|
||||
}
|
||||
|
||||
setConfig(config: SmtpConfig): Promise<void> {
|
||||
|
|
|
@ -524,6 +524,9 @@ importers:
|
|||
clsx:
|
||||
specifier: ^1.2.1
|
||||
version: 1.2.1
|
||||
dotenv:
|
||||
specifier: ^16.0.3
|
||||
version: 16.0.3
|
||||
graphql:
|
||||
specifier: ^16.6.0
|
||||
version: 16.6.0
|
||||
|
@ -6167,7 +6170,7 @@ packages:
|
|||
'@graphql-tools/utils': 9.2.1(graphql@16.6.0)
|
||||
'@whatwg-node/fetch': 0.7.0(@types/node@18.13.0)
|
||||
graphql: 16.6.0
|
||||
tslib: 2.5.0
|
||||
tslib: 2.5.2
|
||||
transitivePeerDependencies:
|
||||
- '@types/node'
|
||||
- encoding
|
||||
|
@ -6194,7 +6197,7 @@ packages:
|
|||
'@graphql-tools/utils': 9.2.1(graphql@16.6.0)
|
||||
globby: 11.1.0
|
||||
graphql: 16.6.0
|
||||
tslib: 2.5.0
|
||||
tslib: 2.5.2
|
||||
unixify: 1.0.0
|
||||
transitivePeerDependencies:
|
||||
- '@babel/core'
|
||||
|
@ -6309,7 +6312,7 @@ packages:
|
|||
graphql: 16.6.0
|
||||
is-glob: 4.0.3
|
||||
micromatch: 4.0.5
|
||||
tslib: 2.5.0
|
||||
tslib: 2.5.2
|
||||
unixify: 1.0.0
|
||||
transitivePeerDependencies:
|
||||
- '@babel/core'
|
||||
|
@ -6344,7 +6347,7 @@ packages:
|
|||
'@graphql-tools/utils': 9.2.1(graphql@16.6.0)
|
||||
'@whatwg-node/fetch': 0.7.0(@types/node@18.13.0)
|
||||
graphql: 16.6.0
|
||||
tslib: 2.5.0
|
||||
tslib: 2.5.2
|
||||
transitivePeerDependencies:
|
||||
- '@babel/core'
|
||||
- '@types/node'
|
||||
|
@ -6361,7 +6364,7 @@ packages:
|
|||
'@graphql-tools/utils': 9.2.1(graphql@16.6.0)
|
||||
globby: 11.1.0
|
||||
graphql: 16.6.0
|
||||
tslib: 2.5.0
|
||||
tslib: 2.5.2
|
||||
unixify: 1.0.0
|
||||
dev: true
|
||||
|
||||
|
@ -6401,7 +6404,7 @@ packages:
|
|||
'@graphql-tools/utils': 9.2.1(graphql@16.6.0)
|
||||
globby: 11.1.0
|
||||
graphql: 16.6.0
|
||||
tslib: 2.5.0
|
||||
tslib: 2.5.2
|
||||
unixify: 1.0.0
|
||||
dev: true
|
||||
|
||||
|
@ -6414,7 +6417,7 @@ packages:
|
|||
'@graphql-tools/utils': 9.2.1(graphql@16.6.0)
|
||||
graphql: 16.6.0
|
||||
p-limit: 3.1.0
|
||||
tslib: 2.5.0
|
||||
tslib: 2.5.2
|
||||
dev: true
|
||||
|
||||
/@graphql-tools/merge@8.3.18(graphql@16.6.0):
|
||||
|
@ -6492,7 +6495,7 @@ packages:
|
|||
jsonwebtoken: 9.0.0
|
||||
lodash: 4.17.21
|
||||
scuid: 1.1.0
|
||||
tslib: 2.5.0
|
||||
tslib: 2.5.2
|
||||
yaml-ast-parser: 0.0.43
|
||||
transitivePeerDependencies:
|
||||
- '@types/node'
|
||||
|
@ -6586,7 +6589,7 @@ packages:
|
|||
graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0
|
||||
dependencies:
|
||||
graphql: 16.6.0
|
||||
tslib: 2.4.1
|
||||
tslib: 2.5.2
|
||||
dev: true
|
||||
|
||||
/@graphql-tools/utils@9.2.1(graphql@16.6.0):
|
||||
|
@ -13744,7 +13747,6 @@ packages:
|
|||
/dotenv@16.0.3:
|
||||
resolution: {integrity: sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==}
|
||||
engines: {node: '>=12'}
|
||||
dev: true
|
||||
|
||||
/dotenv@8.6.0:
|
||||
resolution: {integrity: sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==}
|
||||
|
@ -14436,7 +14438,7 @@ packages:
|
|||
peerDependencies:
|
||||
eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8
|
||||
dependencies:
|
||||
'@babel/runtime': 7.20.13
|
||||
'@babel/runtime': 7.22.3
|
||||
aria-query: 5.1.3
|
||||
array-includes: 3.1.6
|
||||
array.prototype.flatmap: 1.3.1
|
||||
|
@ -16075,7 +16077,7 @@ packages:
|
|||
jiti: 1.17.1
|
||||
minimatch: 4.2.3
|
||||
string-env-interpolation: 1.0.1
|
||||
tslib: 2.5.0
|
||||
tslib: 2.5.2
|
||||
transitivePeerDependencies:
|
||||
- '@types/node'
|
||||
- bufferutil
|
||||
|
|
Loading…
Reference in a new issue