Skip to content
Draft
  •  
  •  
  •  
134 changes: 134 additions & 0 deletions clients/admin-ui/openapi-ts-api.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
// eslint-disable-next-line import/no-extraneous-dependencies
import { defineConfig } from "@hey-api/openapi-ts";

export default defineConfig({
input: "http://localhost:8080/openapi.json",
output: {
path: "./src/types/api",
format: "prettier",
fileName: {
suffix: null,
},
},
parser: {
patch: {
schemas: {
ScopeRegistryEnum: (schema) => {
// Add x-enum-varnames to replace colons with underscores in enum keys
if (schema.enum && Array.isArray(schema.enum)) {
// eslint-disable-next-line no-param-reassign
schema["x-enum-varnames"] = schema.enum.map((value: string) =>
value.toUpperCase().replace(/:/g, "_").replace(/-/g, "_"),
);
}
},
DATAMAP_GROUPING: (schema) => {
// Replace commas and spaces in enum values to produce valid identifier keys
if (schema.enum && Array.isArray(schema.enum)) {
// eslint-disable-next-line no-param-reassign
schema["x-enum-varnames"] = schema.enum.map((value: string) =>
value
.toUpperCase()
.replace(/,\s*/g, "_")
.replace(/\s+/g, "_")
.replace(/-/g, "_"),
);
}
},
AllowedTypes: (schema) => {
// Assign distinct enum key names since both values would map to STRING
if (schema.enum && Array.isArray(schema.enum)) {
// eslint-disable-next-line no-param-reassign
schema["x-enum-varnames"] = schema.enum.map((value: string) => {
if (value === "string") {
return "STRING";
}
if (value === "string[]") {
return "STRING_ARRAY";
}
return value.toUpperCase().replace(/[^A-Z0-9]/g, "_");
});
}
},
// -- Fidesplus computed properties not in base OpenAPI spec --
StagedResourceAPIResponse: (schema: any) => {
schema.properties.preferred_data_uses = {
type: "array",
items: { type: "string" },
nullable: true,
description:
"Computed: user_assigned_data_uses ?? data_uses (fidesplus)",
};
},
Field: (schema: any) => {
schema.properties.preferred_data_categories = {
type: "array",
items: { type: "string" },
nullable: true,
description: "Computed: field_data_categories (fidesplus)",
};
// Remove additionalProperties to prevent catch-all index signature
// that causes TypeScript to resolve all properties to `{}`
delete schema.additionalProperties;
},
ExperienceConfigCreate: (schema: any) => {
schema.properties.resurface_behavior = {
type: "array",
items: { type: "string", enum: ["reject", "dismiss"] },
nullable: true,
description: "Resurface behavior options (fidesplus)",
};
},
ExperienceConfigUpdate: (schema: any) => {
schema.properties.resurface_behavior = {
type: "array",
items: { type: "string", enum: ["reject", "dismiss"] },
nullable: true,
description: "Resurface behavior options (fidesplus)",
};
},
SystemStagedResourcesAggregateRecord: (schema: any) => {
schema.properties.metadata = {
$ref: "#/components/schemas/IdentityProviderApplicationMetadata",
nullable: true,
description:
"Okta application metadata for identity provider resources (fidesplus)",
};
},
},
},
hooks: {
symbols: {
getFilePath: (symbol) => {
// Skip endpoint-specific types (camelCase pattern: operationNameApiV1PathMethodData/Response/Errors/etc)
// These are auto-generated for each endpoint and bloat the output
if (
symbol.name &&
/Api[A-Z].*(?:Data|Response|Responses|Error|Errors)$/.test(
symbol.name,
)
) {
// Return undefined to skip this symbol entirely
return undefined;
}

// Keep one type/enum per file in models/ directory
// Note: enums have kind: undefined, only types have kind: "type"
// So we match on any symbol with a name (enums, types, interfaces all get split)
if (symbol.name) {
return `models/${symbol.name}`;
}
// Let other symbols use default placement
return undefined;
},
},
},
},
plugins: [
{
name: "@hey-api/typescript",
enums: "typescript",
case: "preserve",
},
],
});
14 changes: 14 additions & 0 deletions clients/admin-ui/openapi-ts-dictionary.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// eslint-disable-next-line import/no-extraneous-dependencies
import { defineConfig } from "@hey-api/openapi-ts";

