Skip to content

Commit

Permalink
Enable client logs by default in AvaTax (#1705)
Browse files Browse the repository at this point in the history
  • Loading branch information
krzysztofzuraw authored Jan 22, 2025
1 parent 18a9c3d commit e195c8d
Show file tree
Hide file tree
Showing 10 changed files with 70 additions and 140 deletions.
5 changes: 5 additions & 0 deletions .changeset/long-cherries-eat.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"app-avatax": patch
---

Remove feature flag for client logs. After this change logs are enabled by default.
7 changes: 4 additions & 3 deletions apps/avatax/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,12 @@ SECRET_KEY=
# E2E_USER_PASSWORD=


FF_ENABLE_EXPERIMENTAL_LOGS=true
DYNAMODB_LOGS_ITEM_TTL_IN_DAYS=30
# DYNAMODB_LOGS_ITEM_TTL_IN_DAYS=14 - time to live for logs in DynamoDB
DYNAMODB_LOGS_TABLE_NAME=avatax-client-logs

AWS_REGION=localhost
AWS_ENDPOINT_URL=http://localhost:8000
AWS_ACCESS_KEY_ID=...
AWS_SECRET_ACCESS_KEY=...

MANIFEST_APP_ID=saleor.app.avatax
MANIFEST_APP_ID=saleor.app.avatax
13 changes: 5 additions & 8 deletions apps/avatax/src/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,13 @@ export const env = createEnv({
APP_IFRAME_BASE_URL: z.string().optional(),
APP_LOG_LEVEL: z.enum(["fatal", "error", "warn", "info", "debug", "trace"]).default("info"),
AVATAX_CLIENT_TIMEOUT: z.coerce.number().optional().default(15000),
// TODO: make them required once we remove `FF_ENABLE_EXPERIMENTAL_LOGS`
AWS_ACCESS_KEY_ID: z.string().optional(),
AWS_REGION: z.string().optional(),
AWS_SECRET_ACCESS_KEY: z.string().optional(),
DYNAMODB_LOGS_ITEM_TTL_IN_DAYS: z.coerce.number().optional(),
DYNAMODB_LOGS_TABLE_NAME: z.string().optional(),
AWS_ACCESS_KEY_ID: z.string(),
AWS_REGION: z.string(),
AWS_SECRET_ACCESS_KEY: z.string(),
DYNAMODB_LOGS_ITEM_TTL_IN_DAYS: z.coerce.number().positive().optional().default(14),
DYNAMODB_LOGS_TABLE_NAME: z.string(),
E2E_USER_NAME: z.string().optional(),
E2E_USER_PASSWORD: z.string().optional(),
FF_ENABLE_EXPERIMENTAL_LOGS: booleanSchema.optional().default("false"),
FILE_APL_PATH: z.string().optional(),
MANIFEST_APP_ID: z.string().optional().default("saleor.app.avatax"),
OTEL_ENABLED: booleanSchema.optional().default("false"),
Expand Down Expand Up @@ -59,7 +57,6 @@ export const env = createEnv({
E2E_USER_NAME: process.env.E2E_USER_NAME,
E2E_USER_PASSWORD: process.env.E2E_USER_PASSWORD,
ENV: process.env.ENV,
FF_ENABLE_EXPERIMENTAL_LOGS: process.env.FF_ENABLE_EXPERIMENTAL_LOGS,
FILE_APL_PATH: process.env.FILE_APL_PATH,
MANIFEST_APP_ID: process.env.MANIFEST_APP_ID,
NEXT_PUBLIC_SENTRY_DSN: process.env.NEXT_PUBLIC_SENTRY_DSN,
Expand Down
36 changes: 0 additions & 36 deletions apps/avatax/src/modules/client-logs/client-logs-feature-config.ts

This file was deleted.

82 changes: 24 additions & 58 deletions apps/avatax/src/modules/client-logs/client-logs.router.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
import * as Sentry from "@sentry/nextjs";
import { TRPCError } from "@trpc/server";
import { err, ok } from "neverthrow";
import { z } from "zod";

import { createLogger } from "@/logger";
import { env } from "@/env";
import { type ClientLogValue } from "@/modules/client-logs/client-log";
import { clientLogsFeatureConfig } from "@/modules/client-logs/client-logs-feature-config";
import {
createLogsDocumentClient,
createLogsDynamoClient,
Expand All @@ -16,72 +13,41 @@ import { router } from "@/modules/trpc/trpc-server";

import { ClientLogDynamoEntityFactory, LogsTable } from "./dynamo-schema";

// TODO: Remove this lazy method once feature is not behind feature flag
const getLogsRepository = () => {
const logger = createLogger("getLogsRepository");

if (!clientLogsFeatureConfig.dynamoTableName) {
logger.warn("DYNAMODB_LOGS_TABLE_NAME is not set.");

return err(new Error("DYNAMODB_LOGS_TABLE_NAME is not set."));
}

const logsTable = LogsTable.create({
documentClient: createLogsDocumentClient(createLogsDynamoClient()),
tableName: clientLogsFeatureConfig.dynamoTableName,
});
const logByDateEntity = ClientLogDynamoEntityFactory.createLogByDate(logsTable);
const logByCheckoutOrOrderId =
ClientLogDynamoEntityFactory.createLogByCheckoutOrOrderId(logsTable);

return ok(
new LogsRepositoryDynamodb({
logsTable,
logByDateEntity,
logByCheckoutOrOrderId: logByCheckoutOrOrderId,
}),
);
};

const procedureWithFlag = protectedClientProcedure.use(({ ctx, next }) => {
if (!clientLogsFeatureConfig.isEnabled) {
throw new TRPCError({
cause: "Feature disabled",
code: "FORBIDDEN",
message: "Feature is disabled",
const procedureWithLogsRepository = protectedClientProcedure.use(({ ctx, next }) => {
try {
const logsTable = LogsTable.create({
documentClient: createLogsDocumentClient(createLogsDynamoClient()),
tableName: env.DYNAMODB_LOGS_TABLE_NAME,
});
}

const logsRepositoryResult = getLogsRepository();

if (logsRepositoryResult.isErr()) {
Sentry.captureException(logsRepositoryResult.error);

const logByDateEntity = ClientLogDynamoEntityFactory.createLogByDate(logsTable);
const logByCheckoutOrOrderId =
ClientLogDynamoEntityFactory.createLogByCheckoutOrOrderId(logsTable);

return next({
ctx: {
...ctx,
logsRepository: new LogsRepositoryDynamodb({
logsTable,
logByDateEntity,
logByCheckoutOrOrderId,
}),
},
});
} catch (e) {
throw new TRPCError({
code: "INTERNAL_SERVER_ERROR",
message: "Logs unavailable, contact support",
message: "Logs are not available, contact Saleor support",
});
}

return next({
ctx: {
...ctx,
logsRepository: logsRepositoryResult.value,
},
});
});

/**
* TODO: Implement pagination
*
* Router that fetches logs in the frontend.
* To write log, use directly repository
*/
export const clientLogsRouter = router({
isEnabled: protectedClientProcedure.query(({ ctx }) => {
return clientLogsFeatureConfig.isEnabled;
}),
getByDate: procedureWithFlag
getByDate: procedureWithLogsRepository
.input(
z.object({
startDate: z.string().datetime(),
Expand Down Expand Up @@ -115,7 +81,7 @@ export const clientLogsRouter = router({
};
},
),
getByCheckoutOrOrderId: procedureWithFlag
getByCheckoutOrOrderId: procedureWithLogsRepository
.input(
z.object({
checkoutOrOrderId: z.string(),
Expand Down
4 changes: 2 additions & 2 deletions apps/avatax/src/modules/client-logs/dynamo-schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { type DynamoDBDocumentClient } from "@aws-sdk/lib-dynamodb";
import { Entity, number, schema, string, Table } from "dynamodb-toolbox";
import { ulid } from "ulid";

import { clientLogsFeatureConfig } from "@/modules/client-logs/client-logs-feature-config";
import { env } from "@/env";

export class LogsTable extends Table<
{
Expand Down Expand Up @@ -61,7 +61,7 @@ export class LogsTable extends Table<
}

static getDefaultTTL() {
const daysUntilExpire = clientLogsFeatureConfig.ttlInDays;
const daysUntilExpire = env.DYNAMODB_LOGS_ITEM_TTL_IN_DAYS;
const today = new Date();

// Add today + days until expire, export to UNIX epoch timestamp
Expand Down
51 changes: 24 additions & 27 deletions apps/avatax/src/modules/client-logs/log-writer-factory.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
import { clientLogsFeatureConfig } from "@/modules/client-logs/client-logs-feature-config";
import { env } from "@/env";
import { BaseError } from "@/error";
import {
createLogsDocumentClient,
createLogsDynamoClient,
} from "@/modules/client-logs/dynamo-client";
import { ClientLogDynamoEntityFactory, LogsTable } from "@/modules/client-logs/dynamo-schema";
import {
DynamoDbLogWriter,
ILogWriter,
LogWriterContext,
NoopLogWriter,
} from "@/modules/client-logs/log-writer";
import { DynamoDbLogWriter, ILogWriter, LogWriterContext } from "@/modules/client-logs/log-writer";
import { LogsRepositoryDynamodb } from "@/modules/client-logs/logs-repository";

export interface ILogWriterFactory {
Expand All @@ -20,30 +16,31 @@ export interface ILogWriterFactory {
* Depending on static config, create an ILogWriter instance
*/
export class LogWriterFactory implements ILogWriterFactory {
private createDynamoDbWriter(context: LogWriterContext): ILogWriter {
const dynamoClient = createLogsDynamoClient();
const logsTable = LogsTable.create({
documentClient: createLogsDocumentClient(dynamoClient),
tableName: clientLogsFeatureConfig.dynamoTableName!, // If not set, it will throw earlier
});
const repository = new LogsRepositoryDynamodb({
logsTable,
logByCheckoutOrOrderId: ClientLogDynamoEntityFactory.createLogByCheckoutOrOrderId(logsTable),
logByDateEntity: ClientLogDynamoEntityFactory.createLogByDate(logsTable),
});
static ErrorCreatingLogWriterError = BaseError.subclass("ErrorCreatingLogWriterError");

return new DynamoDbLogWriter(repository, context);
}
private createDynamoDbWriter(context: LogWriterContext): ILogWriter {
try {
const dynamoClient = createLogsDynamoClient();
const logsTable = LogsTable.create({
documentClient: createLogsDocumentClient(dynamoClient),
tableName: env.DYNAMODB_LOGS_TABLE_NAME,
});
const repository = new LogsRepositoryDynamodb({
logsTable,
logByCheckoutOrOrderId:
ClientLogDynamoEntityFactory.createLogByCheckoutOrOrderId(logsTable),
logByDateEntity: ClientLogDynamoEntityFactory.createLogByDate(logsTable),
});

private createNoopWriter(): ILogWriter {
return new NoopLogWriter();
return new DynamoDbLogWriter(repository, context);
} catch (e) {
throw new LogWriterFactory.ErrorCreatingLogWriterError("Failed to create DynamoDbLogWriter", {
cause: e,
});
}
}

createWriter(context: LogWriterContext): ILogWriter {
if (clientLogsFeatureConfig.isEnabled) {
return this.createDynamoDbWriter(context);
} else {
return this.createNoopWriter();
}
return this.createDynamoDbWriter(context);
}
}
5 changes: 1 addition & 4 deletions apps/avatax/src/pages/configuration.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,21 @@ import { useAppBridge } from "@saleor/app-sdk/app-bridge";
import { Box, Button, Text } from "@saleor/macaw-ui";
import { useRouter } from "next/router";

import { trpcClient } from "@/modules/trpc/trpc-client";

import { ChannelSection } from "../modules/channel-configuration/ui/channel-section";
import { ProvidersSection } from "../modules/provider-connections/ui/providers-section";
import { AppPageLayout } from "../modules/ui/app-page-layout";
import { Section } from "../modules/ui/app-section";
import { MatcherSection } from "../modules/ui/matcher-section";

const Header = () => {
const { data: logsEnabled } = trpcClient.clientLogs.isEnabled.useQuery();
const { push } = useRouter();

return (
<Box display="flex" justifyContent="space-between">
<Section.Header>
Configure the app by connecting to AvaTax. You can connect to multiple accounts.
</Section.Header>
{logsEnabled && <Button onClick={() => push("/logs")}>Open Logs</Button>}
<Button onClick={() => push("/logs")}>Open Logs</Button>
</Box>
);
};
Expand Down
3 changes: 1 addition & 2 deletions apps/avatax/src/pages/logs.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import { useAppBridge } from "@saleor/app-sdk/app-bridge";
import { Box } from "@saleor/macaw-ui";
import React from "react";

import { LogsBrowser } from "@/modules/client-logs/ui/logs-browser";
import { AppBreadcrumbs } from "@/modules/ui/app-breadcrumbs";

import { Section } from "../modules/ui/app-section";

const Header = () => {
return <Section.Header>Check App logs (up to last 100)</Section.Header>;
return <Section.Header>Check App logs</Section.Header>;
};

const ConfigurationPage = () => {
Expand Down
4 changes: 4 additions & 0 deletions apps/avatax/src/setup-tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ import { vi } from "vitest";

vi.stubEnv("DYNAMODB_LOGS_ITEM_TTL_IN_DAYS", "7");
vi.stubEnv("SECRET_KEY", "test_secret_key");
vi.stubEnv("DYNAMODB_LOGS_TABLE_NAME", "test-table");
vi.stubEnv("AWS_REGION", "test");
vi.stubEnv("AWS_ACCESS_KEY_ID", "test-id");
vi.stubEnv("AWS_SECRET_ACCESS_KEY", "test-key");

/**
* Add test setup logic here
Expand Down

0 comments on commit e195c8d

Please sign in to comment.