fix/split providers (#271)
* refactor: ♻️ add explicit return to services * refactor: ♻️ use provider get method * refactor: ♻️ move obfuscation logic to router & separate public service * build: 💚 add changeset
This commit is contained in:
parent
56a4dbb3a3
commit
b46a9f3e70
14 changed files with 154 additions and 209 deletions
5
.changeset/yellow-bottles-jog.md
Normal file
5
.changeset/yellow-bottles-jog.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
"saleor-app-taxes": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Fix the create provider error. Add explicit return types to configuration services. Move obfuscating logic to routers and public services.
|
|
@ -1,4 +1,5 @@
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
import { obfuscateSecret } from "../../lib/utils";
|
||||||
|
|
||||||
export const avataxConfigSchema = z.object({
|
export const avataxConfigSchema = z.object({
|
||||||
name: z.string().min(1, { message: "Name requires at least one character." }),
|
name: z.string().min(1, { message: "Name requires at least one character." }),
|
||||||
|
@ -27,3 +28,15 @@ export const avataxInstanceConfigSchema = z.object({
|
||||||
});
|
});
|
||||||
|
|
||||||
export type AvataxInstanceConfig = z.infer<typeof avataxInstanceConfigSchema>;
|
export type AvataxInstanceConfig = z.infer<typeof avataxInstanceConfigSchema>;
|
||||||
|
|
||||||
|
export const obfuscateAvataxConfig = (config: AvataxConfig) => ({
|
||||||
|
...config,
|
||||||
|
username: obfuscateSecret(config.username),
|
||||||
|
password: obfuscateSecret(config.password),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const obfuscateAvataxInstances = (instances: AvataxInstanceConfig[]) =>
|
||||||
|
instances.map((instance) => ({
|
||||||
|
...instance,
|
||||||
|
config: obfuscateAvataxConfig(instance.config),
|
||||||
|
}));
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { logger as pinoLogger } from "../../lib/logger";
|
import { logger as pinoLogger } from "../../lib/logger";
|
||||||
|
import { isObfuscated } from "../../lib/utils";
|
||||||
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 { avataxConfigSchema } from "./avatax-config";
|
import { avataxConfigSchema, obfuscateAvataxConfig } from "./avatax-config";
|
||||||
import { AvataxConfigurationService } from "./avatax-configuration.service";
|
import { AvataxConfigurationService } from "./avatax-configuration.service";
|
||||||
|
|
||||||
const getInputSchema = z.object({
|
const getInputSchema = z.object({
|
||||||
|
@ -15,12 +16,14 @@ const deleteInputSchema = z.object({
|
||||||
|
|
||||||
const patchInputSchema = z.object({
|
const patchInputSchema = z.object({
|
||||||
id: z.string(),
|
id: z.string(),
|
||||||
value: avataxConfigSchema.partial(),
|
value: avataxConfigSchema.partial().transform((c) => {
|
||||||
});
|
const { username, password, ...config } = c ?? {};
|
||||||
|
return {
|
||||||
const putInputSchema = z.object({
|
...config,
|
||||||
id: z.string(),
|
...(username && !isObfuscated(username) && { username }),
|
||||||
value: avataxConfigSchema,
|
...(password && !isObfuscated(password) && { password }),
|
||||||
|
};
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
const postInputSchema = z.object({
|
const postInputSchema = z.object({
|
||||||
|
@ -43,7 +46,7 @@ export const avataxConfigurationRouter = router({
|
||||||
|
|
||||||
logger.debug({ result }, "avataxConfigurationRouter.get finished");
|
logger.debug({ result }, "avataxConfigurationRouter.get finished");
|
||||||
|
|
||||||
return result;
|
return { ...result, config: obfuscateAvataxConfig(result.config) };
|
||||||
}),
|
}),
|
||||||
post: protectedClientProcedure.input(postInputSchema).mutation(async ({ ctx, input }) => {
|
post: protectedClientProcedure.input(postInputSchema).mutation(async ({ ctx, input }) => {
|
||||||
const logger = pinoLogger.child({
|
const logger = pinoLogger.child({
|
||||||
|
@ -94,23 +97,6 @@ export const avataxConfigurationRouter = router({
|
||||||
|
|
||||||
logger.debug({ result }, "avataxConfigurationRouter.patch finished");
|
logger.debug({ result }, "avataxConfigurationRouter.patch finished");
|
||||||
|
|
||||||
return result;
|
|
||||||
}),
|
|
||||||
put: protectedClientProcedure.input(putInputSchema).mutation(async ({ ctx, input }) => {
|
|
||||||
const logger = pinoLogger.child({
|
|
||||||
saleorApiUrl: ctx.saleorApiUrl,
|
|
||||||
procedure: "avataxConfigurationRouter.put",
|
|
||||||
});
|
|
||||||
|
|
||||||
logger.debug("avataxConfigurationRouter.put called");
|
|
||||||
|
|
||||||
const { apiClient, saleorApiUrl } = ctx;
|
|
||||||
const avataxConfigurationService = new AvataxConfigurationService(apiClient, saleorApiUrl);
|
|
||||||
|
|
||||||
const result = await avataxConfigurationService.put(input.id, input.value);
|
|
||||||
|
|
||||||
logger.debug({ result }, "avataxConfigurationRouter.put finished");
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,54 +1,14 @@
|
||||||
import pino from "pino";
|
import pino from "pino";
|
||||||
import { Client } from "urql";
|
import { Client } from "urql";
|
||||||
import { createLogger } from "../../lib/logger";
|
import { createLogger } from "../../lib/logger";
|
||||||
import { isObfuscated, obfuscateSecret } from "../../lib/utils";
|
|
||||||
import { createSettingsManager } from "../app-configuration/metadata-manager";
|
import { createSettingsManager } from "../app-configuration/metadata-manager";
|
||||||
import { CrudSettingsConfigurator } from "../crud-settings/crud-settings.service";
|
import { CrudSettingsConfigurator } from "../crud-settings/crud-settings.service";
|
||||||
import { providersSchema } from "../providers-configuration/providers-config";
|
import { providersSchema } from "../providers-configuration/providers-config";
|
||||||
import { TAX_PROVIDER_KEY } from "../providers-configuration/providers-configuration-service";
|
import { TAX_PROVIDER_KEY } from "../providers-configuration/public-providers-configuration-service";
|
||||||
import { AvataxClient } from "./avatax-client";
|
import { AvataxClient } from "./avatax-client";
|
||||||
import {
|
import { AvataxConfig, AvataxInstanceConfig, avataxInstanceConfigSchema } from "./avatax-config";
|
||||||
AvataxConfig,
|
|
||||||
avataxConfigSchema,
|
|
||||||
AvataxInstanceConfig,
|
|
||||||
avataxInstanceConfigSchema,
|
|
||||||
} from "./avatax-config";
|
|
||||||
|
|
||||||
const obfuscateConfig = (config: AvataxConfig) => ({
|
|
||||||
...config,
|
|
||||||
username: obfuscateSecret(config.username),
|
|
||||||
password: obfuscateSecret(config.password),
|
|
||||||
});
|
|
||||||
|
|
||||||
const obfuscateProvidersConfig = (instances: AvataxInstanceConfig[]) =>
|
|
||||||
instances.map((instance) => ({
|
|
||||||
...instance,
|
|
||||||
config: obfuscateConfig(instance.config),
|
|
||||||
}));
|
|
||||||
|
|
||||||
const getSchema = avataxInstanceConfigSchema.transform((instance) => ({
|
|
||||||
...instance,
|
|
||||||
config: obfuscateConfig(instance.config),
|
|
||||||
}));
|
|
||||||
|
|
||||||
const patchSchema = avataxConfigSchema.partial().transform((c) => {
|
|
||||||
const { username, password, ...config } = c ?? {};
|
|
||||||
return {
|
|
||||||
...config,
|
|
||||||
...(username && !isObfuscated(username) && { username }),
|
|
||||||
...(password && !isObfuscated(password) && { password }),
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
const putSchema = avataxConfigSchema.transform((c) => {
|
|
||||||
const { username, password, ...config } = c;
|
|
||||||
return {
|
|
||||||
...config,
|
|
||||||
...(!isObfuscated(username) && { username }),
|
|
||||||
...(!isObfuscated(password) && { password }),
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
|
const getSchema = avataxInstanceConfigSchema;
|
||||||
export class AvataxConfigurationService {
|
export class AvataxConfigurationService {
|
||||||
private crudSettingsConfigurator: CrudSettingsConfigurator;
|
private crudSettingsConfigurator: CrudSettingsConfigurator;
|
||||||
private logger: pino.Logger;
|
private logger: pino.Logger;
|
||||||
|
@ -65,7 +25,7 @@ export class AvataxConfigurationService {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async getAll() {
|
async getAll(): Promise<AvataxInstanceConfig[]> {
|
||||||
this.logger.debug(".getAll called");
|
this.logger.debug(".getAll called");
|
||||||
const { data } = await this.crudSettingsConfigurator.readAll();
|
const { data } = await this.crudSettingsConfigurator.readAll();
|
||||||
const validation = providersSchema.safeParse(data);
|
const validation = providersSchema.safeParse(data);
|
||||||
|
@ -79,10 +39,10 @@ export class AvataxConfigurationService {
|
||||||
(instance) => instance.provider === "avatax"
|
(instance) => instance.provider === "avatax"
|
||||||
) as AvataxInstanceConfig[];
|
) as AvataxInstanceConfig[];
|
||||||
|
|
||||||
return obfuscateProvidersConfig(instances);
|
return instances;
|
||||||
}
|
}
|
||||||
|
|
||||||
async get(id: string) {
|
async get(id: string): Promise<AvataxInstanceConfig> {
|
||||||
this.logger.debug(`.get called with id: ${id}`);
|
this.logger.debug(`.get called with id: ${id}`);
|
||||||
const { data } = await this.crudSettingsConfigurator.read(id);
|
const { data } = await this.crudSettingsConfigurator.read(id);
|
||||||
this.logger.debug({ setting: data }, `Fetched setting from crudSettingsConfigurator`);
|
this.logger.debug({ setting: data }, `Fetched setting from crudSettingsConfigurator`);
|
||||||
|
@ -97,7 +57,7 @@ export class AvataxConfigurationService {
|
||||||
return validation.data;
|
return validation.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
async post(config: AvataxConfig) {
|
async post(config: AvataxConfig): Promise<{ id: string }> {
|
||||||
this.logger.debug(`.post called with value: ${JSON.stringify(config)}`);
|
this.logger.debug(`.post called with value: ${JSON.stringify(config)}`);
|
||||||
const avataxClient = new AvataxClient(config);
|
const avataxClient = new AvataxClient(config);
|
||||||
const validation = await avataxClient.ping();
|
const validation = await avataxClient.ping();
|
||||||
|
@ -106,45 +66,40 @@ export class AvataxConfigurationService {
|
||||||
this.logger.error(validation.error);
|
this.logger.error(validation.error);
|
||||||
throw new Error(validation.error);
|
throw new Error(validation.error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const result = await this.crudSettingsConfigurator.create({
|
||||||
|
provider: "avatax",
|
||||||
|
config: config,
|
||||||
|
});
|
||||||
|
|
||||||
|
return result.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
async patch(id: string, config: Partial<AvataxConfig>) {
|
async patch(id: string, config: Partial<AvataxConfig>): Promise<void> {
|
||||||
this.logger.debug(`.patch called with id: ${id} and value: ${JSON.stringify(config)}`);
|
this.logger.debug(`.patch called with id: ${id} and value: ${JSON.stringify(config)}`);
|
||||||
const result = await this.get(id);
|
const data = await this.get(id);
|
||||||
// omit the key "id" from the result
|
// omit the key "id" from the result
|
||||||
const { id: _, ...setting } = result;
|
const { id: _, ...setting } = data;
|
||||||
const validation = patchSchema.safeParse(config);
|
|
||||||
|
|
||||||
if (!validation.success) {
|
|
||||||
this.logger.error({ error: validation.error.format() }, "Validation error while patch");
|
|
||||||
throw new Error(validation.error.message);
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.crudSettingsConfigurator.update(id, {
|
return this.crudSettingsConfigurator.update(id, {
|
||||||
...setting,
|
...setting,
|
||||||
config: { ...setting.config, ...validation.data },
|
config: { ...setting.config, ...config },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async put(id: string, config: AvataxConfig) {
|
async put(id: string, config: AvataxConfig): Promise<void> {
|
||||||
const result = await this.get(id);
|
const data = await this.get(id);
|
||||||
// omit the key "id" from the result
|
// omit the key "id" from the result
|
||||||
const { id: _, ...setting } = result;
|
const { id: _, ...setting } = data;
|
||||||
const validation = putSchema.safeParse(config);
|
|
||||||
|
|
||||||
if (!validation.success) {
|
|
||||||
this.logger.error({ error: validation.error.format() }, "Validation error while patch");
|
|
||||||
throw new Error(validation.error.message);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.logger.debug(`.put called with id: ${id} and value: ${JSON.stringify(config)}`);
|
this.logger.debug(`.put called with id: ${id} and value: ${JSON.stringify(config)}`);
|
||||||
return this.crudSettingsConfigurator.update(id, {
|
return this.crudSettingsConfigurator.update(id, {
|
||||||
...setting,
|
...setting,
|
||||||
config: { ...validation.data },
|
config: { ...config },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async delete(id: string) {
|
async delete(id: string): Promise<void> {
|
||||||
this.logger.debug(`.delete called with id: ${id}`);
|
this.logger.debug(`.delete called with id: ${id}`);
|
||||||
return this.crudSettingsConfigurator.delete(id);
|
return this.crudSettingsConfigurator.delete(id);
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,10 +13,10 @@ import { Button, makeStyles } from "@saleor/macaw-ui";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { Controller, useForm } from "react-hook-form";
|
import { Controller, useForm } from "react-hook-form";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
import { useInstanceId } from "../../taxes/tax-context";
|
||||||
import { trpcClient } from "../../trpc/trpc-client";
|
import { trpcClient } from "../../trpc/trpc-client";
|
||||||
import { AppLink } from "../../ui/app-link";
|
import { AppLink } from "../../ui/app-link";
|
||||||
import { useInstanceId } from "../../taxes/tax-context";
|
import { avataxConfigSchema } from "../avatax-config";
|
||||||
import { avataxConfigSchema, avataxInstanceConfigSchema } from "../avatax-config";
|
|
||||||
|
|
||||||
const useStyles = makeStyles((theme) => ({
|
const useStyles = makeStyles((theme) => ({
|
||||||
reverseRow: {
|
reverseRow: {
|
||||||
|
@ -59,8 +59,12 @@ export const AvataxConfigurationForm = () => {
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const { data: providersConfig, refetch: refetchProvidersConfigurationData } =
|
const { refetch: refetchProvidersConfigurationData } =
|
||||||
trpcClient.providersConfiguration.getAll.useQuery(undefined, {
|
trpcClient.providersConfiguration.getAll.useQuery();
|
||||||
|
const { data: instance } = trpcClient.avataxConfiguration.get.useQuery(
|
||||||
|
{ id: instanceId ?? "" },
|
||||||
|
{
|
||||||
|
enabled: !!instanceId,
|
||||||
onError(error) {
|
onError(error) {
|
||||||
appBridge?.dispatch(
|
appBridge?.dispatch(
|
||||||
actions.Notification({
|
actions.Notification({
|
||||||
|
@ -70,9 +74,8 @@ export const AvataxConfigurationForm = () => {
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
});
|
}
|
||||||
|
);
|
||||||
const instance = providersConfig?.find((instance) => instance.id === instanceId);
|
|
||||||
|
|
||||||
const resetInstanceId = () => {
|
const resetInstanceId = () => {
|
||||||
setInstanceId(null);
|
setInstanceId(null);
|
||||||
|
@ -89,7 +92,7 @@ export const AvataxConfigurationForm = () => {
|
||||||
|
|
||||||
const { mutate: createMutation, isLoading: isCreateLoading } =
|
const { mutate: createMutation, isLoading: isCreateLoading } =
|
||||||
trpcClient.avataxConfiguration.post.useMutation({
|
trpcClient.avataxConfiguration.post.useMutation({
|
||||||
onSuccess({ data: { id } }) {
|
onSuccess({ id }) {
|
||||||
setInstanceId(id);
|
setInstanceId(id);
|
||||||
refetchProvidersConfigurationData();
|
refetchProvidersConfigurationData();
|
||||||
appBridge?.dispatch(
|
appBridge?.dispatch(
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { TaxJarConfigurationService } from "../taxjar/taxjar-configuration.servi
|
||||||
|
|
||||||
export const TAX_PROVIDER_KEY = "tax-providers";
|
export const TAX_PROVIDER_KEY = "tax-providers";
|
||||||
|
|
||||||
export class TaxProvidersConfigurationService {
|
export class PrivateTaxProvidersConfigurationService {
|
||||||
private avataxConfigurationService: AvataxConfigurationService;
|
private avataxConfigurationService: AvataxConfigurationService;
|
||||||
private taxJarConfigurationService: TaxJarConfigurationService;
|
private taxJarConfigurationService: TaxJarConfigurationService;
|
||||||
private logger: pino.Logger;
|
private logger: pino.Logger;
|
||||||
|
@ -14,7 +14,7 @@ export class TaxProvidersConfigurationService {
|
||||||
this.avataxConfigurationService = new AvataxConfigurationService(client, saleorApiUrl);
|
this.avataxConfigurationService = new AvataxConfigurationService(client, saleorApiUrl);
|
||||||
this.taxJarConfigurationService = new TaxJarConfigurationService(client, saleorApiUrl);
|
this.taxJarConfigurationService = new TaxJarConfigurationService(client, saleorApiUrl);
|
||||||
this.logger = createLogger({
|
this.logger = createLogger({
|
||||||
service: "TaxProvidersConfigurationService",
|
service: "PrivateTaxProvidersConfigurationService",
|
||||||
metadataKey: TAX_PROVIDER_KEY,
|
metadataKey: TAX_PROVIDER_KEY,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,6 @@ export class TaxProvidersConfigurationService {
|
||||||
this.logger.debug(".getAll called");
|
this.logger.debug(".getAll called");
|
||||||
const taxJar = await this.taxJarConfigurationService.getAll();
|
const taxJar = await this.taxJarConfigurationService.getAll();
|
||||||
const avatax = await this.avataxConfigurationService.getAll();
|
const avatax = await this.avataxConfigurationService.getAll();
|
||||||
// todo: add more clever way of joining the two. Maybe add updated_at date to the config and use it to sort?
|
|
||||||
return [...taxJar, ...avatax];
|
return [...taxJar, ...avatax];
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
import { logger as pinoLogger } from "../../lib/logger";
|
import { logger as pinoLogger } from "../../lib/logger";
|
||||||
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 { TaxProvidersConfigurationService } from "./providers-configuration-service";
|
import { PublicTaxProvidersConfigurationService } from "./public-providers-configuration-service";
|
||||||
|
|
||||||
export const providersConfigurationRouter = router({
|
export const providersConfigurationRouter = router({
|
||||||
getAll: protectedClientProcedure.query(async ({ ctx }) => {
|
getAll: protectedClientProcedure.query(async ({ ctx }) => {
|
||||||
|
@ -9,6 +9,6 @@ export const providersConfigurationRouter = router({
|
||||||
|
|
||||||
logger.debug("providersConfigurationRouter.fetch called");
|
logger.debug("providersConfigurationRouter.fetch called");
|
||||||
|
|
||||||
return new TaxProvidersConfigurationService(ctx.apiClient, ctx.saleorApiUrl).getAll();
|
return new PublicTaxProvidersConfigurationService(ctx.apiClient, ctx.saleorApiUrl).getAll();
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
import pino from "pino";
|
||||||
|
import { Client } from "urql";
|
||||||
|
import { createLogger } from "../../lib/logger";
|
||||||
|
import { obfuscateAvataxInstances } from "../avatax/avatax-config";
|
||||||
|
import { AvataxConfigurationService } from "../avatax/avatax-configuration.service";
|
||||||
|
import { obfuscateTaxJarInstances } from "../taxjar/taxjar-config";
|
||||||
|
import { TaxJarConfigurationService } from "../taxjar/taxjar-configuration.service";
|
||||||
|
|
||||||
|
export const TAX_PROVIDER_KEY = "tax-providers";
|
||||||
|
|
||||||
|
export class PublicTaxProvidersConfigurationService {
|
||||||
|
private avataxConfigurationService: AvataxConfigurationService;
|
||||||
|
private taxJarConfigurationService: TaxJarConfigurationService;
|
||||||
|
private logger: pino.Logger;
|
||||||
|
constructor(client: Client, saleorApiUrl: string) {
|
||||||
|
this.avataxConfigurationService = new AvataxConfigurationService(client, saleorApiUrl);
|
||||||
|
this.taxJarConfigurationService = new TaxJarConfigurationService(client, saleorApiUrl);
|
||||||
|
this.logger = createLogger({
|
||||||
|
service: "PublicTaxProvidersConfigurationService",
|
||||||
|
metadataKey: TAX_PROVIDER_KEY,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async getAll() {
|
||||||
|
this.logger.debug(".getAll called");
|
||||||
|
const taxJar = await this.taxJarConfigurationService.getAll();
|
||||||
|
const avatax = await this.avataxConfigurationService.getAll();
|
||||||
|
return [...obfuscateTaxJarInstances(taxJar), ...obfuscateAvataxInstances(avatax)];
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,3 +21,14 @@ export const taxJarInstanceConfigSchema = z.object({
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TaxJarInstanceConfig = z.infer<typeof taxJarInstanceConfigSchema>;
|
export type TaxJarInstanceConfig = z.infer<typeof taxJarInstanceConfigSchema>;
|
||||||
|
|
||||||
|
export const obfuscateTaxJarConfig = (config: TaxJarConfig) => ({
|
||||||
|
...config,
|
||||||
|
apiKey: obfuscateSecret(config.apiKey),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const obfuscateTaxJarInstances = (instances: TaxJarInstanceConfig[]) =>
|
||||||
|
instances.map((instance) => ({
|
||||||
|
...instance,
|
||||||
|
config: obfuscateTaxJarConfig(instance.config),
|
||||||
|
}));
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { logger as pinoLogger } from "../../lib/logger";
|
import { logger as pinoLogger } from "../../lib/logger";
|
||||||
|
import { isObfuscated } from "../../lib/utils";
|
||||||
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 { taxJarConfigSchema } from "./taxjar-config";
|
import { obfuscateTaxJarConfig, taxJarConfigSchema } from "./taxjar-config";
|
||||||
import { TaxJarConfigurationService } from "./taxjar-configuration.service";
|
import { TaxJarConfigurationService } from "./taxjar-configuration.service";
|
||||||
|
|
||||||
const getInputSchema = z.object({
|
const getInputSchema = z.object({
|
||||||
|
@ -15,12 +16,13 @@ const deleteInputSchema = z.object({
|
||||||
|
|
||||||
const patchInputSchema = z.object({
|
const patchInputSchema = z.object({
|
||||||
id: z.string(),
|
id: z.string(),
|
||||||
value: taxJarConfigSchema.partial(),
|
value: taxJarConfigSchema.partial().transform((c) => {
|
||||||
});
|
const { apiKey, ...config } = c ?? {};
|
||||||
|
return {
|
||||||
const putInputSchema = z.object({
|
...config,
|
||||||
id: z.string(),
|
...(apiKey && !isObfuscated(apiKey) && { apiKey }),
|
||||||
value: taxJarConfigSchema,
|
};
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
const postInputSchema = z.object({
|
const postInputSchema = z.object({
|
||||||
|
@ -43,7 +45,7 @@ export const taxjarConfigurationRouter = router({
|
||||||
|
|
||||||
logger.debug({ result }, "taxjarConfigurationRouter.get finished");
|
logger.debug({ result }, "taxjarConfigurationRouter.get finished");
|
||||||
|
|
||||||
return result;
|
return { ...result, config: obfuscateTaxJarConfig(result.config) };
|
||||||
}),
|
}),
|
||||||
post: protectedClientProcedure.input(postInputSchema).mutation(async ({ ctx, input }) => {
|
post: protectedClientProcedure.input(postInputSchema).mutation(async ({ ctx, input }) => {
|
||||||
const logger = pinoLogger.child({
|
const logger = pinoLogger.child({
|
||||||
|
@ -94,23 +96,6 @@ export const taxjarConfigurationRouter = router({
|
||||||
|
|
||||||
logger.debug({ result }, "taxjarConfigurationRouter.patch finished");
|
logger.debug({ result }, "taxjarConfigurationRouter.patch finished");
|
||||||
|
|
||||||
return result;
|
|
||||||
}),
|
|
||||||
put: protectedClientProcedure.input(putInputSchema).mutation(async ({ ctx, input }) => {
|
|
||||||
const logger = pinoLogger.child({
|
|
||||||
saleorApiUrl: ctx.saleorApiUrl,
|
|
||||||
procedure: "taxjarConfigurationRouter.put",
|
|
||||||
});
|
|
||||||
|
|
||||||
logger.debug("taxjarConfigurationRouter.put called");
|
|
||||||
|
|
||||||
const { apiClient, saleorApiUrl } = ctx;
|
|
||||||
const taxjarConfigurationService = new TaxJarConfigurationService(apiClient, saleorApiUrl);
|
|
||||||
|
|
||||||
const result = await taxjarConfigurationService.put(input.id, input.value);
|
|
||||||
|
|
||||||
logger.debug({ result }, "taxjarConfigurationRouter.put finished");
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,50 +1,14 @@
|
||||||
import pino from "pino";
|
import pino from "pino";
|
||||||
import { Client } from "urql";
|
import { Client } from "urql";
|
||||||
import { createLogger } from "../../lib/logger";
|
import { createLogger } from "../../lib/logger";
|
||||||
import { isObfuscated, obfuscateSecret } from "../../lib/utils";
|
|
||||||
import { createSettingsManager } from "../app-configuration/metadata-manager";
|
import { createSettingsManager } from "../app-configuration/metadata-manager";
|
||||||
import { CrudSettingsConfigurator } from "../crud-settings/crud-settings.service";
|
import { CrudSettingsConfigurator } from "../crud-settings/crud-settings.service";
|
||||||
import { providersSchema } from "../providers-configuration/providers-config";
|
import { providersSchema } from "../providers-configuration/providers-config";
|
||||||
import { TAX_PROVIDER_KEY } from "../providers-configuration/providers-configuration-service";
|
import { TAX_PROVIDER_KEY } from "../providers-configuration/public-providers-configuration-service";
|
||||||
import { TaxJarClient } from "./taxjar-client";
|
import { TaxJarClient } from "./taxjar-client";
|
||||||
import {
|
import { TaxJarConfig, TaxJarInstanceConfig, taxJarInstanceConfigSchema } from "./taxjar-config";
|
||||||
TaxJarConfig,
|
|
||||||
taxJarConfigSchema,
|
|
||||||
TaxJarInstanceConfig,
|
|
||||||
taxJarInstanceConfigSchema,
|
|
||||||
} from "./taxjar-config";
|
|
||||||
|
|
||||||
const obfuscateConfig = (config: TaxJarConfig) => ({
|
const getSchema = taxJarInstanceConfigSchema;
|
||||||
...config,
|
|
||||||
apiKey: obfuscateSecret(config.apiKey),
|
|
||||||
});
|
|
||||||
|
|
||||||
const obfuscateProvidersConfig = (instances: TaxJarInstanceConfig[]) =>
|
|
||||||
instances.map((instance) => ({
|
|
||||||
...instance,
|
|
||||||
config: obfuscateConfig(instance.config),
|
|
||||||
}));
|
|
||||||
|
|
||||||
const getSchema = taxJarInstanceConfigSchema.transform((instance) => ({
|
|
||||||
...instance,
|
|
||||||
config: obfuscateConfig(instance.config),
|
|
||||||
}));
|
|
||||||
|
|
||||||
const patchSchema = taxJarConfigSchema.partial().transform((c) => {
|
|
||||||
const { apiKey, ...config } = c ?? {};
|
|
||||||
return {
|
|
||||||
...config,
|
|
||||||
...(apiKey && !isObfuscated(apiKey) && { apiKey }),
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
const putSchema = taxJarConfigSchema.transform((c) => {
|
|
||||||
const { apiKey, ...config } = c;
|
|
||||||
return {
|
|
||||||
...config,
|
|
||||||
...(!isObfuscated(apiKey) && { apiKey }),
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
export class TaxJarConfigurationService {
|
export class TaxJarConfigurationService {
|
||||||
private crudSettingsConfigurator: CrudSettingsConfigurator;
|
private crudSettingsConfigurator: CrudSettingsConfigurator;
|
||||||
|
@ -62,7 +26,7 @@ export class TaxJarConfigurationService {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async getAll() {
|
async getAll(): Promise<TaxJarInstanceConfig[]> {
|
||||||
this.logger.debug(".getAll called");
|
this.logger.debug(".getAll called");
|
||||||
const { data } = await this.crudSettingsConfigurator.readAll();
|
const { data } = await this.crudSettingsConfigurator.readAll();
|
||||||
this.logger.debug({ settings: data }, `Fetched settings from crudSettingsConfigurator`);
|
this.logger.debug({ settings: data }, `Fetched settings from crudSettingsConfigurator`);
|
||||||
|
@ -77,12 +41,12 @@ export class TaxJarConfigurationService {
|
||||||
(instance) => instance.provider === "taxjar"
|
(instance) => instance.provider === "taxjar"
|
||||||
) as TaxJarInstanceConfig[];
|
) as TaxJarInstanceConfig[];
|
||||||
|
|
||||||
return obfuscateProvidersConfig(instances);
|
return instances;
|
||||||
}
|
}
|
||||||
|
|
||||||
async get(id: string) {
|
async get(id: string): Promise<TaxJarInstanceConfig> {
|
||||||
this.logger.debug(`.get called with id: ${id}`);
|
this.logger.debug(`.get called with id: ${id}`);
|
||||||
const { data } = await this.crudSettingsConfigurator.read(id);
|
const data = await this.crudSettingsConfigurator.read(id);
|
||||||
this.logger.debug({ setting: data }, `Fetched setting from crudSettingsConfigurator`);
|
this.logger.debug({ setting: data }, `Fetched setting from crudSettingsConfigurator`);
|
||||||
|
|
||||||
const validation = getSchema.safeParse(data);
|
const validation = getSchema.safeParse(data);
|
||||||
|
@ -95,7 +59,7 @@ export class TaxJarConfigurationService {
|
||||||
return validation.data;
|
return validation.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
async post(config: TaxJarConfig) {
|
async post(config: TaxJarConfig): Promise<{ id: string }> {
|
||||||
this.logger.debug(`.post called with value: ${JSON.stringify(config)}`);
|
this.logger.debug(`.post called with value: ${JSON.stringify(config)}`);
|
||||||
const taxJarClient = new TaxJarClient(config);
|
const taxJarClient = new TaxJarClient(config);
|
||||||
const validation = await taxJarClient.ping();
|
const validation = await taxJarClient.ping();
|
||||||
|
@ -104,49 +68,39 @@ export class TaxJarConfigurationService {
|
||||||
this.logger.error({ error: validation.error }, "Validation error while post");
|
this.logger.error({ error: validation.error }, "Validation error while post");
|
||||||
throw new Error(validation.error);
|
throw new Error(validation.error);
|
||||||
}
|
}
|
||||||
return this.crudSettingsConfigurator.create({
|
const result = await this.crudSettingsConfigurator.create({
|
||||||
provider: "taxjar",
|
provider: "taxjar",
|
||||||
config: config,
|
config: config,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return result.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
async patch(id: string, config: Partial<TaxJarConfig>) {
|
async patch(id: string, config: Partial<TaxJarConfig>): Promise<void> {
|
||||||
this.logger.debug(`.patch called with id: ${id} and value: ${JSON.stringify(config)}`);
|
this.logger.debug(`.patch called with id: ${id} and value: ${JSON.stringify(config)}`);
|
||||||
const result = await this.get(id);
|
const data = await this.get(id);
|
||||||
// omit the key "id" from the result
|
// omit the key "id" from the result
|
||||||
const { id: _, ...setting } = result;
|
const { id: _, ...setting } = data;
|
||||||
const validation = patchSchema.safeParse(config);
|
|
||||||
|
|
||||||
if (!validation.success) {
|
|
||||||
this.logger.error({ error: validation.error.format() }, "Validation error while patch");
|
|
||||||
throw new Error(validation.error.message);
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.crudSettingsConfigurator.update(id, {
|
return this.crudSettingsConfigurator.update(id, {
|
||||||
...setting,
|
...setting,
|
||||||
config: { ...setting.config, ...validation.data },
|
config: { ...setting.config, ...config },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async put(id: string, config: TaxJarConfig) {
|
async put(id: string, config: TaxJarConfig): Promise<void> {
|
||||||
const result = await this.get(id);
|
const data = await this.get(id);
|
||||||
// omit the key "id" from the result
|
// omit the key "id" from the result
|
||||||
const { id: _, ...setting } = result;
|
const { id: _, ...setting } = data;
|
||||||
const validation = putSchema.safeParse(config);
|
|
||||||
|
|
||||||
if (!validation.success) {
|
|
||||||
this.logger.error({ error: validation.error.format() }, "Validation error while patch");
|
|
||||||
throw new Error(validation.error.message);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.logger.debug(`.put called with id: ${id} and value: ${JSON.stringify(config)}`);
|
this.logger.debug(`.put called with id: ${id} and value: ${JSON.stringify(config)}`);
|
||||||
return this.crudSettingsConfigurator.update(id, {
|
return this.crudSettingsConfigurator.update(id, {
|
||||||
...setting,
|
...setting,
|
||||||
config: { ...validation.data },
|
config: { ...config },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async delete(id: string) {
|
async delete(id: string): Promise<void> {
|
||||||
this.logger.debug(`.delete called with id: ${id}`);
|
this.logger.debug(`.delete called with id: ${id}`);
|
||||||
return this.crudSettingsConfigurator.delete(id);
|
return this.crudSettingsConfigurator.delete(id);
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,9 +13,9 @@ import { Button, makeStyles } from "@saleor/macaw-ui";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { Controller, useForm } from "react-hook-form";
|
import { Controller, useForm } from "react-hook-form";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { trpcClient } from "../../trpc/trpc-client";
|
|
||||||
import { useInstanceId } from "../../taxes/tax-context";
|
import { useInstanceId } from "../../taxes/tax-context";
|
||||||
import { taxJarConfigSchema, taxJarInstanceConfigSchema } from "../taxjar-config";
|
import { trpcClient } from "../../trpc/trpc-client";
|
||||||
|
import { taxJarConfigSchema } from "../taxjar-config";
|
||||||
|
|
||||||
const useStyles = makeStyles((theme) => ({
|
const useStyles = makeStyles((theme) => ({
|
||||||
reverseRow: {
|
reverseRow: {
|
||||||
|
@ -61,8 +61,12 @@ export const TaxJarConfigurationForm = () => {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const { data: providersConfigurationData, refetch: refetchProvidersConfigurationData } =
|
const { refetch: refetchProvidersConfigurationData } =
|
||||||
trpcClient.providersConfiguration.getAll.useQuery(undefined, {
|
trpcClient.providersConfiguration.getAll.useQuery();
|
||||||
|
const { data: instance } = trpcClient.avataxConfiguration.get.useQuery(
|
||||||
|
{ id: instanceId ?? "" },
|
||||||
|
{
|
||||||
|
enabled: !!instanceId,
|
||||||
onError(error) {
|
onError(error) {
|
||||||
appBridge?.dispatch(
|
appBridge?.dispatch(
|
||||||
actions.Notification({
|
actions.Notification({
|
||||||
|
@ -72,13 +76,12 @@ export const TaxJarConfigurationForm = () => {
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
});
|
}
|
||||||
|
);
|
||||||
const instance = providersConfigurationData?.find((instance) => instance.id === instanceId);
|
|
||||||
|
|
||||||
const { mutate: createMutation, isLoading: isCreateLoading } =
|
const { mutate: createMutation, isLoading: isCreateLoading } =
|
||||||
trpcClient.taxJarConfiguration.post.useMutation({
|
trpcClient.taxJarConfiguration.post.useMutation({
|
||||||
onSuccess({ data: { id } }) {
|
onSuccess({ id }) {
|
||||||
setInstanceId(id);
|
setInstanceId(id);
|
||||||
refetchProvidersConfigurationData();
|
refetchProvidersConfigurationData();
|
||||||
refetchChannelConfigurationData();
|
refetchChannelConfigurationData();
|
||||||
|
|
|
@ -5,7 +5,8 @@ import { createClient } from "../../../lib/graphql";
|
||||||
import { createLogger } from "../../../lib/logger";
|
import { createLogger } from "../../../lib/logger";
|
||||||
import { calculateTaxesPayloadSchema, ExpectedWebhookPayload } from "../../../lib/saleor/schema";
|
import { calculateTaxesPayloadSchema, ExpectedWebhookPayload } from "../../../lib/saleor/schema";
|
||||||
import { GetChannelsConfigurationService } from "../../../modules/channels-configuration/get-channels-configuration.service";
|
import { GetChannelsConfigurationService } from "../../../modules/channels-configuration/get-channels-configuration.service";
|
||||||
import { TaxProvidersConfigurationService } from "../../../modules/providers-configuration/providers-configuration-service";
|
import { PrivateTaxProvidersConfigurationService } from "../../../modules/providers-configuration/private-providers-configuration-service";
|
||||||
|
|
||||||
import { ActiveTaxProvider } from "../../../modules/taxes/active-tax-provider";
|
import { ActiveTaxProvider } from "../../../modules/taxes/active-tax-provider";
|
||||||
|
|
||||||
export const config = {
|
export const config = {
|
||||||
|
@ -43,7 +44,7 @@ export default checkoutCalculateTaxesSyncWebhook.createHandler(async (req, res,
|
||||||
Promise.resolve({ token: authData.token })
|
Promise.resolve({ token: authData.token })
|
||||||
);
|
);
|
||||||
|
|
||||||
const providersConfig = await new TaxProvidersConfigurationService(
|
const providersConfig = await new PrivateTaxProvidersConfigurationService(
|
||||||
client,
|
client,
|
||||||
authData.saleorApiUrl
|
authData.saleorApiUrl
|
||||||
).getAll();
|
).getAll();
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { createClient } from "../../../lib/graphql";
|
||||||
import { createLogger } from "../../../lib/logger";
|
import { createLogger } from "../../../lib/logger";
|
||||||
import { calculateTaxesPayloadSchema, ExpectedWebhookPayload } from "../../../lib/saleor/schema";
|
import { calculateTaxesPayloadSchema, ExpectedWebhookPayload } from "../../../lib/saleor/schema";
|
||||||
import { GetChannelsConfigurationService } from "../../../modules/channels-configuration/get-channels-configuration.service";
|
import { GetChannelsConfigurationService } from "../../../modules/channels-configuration/get-channels-configuration.service";
|
||||||
import { TaxProvidersConfigurationService } from "../../../modules/providers-configuration/providers-configuration-service";
|
import { PrivateTaxProvidersConfigurationService } from "../../../modules/providers-configuration/private-providers-configuration-service";
|
||||||
import { ActiveTaxProvider } from "../../../modules/taxes/active-tax-provider";
|
import { ActiveTaxProvider } from "../../../modules/taxes/active-tax-provider";
|
||||||
|
|
||||||
export const config = {
|
export const config = {
|
||||||
|
@ -42,7 +42,7 @@ export default orderCalculateTaxesSyncWebhook.createHandler(async (req, res, ctx
|
||||||
const client = createClient(authData.saleorApiUrl, async () =>
|
const client = createClient(authData.saleorApiUrl, async () =>
|
||||||
Promise.resolve({ token: authData.token })
|
Promise.resolve({ token: authData.token })
|
||||||
);
|
);
|
||||||
const providersConfig = await new TaxProvidersConfigurationService(
|
const providersConfig = await new PrivateTaxProvidersConfigurationService(
|
||||||
client,
|
client,
|
||||||
authData.saleorApiUrl
|
authData.saleorApiUrl
|
||||||
).getAll();
|
).getAll();
|
||||||
|
|
Loading…
Reference in a new issue