export default defineConfig({
input: "http://localhost:8081/openapi.json",
output: {
path: "./src/types/dictionary-api",
format: "prettier",
},
types: {
enums: "typescript",
identifierCase: "preserve",
},
});
6 changes: 3 additions & 3 deletions clients/admin-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@
"lint": "next lint && eslint ./cypress/e2e/**/*.ts ./cypress/support/**/*.ts",
"lint:fix": "eslint --fix . && eslint --fix ./cypress/e2e/**/*.ts ./cypress/support/**/*.ts",
"lint-staged:fix": "lint-staged --diff=main",
"openapi:generate": "openapi --input http://localhost:8080/openapi.json --output ./src/types/api --exportCore false --exportServices false --indent 2 && prettier --write .",
"openapi:generate-dictionary": "openapi --input http://localhost:8081/openapi.json --output ./src/types/dictionary-api --exportCore false --exportServices false --indent 2",
"openapi:generate": "openapi-ts --file openapi-ts-api.config.ts",
"openapi:generate-dictionary": "openapi-ts --file openapi-ts-dictionary.config.ts",
"start": "next start",
"test": "jest",
"test:watch": "jest --watchAll",
Expand Down Expand Up @@ -78,6 +78,7 @@
"yup": "^1.4.0"
},
"devDependencies": {
"@hey-api/openapi-ts": "^0.88.0",
"@jest/globals": "^29.7.0",
"@next/bundle-analyzer": "^13.2.4",
"@testing-library/cypress": "^10.0.3",
Expand Down Expand Up @@ -121,7 +122,6 @@
"jest-environment-jsdom": "^29.7.0",
"lint-staged": "^13.2.0",
"msw": "^1.2.1",
"openapi-typescript-codegen": "^0.23.0",
"postcss": "^8.4.41",
"prettier": "^3.3.3",
"sass": "^1.80.6",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { getErrorMessage, isErrorResult } from "~/features/common/helpers";
import { CHAT_PROVIDERS_CONFIGURE_ROUTE } from "~/features/common/nav/routes";
import { useHasPermission } from "~/features/common/Restrict";
import { TableSkeletonLoader } from "~/features/common/table/v2";
import { ChatProviderSettingsResponse, ScopeRegistryEnum } from "~/types/api";
import { ChatConfigResponse, ScopeRegistryEnum } from "~/types/api";

import { SlackLogo } from "../common/logos/SlackLogo";
import {
Expand Down Expand Up @@ -71,7 +71,7 @@ export const ChatConfigurations = () => {
// Delete modal state
const [deleteModalOpen, setDeleteModalOpen] = useState(false);
const [configToDelete, setConfigToDelete] =
useState<ChatProviderSettingsResponse | null>(null);
useState<ChatConfigResponse | null>(null);

// Use items from the list response
const tableData = useMemo(() => {
Expand All @@ -86,7 +86,7 @@ export const ChatConfigurations = () => {
);

const handleDeleteConfiguration = useCallback(
(config: ChatProviderSettingsResponse) => {
(config: ChatConfigResponse) => {
setConfigToDelete(config);
setDeleteModalOpen(true);
},
Expand Down Expand Up @@ -134,12 +134,12 @@ export const ChatConfigurations = () => {
}, []);

// Column definitions
const columns: ColumnsType<ChatProviderSettingsResponse> = useMemo(
const columns: ColumnsType<ChatConfigResponse> = useMemo(
() => [
{
title: "Provider",
key: ChatProviderColumnKeys.PROVIDER,
render: (_value: unknown, record: ChatProviderSettingsResponse) => {
render: (_value: unknown, record: ChatConfigResponse) => {
const getProviderIcon = () => {
switch (record.provider_type) {
case "slack":
Expand Down Expand Up @@ -172,7 +172,7 @@ export const ChatConfigurations = () => {
{
title: "Status",
key: ChatProviderColumnKeys.STATUS,
render: (_value: unknown, record: ChatProviderSettingsResponse) => {
render: (_value: unknown, record: ChatConfigResponse) => {
if (record.authorized) {
return (
<Tag color="success" data-testid="status-authorized">
Expand All @@ -194,7 +194,7 @@ export const ChatConfigurations = () => {
title: "Enabled",
key: ChatProviderColumnKeys.ENABLED,
width: 100,
render: (_value: unknown, record: ChatProviderSettingsResponse) => (
render: (_value: unknown, record: ChatConfigResponse) => (
<Switch
checked={record.enabled}
disabled={!userCanUpdate || record.enabled}
Expand All @@ -215,7 +215,7 @@ export const ChatConfigurations = () => {
{
title: "Actions",
key: ChatProviderColumnKeys.ACTIONS,
render: (_value: unknown, record: ChatProviderSettingsResponse) => (
render: (_value: unknown, record: ChatConfigResponse) => (
<Space>
{userCanUpdate && !record.authorized && record.client_id && (
<Button
Expand Down
20 changes: 10 additions & 10 deletions clients/admin-ui/src/features/chat-provider/chatProvider.slice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,17 @@ import type {
ChatChannelsResponse,
ChatConfigCreate,
ChatConfigListResponse,
ChatConfigResponse,
ChatConfigSecrets,
ChatConfigTestResponse,
ChatConfigUpdate,
ChatProviderSecrets,
ChatProviderSettingsResponse,
ChatProviderTestResponse,
SendMessageRequest,
SendMessageResponse,
} from "~/types/api";

const chatProviderApi = baseApi.injectEndpoints({
endpoints: (build) => ({
testChatConnection: build.mutation<ChatProviderTestResponse, void>({
testChatConnection: build.mutation<ChatConfigTestResponse, void>({
query: () => ({
url: "plus/chat/test",
method: "POST",
Expand All @@ -25,7 +25,7 @@ const chatProviderApi = baseApi.injectEndpoints({
providesTags: ["Chat Provider Config"],
}),
createChatConfig: build.mutation<
ChatProviderSettingsResponse,
ChatConfigResponse,
ChatConfigCreate
>({
query: (body) => ({
Expand All @@ -35,14 +35,14 @@ const chatProviderApi = baseApi.injectEndpoints({
}),
invalidatesTags: ["Chat Provider Config"],
}),
getChatConfig: build.query<ChatProviderSettingsResponse, string>({
getChatConfig: build.query<ChatConfigResponse, string>({
query: (configId) => ({ url: `plus/chat/config/${configId}` }),
providesTags: (_result, _error, id) => [
{ type: "Chat Provider Config", id },
],
}),
updateChatConfig: build.mutation<
ChatProviderSettingsResponse,
ChatConfigResponse,
{ configId: string; data: ChatConfigUpdate }
>({
query: ({ configId, data }) => ({
Expand All @@ -62,16 +62,16 @@ const chatProviderApi = baseApi.injectEndpoints({
}),
invalidatesTags: ["Chat Provider Config"],
}),
enableChatConfig: build.mutation<ChatProviderSettingsResponse, string>({
enableChatConfig: build.mutation<ChatConfigResponse, string>({
query: (configId) => ({
url: `plus/chat/config/${configId}/enable`,
method: "PUT",
}),
invalidatesTags: ["Chat Provider Config"],
}),
updateChatConfigSecrets: build.mutation<
ChatProviderSettingsResponse,
{ configId: string; secrets: ChatProviderSecrets }
ChatConfigResponse,
{ configId: string; secrets: ChatConfigSecrets }
>({
query: ({ configId, secrets }) => ({
url: `plus/chat/config/${configId}/secret`,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,14 +102,14 @@ export const ConsentManagementModal = ({
{listRender(
"Data uses",
systemPurposeSummary.purposes[purposeName]
.data_uses,
.data_uses as string[],
)}
</Flex>
<Flex className="my-4" vertical>
{listRender(
"Legal basis",
systemPurposeSummary.purposes[purposeName]
.legal_bases,
.legal_bases as string[],
)}
</Flex>
</AccordionPanel>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Dayjs } from "dayjs";

import { baseApi } from "~/features/common/api.slice";
import {
Page_ConsentReportingSchema_,
ConditionalTotalPage_ConsentReportingSchema_,
PreferencesSavedExtended,
} from "~/types/api";
import { DateRangeParams, PaginationQueryParams } from "~/types/query-params";
Expand Down Expand Up @@ -47,7 +47,7 @@ export const consentReportingApi = baseApi.injectEndpoints({
providesTags: ["Consent Reporting Export"],
}),
getAllHistoricalPrivacyPreferences: build.query<
Page_ConsentReportingSchema_,
ConditionalTotalPage_ConsentReportingSchema_,
PaginationQueryParams & DateRangeParams & { includeTotal?: boolean }
>({
query: ({ page, size, startDate, endDate, includeTotal = true }) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,5 @@ export const REQUEST_ORIGIN_LABELS: Record<RequestOrigin, string> = {
[RequestOrigin.OVERLAY]: "Overlay",
[RequestOrigin.PRIVACY_CENTER]: "Privacy Center",
[RequestOrigin.TCF_OVERLAY]: "TCF Overlay",
[RequestOrigin.HEADLESS]: "Headless",
};
Loading
Loading