restore invoices

This commit is contained in:
Lukasz Ostrowski 2023-09-07 16:59:20 +02:00
parent 7fa1648349
commit bbc7a7f8f9
20 changed files with 574 additions and 57 deletions

View file

@ -50,7 +50,6 @@
"@graphql-codegen/typescript-operations": "4.0.1", "@graphql-codegen/typescript-operations": "4.0.1",
"@graphql-codegen/typescript-urql": "3.7.3", "@graphql-codegen/typescript-urql": "3.7.3",
"@graphql-typed-document-node/core": "3.2.0", "@graphql-typed-document-node/core": "3.2.0",
"@total-typescript/ts-reset": "^0.5.1",
"@types/react": "18.2.5", "@types/react": "18.2.5",
"@types/react-dom": "18.2.5", "@types/react-dom": "18.2.5",
"@types/rimraf": "^3.0.2", "@types/rimraf": "^3.0.2",

View file

@ -0,0 +1,41 @@
/* eslint-disable turbo/no-undeclared-env-vars */
import { createGraphQLClient } from "@saleor/apps-shared";
import { createSettingsManager } from "../../src/modules/app-configuration/metadata-manager";
import { SaleorCloudAPL } from "@saleor/app-sdk/APL";
export const getMetadataManagerForEnv = (apiUrl: string, appToken: string) => {
const client = createGraphQLClient({
saleorApiUrl: apiUrl,
token: appToken,
});
return createSettingsManager(client);
};
export const safeParse = (json?: string) => {
if (!json) return null;
try {
return JSON.parse(json);
} catch (e) {
return null;
}
};
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();
};

View file

@ -0,0 +1,4 @@
export const MigrationV1toV2Consts = {
appConfigV2metadataKey: "app-config-v2",
appConfigV1metadataKey: "app-config",
};

View file

@ -0,0 +1,3 @@
Run `npx tsx run-report.ts` to print report (dry-run)
Run `npx tsx run-migration.ts` to migrate
Run `npx tsx restore-migration.ts` to restore migration (remove metadata v2)

View file

@ -0,0 +1,46 @@
/* eslint-disable turbo/no-undeclared-env-vars */
import * as dotenv from "dotenv";
import { fetchCloudAplEnvs, verifyRequiredEnvs } from "../migration-utils";
import { RemoveMetadataDocument } from "../../../generated/graphql";
import { MigrationV1toV2Consts } from "./const";
import { createGraphQLClient } from "@saleor/apps-shared";
dotenv.config();
const runMigration = async () => {
verifyRequiredEnvs();
const allEnvs = await fetchCloudAplEnvs();
const results = await Promise.all(
allEnvs.map((env) => {
const client = createGraphQLClient({
saleorApiUrl: env.saleorApiUrl,
token: env.token,
});
return client
.mutation(RemoveMetadataDocument, {
id: env.appId,
keys: [MigrationV1toV2Consts.appConfigV2metadataKey],
})
.toPromise()
.then((r) => {
if (r.error) {
console.error("❌ Error removing metadata", r.error.message);
throw r.error.message;
}
return r;
})
.catch((e) => {
console.error("❌ Error removing metadata", e);
});
}),
);
console.log(results);
};
runMigration();

View file

@ -0,0 +1,67 @@
/* eslint-disable turbo/no-undeclared-env-vars */
import * as dotenv from "dotenv";
import {
fetchCloudAplEnvs,
getMetadataManagerForEnv,
safeParse,
verifyRequiredEnvs,
} from "../migration-utils";
import { ConfigV1ToV2Transformer } from "../../../src/modules/app-configuration/schema-v2/config-v1-to-v2-transformer";
import { AppConfigV2MetadataManager } from "../../../src/modules/app-configuration/schema-v2/app-config-v2-metadata-manager";
import { AppConfigV2 } from "../../../src/modules/app-configuration/schema-v2/app-config";
import { MigrationV1toV2Consts } from "./const";
dotenv.config();
const runMigration = async () => {
verifyRequiredEnvs();
const allEnvs = await fetchCloudAplEnvs();
const results = await Promise.all(
allEnvs.map((env) => {
const metadataManager = getMetadataManagerForEnv(env.saleorApiUrl, env.token);
return Promise.all([
metadataManager.get(MigrationV1toV2Consts.appConfigV1metadataKey, env.saleorApiUrl),
metadataManager.get(MigrationV1toV2Consts.appConfigV2metadataKey),
])
.then(([v1, v2]) => {
if (v2 && v2 !== "undefined") {
console.log("▶️ v2 already exists for ", env.saleorApiUrl);
return;
}
if (!v1) {
console.log("🚫 v1 does not exist for ", env.saleorApiUrl);
return new AppConfigV2MetadataManager(metadataManager)
.set(new AppConfigV2().serialize())
.then((r) => {
console.log(`✅ created empty config for ${env.saleorApiUrl}`);
})
.catch((e) => {
console.log(
`🚫 failed to create empty config for ${env.saleorApiUrl}. Env may not exist.`,
e.message,
);
});
}
const v2Config = new ConfigV1ToV2Transformer().transform(JSON.parse(v1));
return new AppConfigV2MetadataManager(metadataManager)
.set(v2Config.serialize())
.then((r) => {
console.log(`✅ migrated ${env.saleorApiUrl}`);
});
})
.catch((e) => {
console.error("🚫 Failed to migrate ", env.saleorApiUrl, e);
});
}),
);
};
runMigration();

