Skip to content

Commit

Permalink
✨ feat(user-preferences): move themes to user preferences
Browse files Browse the repository at this point in the history
  • Loading branch information
thrownullexception committed Dec 3, 2023
1 parent ba95569 commit 4190e76
Show file tree
Hide file tree
Showing 46 changed files with 490 additions and 448 deletions.
15 changes: 0 additions & 15 deletions src/__tests__/_/api-handlers/account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ let ME: IAuthenticatedUserBag = {
role: "creator",
systemProfile: "{userId: 1}",
username: "root",
preferences: '{"theme":"dark"}',
};

let USERS = [
Expand Down Expand Up @@ -58,20 +57,6 @@ export const accountApiHandlers = [
return res(ctx.status(204));
}),

rest.patch(
BASE_TEST_URL("/api/account/preferences"),
async (req, res, ctx) => {
ME = {
...ME,
preferences: JSON.stringify({
...JSON.parse(ME.preferences),
...(await req.json()),
}),
};
return res(ctx.status(204));
}
),

rest.get(BASE_TEST_URL("/api/account/:username"), async (_, res, ctx) => {
return res(ctx.json(USER));
}),
Expand Down
2 changes: 2 additions & 0 deletions src/__tests__/api/_test-utils/_all.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { setupActivatedActionTestData } from "./_activated-actions";
import { setupActionInstanceTestData } from "./_action-instances";
import { setupTestDatabaseData } from "./_data";
import { portalTestData } from "./portal";
import { setupUserPreferencesTestData } from "./_user-preferences";

type DomainTypes = ConfigDomain | KeyValueDomain | "data";

Expand All @@ -28,6 +29,7 @@ export const setupAllTestData = async (domains: DomainTypes[]) => {
["constants", setupIntegrationsConstantsTestData],
["environment-variables", setupIntegrationsEnvTestData],
["credentials", setupCredentialsTestData],
["users-preferences", setupUserPreferencesTestData],
...portalTestData,
];

Expand Down
8 changes: 8 additions & 0 deletions src/__tests__/api/_test-utils/_user-preferences.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { createConfigDomainPersistenceService } from "backend/lib/config-persistence";

export const setupUserPreferencesTestData = async () => {
const configPersistenceService =
createConfigDomainPersistenceService("users-preferences");

await configPersistenceService.resetToEmpty();
};
72 changes: 0 additions & 72 deletions src/__tests__/api/account/preferences.spec.ts

This file was deleted.

66 changes: 66 additions & 0 deletions src/__tests__/api/config/[key]/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,4 +117,70 @@ describe("/api/config/[key]/index", () => {
expect(getReq.res._getStatusCode()).toBe(200);
expect(getReq.res._getJSONData()).toEqual(["order-1", "order-2"]);
});

describe("App config key validation", () => {
it("should return error for invalid config key", async () => {
const { req, res } = createAuthenticatedMocks({
method: "GET",
query: {
key: "some-invalid-key",
},
});

await handler(req, res);

expect(res._getStatusCode()).toBe(400);
expect(res._getJSONData()).toMatchInlineSnapshot(`
{
"message": "Configuration key 'some-invalid-key' doesn't exist",
"method": "GET",
"name": "BadRequestError",
"path": "",
"statusCode": 400,
}
`);
});

it("should return error for no config key", async () => {
const { req, res } = createAuthenticatedMocks({
method: "GET",
query: {},
});

await handler(req, res);

expect(res._getStatusCode()).toBe(400);
expect(res._getJSONData()).toMatchInlineSnapshot(`
{
"message": "Configuration key 'undefined' doesn't exist",
"method": "GET",
"name": "BadRequestError",
"path": "",
"statusCode": 400,
}
`);
});

it("should return error for entity config keys when the entity is not passed", async () => {
const { req, res } = createAuthenticatedMocks({
method: "GET",
query: {
key: "entity_views",
},
});

await handler(req, res);

expect(res._getStatusCode()).toBe(400);
expect(res._getJSONData()).toMatchInlineSnapshot(`
{
"message": "Configuration of key 'entity_views' requires entity",
"method": "GET",
"name": "BadRequestError",
"path": "",
"statusCode": 400,
}
`);
});
});
});
139 changes: 139 additions & 0 deletions src/__tests__/api/user-preferences/[key].spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
import handler from "pages/api/user-preferences/[key]";
import {
createAuthenticatedMocks,
setupAllTestData,
} from "__tests__/api/_test-utils";

