Refactor config CRUD handlers to standardize naming conventions for config strings and enhance validation in update operations.

This commit is contained in:
Zai Shi 2025-07-31 15:40:34 -07:00
parent 7862a3ef65
commit ee330cb919
4 changed files with 27 additions and 46 deletions

View File

@ -7,7 +7,7 @@ export const configCrudHandlers = createLazyProxy(() => createCrudHandlers(confi
paramsSchema: yupObject({}),
onRead: async ({ auth }) => {
return {
configString: JSON.stringify(auth.tenancy.config),
config_string: JSON.stringify(auth.tenancy.config),
};
},
}));

View File

@ -1,4 +1,4 @@
import { getRenderedEnvironmentConfigQuery, overrideEnvironmentConfigOverride } from "@/lib/config";
import { getRenderedEnvironmentConfigQuery, overrideEnvironmentConfigOverride, validateEnvironmentConfigOverride } from "@/lib/config";
import { globalPrismaClient, rawQuery } from "@/prisma-client";
import { createCrudHandlers } from "@/route-handlers/crud-handler";
import { configOverrideCrud } from "@stackframe/stack-shared/dist/interface/crud/config";
@ -9,16 +9,27 @@ import { createLazyProxy } from "@stackframe/stack-shared/dist/utils/proxies";
export const configOverridesCrudHandlers = createLazyProxy(() => createCrudHandlers(configOverrideCrud, {
paramsSchema: yupObject({}),
onUpdate: async ({ auth, data }) => {
if (data.configOverrideString) {
if (data.config_override_string) {
let parsedConfig;
try {
parsedConfig = JSON.parse(data.configOverrideString);
parsedConfig = JSON.parse(data.config_override_string);
} catch (e) {
if (e instanceof SyntaxError) {
throw new StatusError(StatusError.BadRequest, 'Invalid config JSON');
}
throw e;
}
const validationResult = await validateEnvironmentConfigOverride({
environmentConfigOverride: parsedConfig,
branchId: auth.tenancy.branchId,
projectId: auth.tenancy.project.id,
});
if (validationResult.status === "error") {
throw new StatusError(StatusError.BadRequest, validationResult.error);
}
await overrideEnvironmentConfigOverride({
projectId: auth.tenancy.project.id,
branchId: auth.tenancy.branchId,
@ -32,7 +43,7 @@ export const configOverridesCrudHandlers = createLazyProxy(() => createCrudHandl
}));
return {
configOverrideString: JSON.stringify(updatedConfig),
config_override_string: JSON.stringify(updatedConfig),
};
},
}));

View File

@ -339,14 +339,14 @@ it("returns an error when the oauth config is misconfigured", async ({ expect })
});
// Test invalid OAuth provider type
const invalidTypeResponse = await niceBackendFetch("/api/v1/internal/config-overrides", {
const invalidTypeResponse = await niceBackendFetch("/api/v1/internal/configs/overrides", {
method: "PATCH",
accessType: "admin",
headers: {
'x-stack-admin-access-token': adminAccessToken,
},
body: {
config: JSON.stringify({
config_override_string: JSON.stringify({
'auth.oauth.providers.invalid': {
type: 'invalid-provider',
isShared: false,
@ -359,43 +359,13 @@ it("returns an error when the oauth config is misconfigured", async ({ expect })
},
});
expect(invalidTypeResponse.status).toBe(400);
// Test missing required fields for non-shared provider
const missingFieldsResponse = await niceBackendFetch("/api/v1/internal/config-overrides", {
method: "PATCH",
accessType: "admin",
headers: {
'x-stack-admin-access-token': adminAccessToken,
},
body: {
config: JSON.stringify({
'auth.oauth.providers.google': {
type: 'google',
isShared: false,
allowSignIn: true,
allowConnectedAccounts: true,
// Missing clientId and clientSecret
},
}),
},
});
expect(missingFieldsResponse.status).toBe(400);
// Test invalid JSON
const invalidJsonResponse = await niceBackendFetch("/api/v1/internal/config-overrides", {
method: "PATCH",
accessType: "admin",
headers: {
'x-stack-admin-access-token': adminAccessToken,
},
body: {
config: "invalid json",
},
});
expect(invalidJsonResponse.status).toBe(400);
expect(invalidTypeResponse).toMatchInlineSnapshot(`
NiceResponse {
"status": 400,
"body": "[ERROR] auth.oauth.providers.invalid.type must be one of the following values: google, github, microsoft, spotify, facebook, discord, gitlab, bitbucket, linkedin, apple, x, twitch",
"headers": Headers { <some fields may have been hidden> },
}
`);
});
it("adds, updates, and removes domains", async ({ expect }) => {

View File

@ -5,7 +5,7 @@ import { yupObject } from "../../schema-fields";
export const configOverrideCrudAdminReadSchema = yupObject({}).defined();
export const configOverrideCrudAdminUpdateSchema = yupObject({
configOverrideString: schemaFields.yupString().optional(),
config_override_string: schemaFields.yupString().optional(),
}).defined();
export const configOverrideCrud = createCrud({
@ -22,7 +22,7 @@ export const configOverrideCrud = createCrud({
export type ConfigOverrideCrud = CrudTypeOf<typeof configOverrideCrud>;
export const configCrudAdminReadSchema = yupObject({
configString: schemaFields.yupString().defined(),
config_string: schemaFields.yupString().defined(),
}).defined();
export const configCrud = createCrud({