View file

@ -0,0 +1,61 @@
/* eslint-disable turbo/no-undeclared-env-vars */
import * as dotenv from "dotenv";
import {
fetchCloudAplEnvs,
getMetadataManagerForEnv,
safeParse,
verifyRequiredEnvs,
} from "../migration-utils";
import { MigrationV1toV2Consts } from "./const";
dotenv.config();
const runReport = async () => {
verifyRequiredEnvs();
const allEnvs = await fetchCloudAplEnvs().catch((r) => {
console.error(r);
process.exit(1);
});
const results = await Promise.all(
allEnvs.map((env) => {
const metadataManager = getMetadataManagerForEnv(env.saleorApiUrl, env.token);
return Promise.all([
metadataManager.get(MigrationV1toV2Consts.appConfigV1metadataKey, env.saleorApiUrl),
metadataManager.get(MigrationV1toV2Consts.appConfigV2metadataKey),
])
.then(([v1, v2]) => {
return {
schemaV1: safeParse(v1),
schemaV2: safeParse(v2),
};
})
.then((metadata) => ({
metadata: metadata,
env: env.saleorApiUrl,
}));
}),
);
const report = results.map((r: any) => ({
env: r.env,
hasV1: !!r.metadata.schemaV1,
hasV2: !!r.metadata.schemaV2,
}));
const notMigratedCount = report.reduce((acc: number, curr: any) => {
if (!curr.hasV2) {
return acc + 1;
}
return acc;
}, 0);
console.table(report);
console.log(`Envs left to migrate: ${notMigratedCount}`);
};
runReport();

View file