describe("/api/user-preferences/[key]", () => {
beforeAll(async () => {
await setupAllTestData(["users-preferences"]);
});

it("should get the default value when value doesn't exist for user", async () => {
const getRequest = createAuthenticatedMocks({
method: "GET",
query: {
key: "theme",
},
});

await handler(getRequest.req, getRequest.res);

expect(getRequest.res._getJSONData()).toMatchInlineSnapshot(`
{
"data": "light",
}
`);
});

it("should create authenticated user preference when doesn't exist", async () => {
const postRequest = createAuthenticatedMocks({
method: "PUT",
query: {
key: "theme",
},
body: {
data: "dark",
},
});

await handler(postRequest.req, postRequest.res);

expect(postRequest.res._getStatusCode()).toBe(204);

const getRequest = createAuthenticatedMocks({
method: "GET",
query: {
key: "theme",
},
});

await handler(getRequest.req, getRequest.res);

expect(getRequest.res._getJSONData()).toMatchInlineSnapshot(`
{
"data": "dark",
}
`);
});

it("should update authenticated user preference it when exists", async () => {
const postRequest = createAuthenticatedMocks({
method: "PUT",
query: {
key: "theme",
},
body: {
data: "light",
},
});

await handler(postRequest.req, postRequest.res);

const getRequest = createAuthenticatedMocks({
method: "GET",
query: {
key: "theme",
},
});

await handler(getRequest.req, getRequest.res);

expect(getRequest.res._getJSONData()).toMatchInlineSnapshot(`
{
"data": "light",
}
`);
});

describe("Preference key validation", () => {
it("GET", async () => {
const getRequest = createAuthenticatedMocks({
method: "GET",
query: {
key: "some-invalid-preference-key",
},
});

await handler(getRequest.req, getRequest.res);

expect(getRequest.res._getStatusCode()).toBe(400);

expect(getRequest.res._getJSONData()).toMatchInlineSnapshot(`
{
"message": "User Preference key 'some-invalid-preference-key' doesn't exist",
"method": "GET",
"name": "BadRequestError",
"path": "",
"statusCode": 400,
}
`);
});

it("PUT", async () => {
const getRequest = createAuthenticatedMocks({
query: {
key: "some-invalid-preference-key",
},
method: "PUT",
body: {
data: "light",
},
});

await handler(getRequest.req, getRequest.res);

expect(getRequest.res._getStatusCode()).toBe(400);

expect(getRequest.res._getJSONData()).toMatchInlineSnapshot(`
{
"message": "User Preference key 'some-invalid-preference-key' doesn't exist",
"method": "PUT",
"name": "BadRequestError",
"path": "",
"statusCode": 400,
}
`);
});
});
});
41 changes: 32 additions & 9 deletions src/backend/configuration/configuration.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,46 @@ import {
export class ConfigurationApiController {
constructor(private _configurationService: ConfigurationApiService) {}

async showConfig(key: AppConfigurationKeys, entity?: string) {
return await this._configurationService.show(key, entity);
async showConfig(key: string, entity?: string) {
const appConfigurationKey = this.validateAppConfigurationKeys({
key,
entity,
});
return await this._configurationService.show(appConfigurationKey, entity);
}

async showGuestConfig(key: AppConfigurationKeys) {
async showGuestConfig(key: string) {
if (!APP_CONFIGURATION_CONFIG[key].guest) {
throw new BadRequestError(`Invalid guest config key ${key}`);
}
return await this.showConfig(key);
}

async upsertConfig(
key: AppConfigurationKeys,
value: unknown,
entity?: string
) {
await this._configurationService.upsert(key, value, entity);
async upsertConfig(key: string, value: unknown, entity?: string) {
const appConfigurationKey = this.validateAppConfigurationKeys({
key,
entity,
});
await this._configurationService.upsert(appConfigurationKey, value, entity);
}

private validateAppConfigurationKeys({
key,
entity,
}: {
key: string;
entity?: string;
}) {
const configBag = APP_CONFIGURATION_CONFIG[key];
if (!configBag) {
throw new BadRequestError(`Configuration key '${key}' doesn't exist`);
}
if (configBag.requireEntity && !entity) {
throw new BadRequestError(
`Configuration of key '${key}' requires entity`
);
}
return key as AppConfigurationKeys;
}
}

Expand Down
Loading

0 comments on commit 4190e76

Please sign in to comment.