mirror of
https://github.com/logto-io/logto.git
synced 2025-01-20 21:32:31 -05:00
feat: remove redundant fields and add TS type support in generated files
This commit is contained in:
parent
266b520015
commit
fca35e6933
6 changed files with 61 additions and 38 deletions
|
@ -1,14 +1,13 @@
|
|||
// THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||
|
||||
import { OidcModelInstancePayload } from '../foundations';
|
||||
|
||||
export type OidcModelInstanceDBEntry = {
|
||||
modelName: string;
|
||||
id: string;
|
||||
payload: Record<string, unknown>;
|
||||
payload: OidcModelInstancePayload;
|
||||
expiresAt: number;
|
||||
consumedAt?: number;
|
||||
userCode?: string;
|
||||
uid?: string;
|
||||
grantId?: string;
|
||||
};
|
||||
|
||||
export const OidcModelInstances = Object.freeze({
|
||||
|
@ -19,18 +18,6 @@ export const OidcModelInstances = Object.freeze({
|
|||
payload: 'payload',
|
||||
expiresAt: 'expires_at',
|
||||
consumedAt: 'consumed_at',
|
||||
userCode: 'user_code',
|
||||
uid: 'uid',
|
||||
grantId: 'grant_id',
|
||||
},
|
||||
fieldKeys: [
|
||||
'modelName',
|
||||
'id',
|
||||
'payload',
|
||||
'expiresAt',
|
||||
'consumedAt',
|
||||
'userCode',
|
||||
'uid',
|
||||
'grantId',
|
||||
],
|
||||
fieldKeys: ['modelName', 'id', 'payload', 'expiresAt', 'consumedAt'],
|
||||
} as const);
|
||||
|
|
6
packages/schemas/src/foundations.ts
Normal file
6
packages/schemas/src/foundations.ts
Normal file
|
@ -0,0 +1,6 @@
|
|||
export type OidcModelInstancePayload = {
|
||||
[key: string]: unknown;
|
||||
userCode?: string;
|
||||
uid?: string;
|
||||
grantId?: string;
|
||||
};
|
|
@ -12,6 +12,7 @@ type Field = {
|
|||
name: string;
|
||||
type?: string;
|
||||
customType?: string;
|
||||
tsType?: string;
|
||||
required: boolean;
|
||||
isArray: boolean;
|
||||
};
|
||||
|
@ -56,8 +57,7 @@ const generate = async () => {
|
|||
.split(';')
|
||||
.map((value) => normalizeWhitespaces(value));
|
||||
const tables = statements
|
||||
.map((value) => value.toLowerCase())
|
||||
.filter((value) => value.startsWith('create table'))
|
||||
.filter((value) => value.toLowerCase().startsWith('create table'))
|
||||
.map((value) => findFirstParentheses(value))
|
||||
.filter((value): value is NonNullable<typeof value> => Boolean(value))
|
||||
.map<Table>(({ prefix, body }) => {
|
||||
|
@ -69,23 +69,35 @@ const generate = async () => {
|
|||
.map((value) => normalizeWhitespaces(value))
|
||||
.filter((value) =>
|
||||
['primary', 'foreign', 'unique', 'exclude', 'check'].every(
|
||||
(constraint) => !value.startsWith(constraint + ' ')
|
||||
(constraint) => !value.toLowerCase().startsWith(constraint + ' ')
|
||||
)
|
||||
)
|
||||
.map<Field>((value) => {
|
||||
const [name, type, ...rest] = value.split(' ');
|
||||
assert(name && type, 'Missing column name or type: ' + value);
|
||||
const [nameRaw, typeRaw, ...rest] = value.split(' ');
|
||||
assert(nameRaw && typeRaw, 'Missing column name or type: ' + value);
|
||||
|
||||
const name = nameRaw.toLowerCase();
|
||||
const type = typeRaw.toLowerCase();
|
||||
const restJoined = rest.join(' ');
|
||||
const restLowercased = restJoined.toLowerCase();
|
||||
// CAUTION: Only works for single dimension arrays
|
||||
const isArray = Boolean(/\[.*]/.test(type)) || restJoined.includes('array');
|
||||
const required = restJoined.includes('not null');
|
||||
const isArray = Boolean(/\[.*]/.test(type)) || restLowercased.includes('array');
|
||||
const required = restLowercased.includes('not null');
|
||||
const primitiveType = getType(type);
|
||||
const tsType = /\/\* @use (.*) \*\//.exec(restJoined)?.[1];
|
||||
|
||||
assert(
|
||||
!(!primitiveType && tsType),
|
||||
`TS type can only be applied on primitive types, found ${
|
||||
tsType ?? 'N/A'
|
||||
} over ${type}`
|
||||
);
|
||||
|
||||
return {
|
||||
name,
|
||||
type: primitiveType,
|
||||
customType: conditional(!primitiveType && type),
|
||||
tsType,
|
||||
isArray,
|
||||
required,
|
||||
};
|
||||
|
@ -116,11 +128,14 @@ const generate = async () => {
|
|||
|
||||
const generatedDir = 'src/db-entries';
|
||||
const generatedTypesFilename = 'custom-types';
|
||||
const tsTypesFilename = '../foundations';
|
||||
const header = '// THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.\n\n';
|
||||
const getOutputFileName = (file: string) => pluralize(file.slice(0, -4).replace(/_/g, '-'), 1);
|
||||
|
||||
await fs.rmdir(generatedDir, { recursive: true });
|
||||
await fs.mkdir(generatedDir, { recursive: true });
|
||||
|
||||
// Postgres custom types
|
||||
const allTypes = generated
|
||||
.flatMap((data) => data[1].types)
|
||||
.map<GeneratedType>((type) => ({
|
||||
|
@ -149,13 +164,18 @@ const generate = async () => {
|
|||
// Generate DB entry types
|
||||
await Promise.all(
|
||||
generated.map(async ([file, { tables }]) => {
|
||||
const tsTypes: string[] = [];
|
||||
const customTypes: string[] = [];
|
||||
const tableWithTypes = tables.map<TableWithType>(({ fields, ...rest }) => ({
|
||||
...rest,
|
||||
fields: fields.map(({ type, customType, ...rest }) => {
|
||||
const finalType = type ?? allTypes.find(({ name }) => name === customType)?.tsName;
|
||||
fields: fields.map(({ type, customType, tsType, ...rest }) => {
|
||||
const finalType =
|
||||
tsType ?? type ?? allTypes.find(({ name }) => name === customType)?.tsName;
|
||||
assert(finalType, `Type ${customType ?? 'N/A'} not found`);
|
||||
if (type === undefined) {
|
||||
|
||||
if (tsType) {
|
||||
tsTypes.push(tsType);
|
||||
} else if (type === undefined) {
|
||||
customTypes.push(finalType);
|
||||
}
|
||||
|
||||
|
@ -163,6 +183,17 @@ const generate = async () => {
|
|||
}),
|
||||
}));
|
||||
|
||||
const importTsTypes = conditionalString(
|
||||
tsTypes.length > 0 &&
|
||||
[
|
||||
'import {',
|
||||
uniq(tsTypes)
|
||||
.map((value) => ` ${value}`)
|
||||
.join(',\n'),
|
||||
`} from './${tsTypesFilename}';`,
|
||||
].join('\n') + '\n\n'
|
||||
);
|
||||
|
||||
const importTypes = conditionalString(
|
||||
customTypes.length > 0 &&
|
||||
[
|
||||
|
@ -176,6 +207,7 @@ const generate = async () => {
|
|||
|
||||
const content =
|
||||
header +
|
||||
importTsTypes +
|
||||
importTypes +
|
||||
tableWithTypes
|
||||
.map(({ name, fields }) =>
|
||||
|
|
|
@ -28,7 +28,7 @@ export const removeParentheses = (value: string) =>
|
|||
}
|
||||
).result;
|
||||
|
||||
type ParenthesesMatch = { body: string; prefix: string };
|
||||
export type ParenthesesMatch = { body: string; prefix: string };
|
||||
|
||||
export const findFirstParentheses = (value: string): Optional<ParenthesesMatch> => {
|
||||
const { matched, count, ...rest } = Object.values(value).reduce<{
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
export * from './foundations';
|
||||
export * from './db-entries';
|
||||
|
|
|
@ -1,29 +1,26 @@
|
|||
create table oidc_model_instances (
|
||||
model_name varchar(64) not null,
|
||||
id varchar(128) not null,
|
||||
payload jsonb not null,
|
||||
payload jsonb /* @use OidcModelInstancePayload */ not null,
|
||||
expires_at bigint not null,
|
||||
consumed_at bigint,
|
||||
user_code varchar(128),
|
||||
uid varchar(128),
|
||||
grant_id varchar(128),
|
||||
primary key (model_name, id)
|
||||
);
|
||||
|
||||
create index oidc_model_instances__model_name_user_code
|
||||
create index oidc_model_instances__model_name_payload_user_code
|
||||
on oidc_model_instances (
|
||||
model_name,
|
||||
user_code
|
||||
(payload->>'userCode')
|
||||
);
|
||||
|
||||
create index oidc_model_instances__model_name_uid
|
||||
create index oidc_model_instances__model_name_payload_uid
|
||||
on oidc_model_instances (
|
||||
model_name,
|
||||
uid
|
||||
(payload->>'uid')
|
||||
);
|
||||
|
||||
create index oidc_model_instances__model_name_grant_id
|
||||
create index oidc_model_instances__model_name_payload_grant_id
|
||||
on oidc_model_instances (
|
||||
model_name,
|
||||
grant_id
|
||||
(payload->>'grantId')
|
||||
);
|
||||
|
|
Loading…
Add table
Reference in a new issue