@ -1,6 +1,6 @@
import { ShopAddress } from "../modules/shop-info/shop-address"; import { SellerShopConfig } from "../modules/app-configuration/schema-v1/app-config-v1";
export const getMockAddress = (): ShopAddress => { export const getMockAddress = (): SellerShopConfig["address"] => {
return { return {
city: "Wrocław", city: "Wrocław",
cityArea: "", cityArea: "",

View file

@ -3,10 +3,11 @@ import { z } from "zod";
import { protectedClientProcedure } from "../trpc/protected-client-procedure"; import { protectedClientProcedure } from "../trpc/protected-client-procedure";
import { router } from "../trpc/trpc-server"; import { router } from "../trpc/trpc-server";
import { createSettingsManager } from "./metadata-manager"; import { createSettingsManager } from "./metadata-manager";
import { AppConfigV2 } from "./schema-v2/app-config";
import { AddressV2Schema } from "./schema-v2/app-config-schema.v2";
import { AppConfigV2MetadataManager } from "./schema-v2/app-config-v2-metadata-manager"; import { AppConfigV2MetadataManager } from "./schema-v2/app-config-v2-metadata-manager";
import { GetAppConfigurationV2Service } from "./schema-v2/get-app-configuration.v2.service"; import { GetAppConfigurationV2Service } from "./schema-v2/get-app-configuration.v2.service";
import { ConfigV1ToV2MigrationService } from "./schema-v2/config-v1-to-v2-migration.service";
import { AddressV2Schema } from "./schema-v2/app-config-schema.v2";
import { AppConfigV2 } from "./schema-v2/app-config";
const UpsertAddressSchema = z.object({ const UpsertAddressSchema = z.object({
address: AddressV2Schema, address: AddressV2Schema,

View file

@ -0,0 +1,23 @@
import { SellerAddress } from "../address";
/**
* @deprecated
* Remove when SchemaV1 is migrated to SchemaV2
*/
export interface SellerShopConfig {
address: SellerAddress;
}
/**
* @deprecated
* Remove when SchemaV1 is migrated to SchemaV2
*/
export type ShopConfigPerChannelSlug = Record<string, SellerShopConfig>;
/**
* @deprecated
* Remove when SchemaV1 is migrated to SchemaV2
*/
export type AppConfigV1 = {
shopConfigPerChannel: ShopConfigPerChannelSlug;
};

View file

@ -0,0 +1,46 @@
import { AppConfigV1 } from "./app-config-v1";
import { SettingsManager } from "@saleor/app-sdk/settings-manager";
/**
* @deprecated
* Remove when SchemaV1 is migrated to SchemaV2
*/
export interface AppConfigurator {
setConfig(config: AppConfigV1): Promise<void>;
getConfig(): Promise<AppConfigV1 | undefined>;
}
/**
* @deprecated
* Remove when SchemaV1 is migrated to SchemaV2
*/
export class PrivateMetadataAppConfiguratorV1 implements AppConfigurator {
private metadataKey = "app-config";
constructor(
private metadataManager: SettingsManager,
private saleorApiUrl: string,
) {}
getConfig(): Promise<AppConfigV1 | 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: AppConfigV1): Promise<void> {
return this.metadataManager.set({
key: this.metadataKey,
value: JSON.stringify(config),
domain: this.saleorApiUrl,
});
}
}

View file

@ -4,7 +4,7 @@ import { z } from "zod";
export class AppConfigV2 { export class AppConfigV2 {
private rootData: AppConfigV2Shape = { channelsOverrides: {} }; private rootData: AppConfigV2Shape = { channelsOverrides: {} };
constructor(initialData?: AppConfigV2Shape | unknown) { constructor(initialData?: AppConfigV2Shape) {
if (initialData) { if (initialData) {
this.rootData = AppConfigV2Schema.parse(initialData); this.rootData = AppConfigV2Schema.parse(initialData);
} }

View file

@ -0,0 +1,95 @@
import { beforeEach, describe, expect, it, vi } from "vitest";
import { ConfigV1ToV2MigrationService } from "./config-v1-to-v2-migration.service";
import { SimpleGraphqlClient } from "../metadata-manager";
import { getMockAddress } from "../../../fixtures/mock-address";
import { AppConfigV2 } from "./app-config";
describe("config-v1-to-v2-migration.service", () => {
const mockClient: SimpleGraphqlClient = {
mutation: vi.fn(),
query: vi.fn(),
};
let service: ConfigV1ToV2MigrationService;
beforeEach(() => {
vi.resetAllMocks();
service = new ConfigV1ToV2MigrationService(mockClient, "https://example.com/graphql/");
vi.spyOn(service.configMetadataManager, "set").mockImplementationOnce(async () =>
Promise.resolve(),
);
});
it("Returns a pure V2 config if V1 config is not present", async () => {
vi.spyOn(service.metadataV1AppConfigurator, "getConfig").mockImplementationOnce(async () =>
Promise.resolve(undefined),
);
const migrationResult = await service.migrate();
expect(migrationResult.getChannelsOverrides()).toEqual({});
expect(service.configMetadataManager.set).toHaveBeenCalledWith(migrationResult.serialize());
});
it("Returns a migrated V2 config from V1 if V1 config is present", async () => {
vi.spyOn(service.metadataV1AppConfigurator, "getConfig").mockImplementationOnce(async () =>
Promise.resolve({
shopConfigPerChannel: {
"default-channel": {
address: getMockAddress(),
},
},
}),
);
const migrationResult = await service.migrate();
expect(migrationResult.getChannelsOverrides()).toEqual(
expect.objectContaining({
"default-channel": expect.objectContaining(getMockAddress()),
}),
);
});
it("Runs a beforeSave callback and saves modified state in metadata - missing v1 config scenario", async () => {
vi.spyOn(service.metadataV1AppConfigurator, "getConfig").mockImplementationOnce(async () =>
Promise.resolve(undefined),
);
const beforeSaveCb = vi.fn().mockImplementationOnce((config: AppConfigV2) => {
config.upsertOverride("test", getMockAddress());
});
const migrationResult = await service.migrate(beforeSaveCb);
expect(migrationResult.getChannelsOverrides()).toEqual({
test: expect.objectContaining(getMockAddress()),
});
expect(service.configMetadataManager.set).toHaveBeenCalledWith(migrationResult.serialize());
expect(beforeSaveCb).toHaveBeenCalledWith(migrationResult);
});
it("Runs a beforeSave callback and saves modified state in metadata - present v1 config scenario", async () => {
vi.spyOn(service.metadataV1AppConfigurator, "getConfig").mockImplementationOnce(async () =>
Promise.resolve({
shopConfigPerChannel: {
"default-channel": {
address: getMockAddress(),
},
},
}),
);
const beforeSaveCb = vi.fn().mockImplementationOnce((config: AppConfigV2) => {
config.removeOverride("default-channel");
});
const migrationResult = await service.migrate(beforeSaveCb);
expect(migrationResult.getChannelsOverrides()).toEqual({});
expect(service.configMetadataManager.set).toHaveBeenCalledWith(migrationResult.serialize());
expect(beforeSaveCb).toHaveBeenCalledWith(migrationResult);
});
});

View file

@ -0,0 +1,57 @@
import { PrivateMetadataAppConfiguratorV1 } from "../schema-v1/app-configurator";
import { createSettingsManager, SimpleGraphqlClient } from "../metadata-manager";
import { AppConfigV2 } from "./app-config";
import { ConfigV1ToV2Transformer } from "./config-v1-to-v2-transformer";
import { AppConfigV2MetadataManager } from "./app-config-v2-metadata-manager";
import { SettingsManager } from "@saleor/app-sdk/settings-manager";
export class ConfigV1ToV2MigrationService {
settingsManager: SettingsManager;
configMetadataManager: AppConfigV2MetadataManager;
metadataV1AppConfigurator: PrivateMetadataAppConfiguratorV1;
constructor(
private client: SimpleGraphqlClient,
private saleorApiUrl: string,
) {
this.settingsManager = createSettingsManager(client);
this.configMetadataManager = new AppConfigV2MetadataManager(this.settingsManager);
this.metadataV1AppConfigurator = new PrivateMetadataAppConfiguratorV1(
this.settingsManager,
this.saleorApiUrl,
);
}
async migrate(beforeSave?: (config: AppConfigV2) => void): Promise<AppConfigV2> {
const v1Config = await this.metadataV1AppConfigurator.getConfig();
/**
* If no v1 config, it means clean install - return pure config
*/
if (!v1Config) {
const pureConfig = new AppConfigV2();
if (beforeSave) {
beforeSave(pureConfig);
}
await this.configMetadataManager.set(pureConfig.serialize());
return pureConfig;
}
/**
* Otherwise, transform v1 config to v2 and save it
*/
const transformer = new ConfigV1ToV2Transformer();
const appConfigV2FromV1 = transformer.transform(v1Config);
if (beforeSave) {
beforeSave(appConfigV2FromV1);
}
await this.configMetadataManager.set(appConfigV2FromV1.serialize());
return appConfigV2FromV1;
}
}

View file

@ -0,0 +1,72 @@
import { describe, expect, it } from "vitest";
import { ConfigV1ToV2Transformer } from "./config-v1-to-v2-transformer";
import { getMockAddress } from "../../../fixtures/mock-address";
describe("ConfigV1ToV2Transformer", function () {
it("Returns empty V2 instance if config is null", () => {
// @ts-expect-error
const v2 = new ConfigV1ToV2Transformer().transform(null);
expect(v2.serialize()).toMatchInlineSnapshot('"{\\"channelsOverrides\\":{}}"');
});
it("Maps V1 address overrides to V2 - single channel override", () => {
const v2 = new ConfigV1ToV2Transformer().transform({
shopConfigPerChannel: {
"default-channel": {
address: getMockAddress(),
},
},
});
expect(v2.getChannelsOverrides()).toEqual(
expect.objectContaining({
"default-channel": getMockAddress(),
}),
);
});
it("Maps V1 address overrides to V2 - multiple channels override", () => {
const v2 = new ConfigV1ToV2Transformer().transform({
shopConfigPerChannel: {
"default-channel": {
address: getMockAddress(),
},
"custom-channel": {
address: getMockAddress(),
},
},
});
expect(v2.getChannelsOverrides()).toEqual(
expect.objectContaining({
"default-channel": getMockAddress(),
"custom-channel": getMockAddress(),
}),
);
});
it("Falls back to empty string for address property if not set", () => {
const addressMock = getMockAddress();
// @ts-expect-error
delete addressMock.city;
const v2 = new ConfigV1ToV2Transformer().transform({
shopConfigPerChannel: {
"default-channel": {
address: addressMock,
},
},
});
expect(v2.getChannelsOverrides()).toEqual(
expect.objectContaining({
"default-channel": {
...getMockAddress(),
city: "",
},
}),
);
});
});

View file

@ -0,0 +1,29 @@
import { AppConfigV1 } from "../schema-v1/app-config-v1";
import { AppConfigV2 } from "./app-config";
export class ConfigV1ToV2Transformer {
transform(v1Config: AppConfigV1): AppConfigV2 {
const configV2 = new AppConfigV2();
if (!v1Config || !v1Config.shopConfigPerChannel) {
return configV2;
}
Object.entries(v1Config.shopConfigPerChannel).forEach(([channelSlug, channelConfigV1]) => {
const addressV1 = channelConfigV1.address;
configV2.upsertOverride(channelSlug, {
city: addressV1.city ?? "",
country: addressV1.country ?? "",
streetAddress2: addressV1.streetAddress2 ?? "",
postalCode: addressV1.postalCode ?? "",
companyName: addressV1.companyName ?? "",
streetAddress1: addressV1.streetAddress1 ?? "",
countryArea: addressV1.countryArea ?? "",
cityArea: addressV1.cityArea ?? "",
});
});
return configV2;
}
}

View file

@ -1,11 +1,11 @@
import { OrderPayloadFragment } from "../../../../generated/graphql"; import { OrderPayloadFragment } from "../../../../generated/graphql";
import { ShopAddress } from "../../shop-info/shop-address"; import { SellerShopConfig } from "../../app-configuration/schema-v1/app-config-v1";
export interface InvoiceGenerator { export interface InvoiceGenerator {
generate(input: { generate(input: {
order: OrderPayloadFragment; order: OrderPayloadFragment;
invoiceNumber: string; invoiceNumber: string;
filename: string; filename: string;
companyAddressData: ShopAddress; companyAddressData: SellerShopConfig["address"];
}): Promise<void>; }): Promise<void>;
} }

View file

@ -1,6 +1,7 @@
import { OrderPayloadFragment } from "../../../../../generated/graphql";
import { AddressV2Shape } from "../../../app-configuration/schema-v2/app-config-schema.v2";
import { InvoiceGenerator } from "../invoice-generator"; import { InvoiceGenerator } from "../invoice-generator";
import { Order, OrderPayloadFragment } from "../../../../../generated/graphql";
import { SellerShopConfig } from "../../../app-configuration/schema-v1/app-config-v1";
import { AddressV2Shape } from "../../../app-configuration/schema-v2/app-config-schema.v2";
const Microinvoice = require("microinvoice"); const Microinvoice = require("microinvoice");
export class MicroinvoiceInvoiceGenerator implements InvoiceGenerator { export class MicroinvoiceInvoiceGenerator implements InvoiceGenerator {
@ -18,17 +19,7 @@ export class MicroinvoiceInvoiceGenerator implements InvoiceGenerator {
const { invoiceNumber, order, companyAddressData, filename } = input; const { invoiceNumber, order, companyAddressData, filename } = input;
const microinvoiceInstance = new Microinvoice({ const microinvoiceInstance = new Microinvoice({
style: { style: {},
/*
* header: {
* image: {
* path: "./examples/logo.png",
* width: 50,
* height: 19,
* },
* },
*/
},
data: { data: {
invoice: { invoice: {
name: `Invoice ${invoiceNumber}`, name: `Invoice ${invoiceNumber}`,
@ -62,12 +53,6 @@ export class MicroinvoiceInvoiceGenerator implements InvoiceGenerator {
order.billingAddress?.country.country, order.billingAddress?.country.country,
], ],
}, },
/*
* {
* label: "Tax Identifier",
* value: "todo",
* },
*/
], ],
seller: [ seller: [
@ -83,28 +68,9 @@ export class MicroinvoiceInvoiceGenerator implements InvoiceGenerator {
companyAddressData.countryArea, companyAddressData.countryArea,
], ],
}, },
/*
* {
* label: "Tax Identifier",
* value: "todo",
* },
*/
], ],
legal: [ legal: [],
/*
* {
* value: "Lorem ipsum dolor sit amet, consectetur adipiscing elit",
* weight: "bold",
* color: "primary",
* },
* {
* value: "sed do eiusmod tempor incididunt ut labore et dolore magna.",
* weight: "bold",
* color: "secondary",
* },
*/
],
details: { details: {
header: [ header: [

View file

@ -1,25 +1,30 @@
import { SALEOR_API_URL_HEADER } from "@saleor/app-sdk/const";
import { NextWebhookApiHandler, SaleorAsyncWebhook } from "@saleor/app-sdk/handlers/next"; import { NextWebhookApiHandler, SaleorAsyncWebhook } from "@saleor/app-sdk/handlers/next";
import { createGraphQLClient, createLogger } from "@saleor/apps-shared";
import { gql } from "urql"; import { gql } from "urql";
import { saleorApp } from "../../../saleor-app";
import { import {
InvoiceRequestedPayloadFragment, InvoiceRequestedPayloadFragment,
OrderPayloadFragment, OrderPayloadFragment,
} from "../../../../generated/graphql"; } from "../../../../generated/graphql";
import { AddressV2Shape } from "../../../modules/app-configuration/schema-v2/app-config-schema.v2"; import { SaleorInvoiceUploader } from "../../../modules/invoices/invoice-uploader/saleor-invoice-uploader";
import { GetAppConfigurationV2Service } from "../../../modules/app-configuration/schema-v2/get-app-configuration.v2.service";
import { InvoiceCreateNotifier } from "../../../modules/invoices/invoice-create-notifier/invoice-create-notifier"; import { InvoiceCreateNotifier } from "../../../modules/invoices/invoice-create-notifier/invoice-create-notifier";
import { hashInvoiceFilename } from "../../../modules/invoices/invoice-file-name/hash-invoice-filename";
import { resolveTempPdfFileLocation } from "../../../modules/invoices/invoice-file-name/resolve-temp-pdf-file-location";
import { MicroinvoiceInvoiceGenerator } from "../../../modules/invoices/invoice-generator/microinvoice/microinvoice-invoice-generator";
import { import {
InvoiceNumberGenerationStrategy, InvoiceNumberGenerationStrategy,
InvoiceNumberGenerator, InvoiceNumberGenerator,
} from "../../../modules/invoices/invoice-number-generator/invoice-number-generator"; } from "../../../modules/invoices/invoice-number-generator/invoice-number-generator";
import { SaleorInvoiceUploader } from "../../../modules/invoices/invoice-uploader/saleor-invoice-uploader"; import { MicroinvoiceInvoiceGenerator } from "../../../modules/invoices/invoice-generator/microinvoice/microinvoice-invoice-generator";
import { hashInvoiceFilename } from "../../../modules/invoices/invoice-file-name/hash-invoice-filename";
import { resolveTempPdfFileLocation } from "../../../modules/invoices/invoice-file-name/resolve-temp-pdf-file-location";
import { createGraphQLClient, createLogger } from "@saleor/apps-shared";
import { SALEOR_API_URL_HEADER } from "@saleor/app-sdk/const";
import { GetAppConfigurationV2Service } from "../../../modules/app-configuration/schema-v2/get-app-configuration.v2.service";
import { ShopInfoFetcher } from "../../../modules/shop-info/shop-info-fetcher"; import { ShopInfoFetcher } from "../../../modules/shop-info/shop-info-fetcher";
import { z } from "zod";
import {
AddressV2Schema,
AddressV2Shape,
} from "../../../modules/app-configuration/schema-v2/app-config-schema.v2";
import { ConfigV1ToV2MigrationService } from "../../../modules/app-configuration/schema-v2/config-v1-to-v2-migration.service";
import { shopInfoQueryToAddressShape } from "../../../modules/shop-info/shop-info-query-to-address-shape"; import { shopInfoQueryToAddressShape } from "../../../modules/shop-info/shop-info-query-to-address-shape";
import { saleorApp } from "../../../saleor-app";
import * as Sentry from "@sentry/nextjs"; import * as Sentry from "@sentry/nextjs";
import { AppConfigV2 } from "../../../modules/app-configuration/schema-v2/app-config"; import { AppConfigV2 } from "../../../modules/app-configuration/schema-v2/app-config";

View file

@ -67,6 +67,7 @@
"vals", "vals",
"urql", "urql",
"Protos", "Protos",
"Consts",
"pino", "pino",
"IFRAME", "IFRAME",
"dedupe" "dedupe"
@ -82,6 +83,7 @@
"**/*.spec.ts", "**/*.spec.ts",
"**/graphql.ts", "**/graphql.ts",
"**/CHANGELOG.md", "**/CHANGELOG.md",
"**/schema.graphql" "**/schema.graphql",
"**/*mock*"
] ]
} }