mirror of
https://github.com/logto-io/logto.git
synced 2025-03-10 22:22:45 -05:00
Init table type generation
This commit is contained in:
parent
b775168a7b
commit
17b6f98833
8 changed files with 312 additions and 2 deletions
|
@ -1,4 +1,7 @@
|
|||
{
|
||||
"space": true,
|
||||
"prettier": true
|
||||
"prettier": true,
|
||||
"rules": {
|
||||
"unicorn/no-array-reduce": "off"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,8 @@
|
|||
"author": "Logto",
|
||||
"license": "UNLICENSED",
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"generate": "ts-node src/gen/index.ts",
|
||||
"build": "rm -rf lib/ && tsc --p tsconfig.build.json",
|
||||
"lint": "xo src/"
|
||||
},
|
||||
"engines": {
|
||||
|
@ -14,6 +15,8 @@
|
|||
"yarn": "1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "14",
|
||||
"ts-node": "^10.0.0",
|
||||
"typescript": "^4.3.4",
|
||||
"xo": "0.39.1"
|
||||
}
|
||||
|
|
67
packages/schemas/src/gen/index.ts
Normal file
67
packages/schemas/src/gen/index.ts
Normal file
|
@ -0,0 +1,67 @@
|
|||
import assert from 'assert';
|
||||
import fs from 'fs/promises';
|
||||
import path from 'path';
|
||||
|
||||
import { findFirstParentheses, getType, normalizeWhitespaces, removeParentheses } from './utils';
|
||||
|
||||
type Field = {
|
||||
name: string;
|
||||
type: string;
|
||||
required: boolean;
|
||||
isArray: boolean;
|
||||
};
|
||||
|
||||
type Table = {
|
||||
name: string;
|
||||
fields: Field[];
|
||||
};
|
||||
|
||||
const dir = 'tables';
|
||||
|
||||
const generate = async () => {
|
||||
const files = await fs.readdir(dir);
|
||||
const generated = await Promise.all(
|
||||
files.map(async (file) => {
|
||||
return (await fs.readFile(path.join(dir, file), { encoding: 'utf-8' }))
|
||||
.split(';')
|
||||
.map((value) => normalizeWhitespaces(value).toLowerCase())
|
||||
.filter((value) => value.startsWith('create table'))
|
||||
.map((value) => findFirstParentheses(value))
|
||||
.filter((value): value is NonNullable<typeof value> => Boolean(value))
|
||||
.map<Table>(({ prefix, body }) => {
|
||||
const name = normalizeWhitespaces(prefix).split(' ')[2];
|
||||
assert(name, 'Missing table name: ' + prefix);
|
||||
|
||||
const fields = removeParentheses(body)
|
||||
.split(',')
|
||||
.map((value) => normalizeWhitespaces(value))
|
||||
.filter((value) =>
|
||||
['primary', 'foreign', 'unique', 'exclude', 'check'].every(
|
||||
(constraint) => !value.startsWith(constraint)
|
||||
)
|
||||
)
|
||||
.map<Field>((value) => {
|
||||
const [name, type, ...rest] = value.split(' ');
|
||||
assert(name && type, 'Missing column name or type: ' + value);
|
||||
|
||||
const restJoined = rest.join(' ');
|
||||
// CAUTION: Only works for single dimension arrays
|
||||
const isArray = Boolean(/\[.*]/.test(type)) || restJoined.includes('array');
|
||||
const required = restJoined.includes('not null');
|
||||
|
||||
return {
|
||||
name,
|
||||
type: getType(type),
|
||||
isArray,
|
||||
required,
|
||||
};
|
||||
});
|
||||
return { name, fields };
|
||||
});
|
||||
})
|
||||
);
|
||||
const tables = generated.flat();
|
||||
console.log(tables);
|
||||
};
|
||||
|
||||
void generate();
|
120
packages/schemas/src/gen/utils.ts
Normal file
120
packages/schemas/src/gen/utils.ts
Normal file
|
@ -0,0 +1,120 @@
|
|||
import { Optional } from '../global';
|
||||
|
||||
export const normalizeWhitespaces = (string: string): string => string.replace(/\s+/g, ' ').trim();
|
||||
|
||||
const getCountDelta = (value: string): number => {
|
||||
if (value === '(') {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (value === ')') {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
||||
export const removeParentheses = (value: string) =>
|
||||
Object.values(value).reduce<{ result: string; count: number }>(
|
||||
(previous, current) => {
|
||||
const count = previous.count + getCountDelta(current);
|
||||
return count === 0 && current !== ')'
|
||||
? { result: previous.result + current, count }
|
||||
: { result: previous.result, count };
|
||||
},
|
||||
{
|
||||
result: '',
|
||||
count: 0,
|
||||
}
|
||||
).result;
|
||||
|
||||
type ParenthesesMatch = { body: string; prefix: string };
|
||||
|
||||
export const findFirstParentheses = (value: string): Optional<ParenthesesMatch> => {
|
||||
const { matched, count, ...rest } = Object.values(value).reduce<{
|
||||
body: string;
|
||||
prefix: string;
|
||||
count: number;
|
||||
matched: boolean;
|
||||
}>(
|
||||
(previous, current) => {
|
||||
const count = previous.count + getCountDelta(current);
|
||||
|
||||
if (count === 0) {
|
||||
if (current === ')') {
|
||||
return {
|
||||
...previous,
|
||||
count,
|
||||
matched: true,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
...previous,
|
||||
count,
|
||||
prefix: previous.prefix + current,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
...previous,
|
||||
count,
|
||||
body: previous.body + (count === 1 && current === '(' ? '' : current),
|
||||
};
|
||||
},
|
||||
{
|
||||
body: '',
|
||||
prefix: '',
|
||||
count: 0,
|
||||
matched: false,
|
||||
}
|
||||
);
|
||||
|
||||
return matched ? rest : undefined;
|
||||
};
|
||||
|
||||
const getRawType = (value: string): string => {
|
||||
const bracketIndex = value.indexOf('[');
|
||||
return bracketIndex === -1 ? value : value.slice(0, bracketIndex);
|
||||
};
|
||||
|
||||
// Reference: https://github.com/SweetIQ/schemats/blob/7c3d3e16b5d507b4d9bd246794e7463b05d20e75/src/schemaPostgres.ts
|
||||
// eslint-disable-next-line complexity
|
||||
export const getType = (value: string): 'string' | 'number' | 'boolean' | 'Object' | 'Date' => {
|
||||
switch (getRawType(value)) {
|
||||
case 'bpchar':
|
||||
case 'char':
|
||||
case 'varchar':
|
||||
case 'text':
|
||||
case 'citext':
|
||||
case 'uuid':
|
||||
case 'bytea':
|
||||
case 'inet':
|
||||
case 'time':
|
||||
case 'timetz':
|
||||
case 'interval':
|
||||
case 'name':
|
||||
return 'string';
|
||||
case 'int2':
|
||||
case 'int4':
|
||||
case 'int8':
|
||||
case 'bigint':
|
||||
case 'float4':
|
||||
case 'float8':
|
||||
case 'numeric':
|
||||
case 'money':
|
||||
case 'oid':
|
||||
return 'number';
|
||||
case 'bool':
|
||||
return 'boolean';
|
||||
case 'json':
|
||||
case 'jsonb':
|
||||
return 'Object';
|
||||
case 'date':
|
||||
case 'timestamp':
|
||||
case 'timestamptz':
|
||||
return 'Date';
|
||||
default:
|
||||
throw new Error('Unable to parse type: ' + value);
|
||||
}
|
||||
};
|
1
packages/schemas/src/global.ts
Normal file
1
packages/schemas/src/global.ts
Normal file
|
@ -0,0 +1 @@
|
|||
export type Optional<T> = T | undefined;
|
28
packages/schemas/tables/oidc_model_instances.sql
Normal file
28
packages/schemas/tables/oidc_model_instances.sql
Normal file
|
@ -0,0 +1,28 @@
|
|||
create table oidc_model_instances (
|
||||
model_name varchar(64) not null,
|
||||
id varchar(128) not null,
|
||||
payload jsonb not null,
|
||||
expires_at bigint not null,
|
||||
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
|
||||
on oidc_model_instances (
|
||||
model_name,
|
||||
user_code
|
||||
);
|
||||
|
||||
create index oidc_model_instances__model_name_uid
|
||||
on oidc_model_instances (
|
||||
model_name,
|
||||
uid
|
||||
);
|
||||
|
||||
create index oidc_model_instances__model_name_grant_id
|
||||
on oidc_model_instances (
|
||||
model_name,
|
||||
grant_id
|
||||
);
|
4
packages/schemas/tsconfig.build.json
Normal file
4
packages/schemas/tsconfig.build.json
Normal file
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"extends": "./tsconfig",
|
||||
"exclude": ["src/gen"]
|
||||
}
|
|
@ -278,6 +278,26 @@
|
|||
dependencies:
|
||||
defer-to-connect "^1.0.1"
|
||||
|
||||
"@tsconfig/node10@^1.0.7":
|
||||
version "1.0.8"
|
||||
resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.8.tgz#c1e4e80d6f964fbecb3359c43bd48b40f7cadad9"
|
||||
integrity sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==
|
||||
|
||||
"@tsconfig/node12@^1.0.7":
|
||||
version "1.0.9"
|
||||
resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.9.tgz#62c1f6dee2ebd9aead80dc3afa56810e58e1a04c"
|
||||
integrity sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw==
|
||||
|
||||
"@tsconfig/node14@^1.0.0":
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.1.tgz#95f2d167ffb9b8d2068b0b235302fafd4df711f2"
|
||||
integrity sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==
|
||||
|
||||
"@tsconfig/node16@^1.0.1":
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.1.tgz#a6ca6a9a0ff366af433f42f5f0e124794ff6b8f1"
|
||||
integrity sha512-FTgBI767POY/lKNDNbIzgAX6miIDBs6NTCbdlDb8TrWovHsSvaVIZDlTqym29C6UqhzwcJx4CYr+AlrMywA0cA==
|
||||
|
||||
"@types/eslint@^7.2.13":
|
||||
version "7.2.13"
|
||||
resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-7.2.13.tgz#e0ca7219ba5ded402062ad6f926d491ebb29dd53"
|
||||
|
@ -324,6 +344,11 @@
|
|||
resolved "https://registry.yarnpkg.com/@types/node/-/node-15.12.4.tgz#e1cf817d70a1e118e81922c4ff6683ce9d422e26"
|
||||
integrity sha512-zrNj1+yqYF4WskCMOHwN+w9iuD12+dGm0rQ35HLl9/Ouuq52cEtd0CH9qMgrdNmi5ejC1/V7vKEXYubB+65DkA==
|
||||
|
||||
"@types/node@14":
|
||||
version "14.17.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.17.3.tgz#6d327abaa4be34a74e421ed6409a0ae2f47f4c3d"
|
||||
integrity sha512-e6ZowgGJmTuXa3GyaPbTGxX17tnThl2aSSizrFthQ7m9uLGZBXiGhgE55cjRZTF5kjZvYn9EOPOMljdjwbflxw==
|
||||
|
||||
"@types/normalize-package-data@^2.4.0":
|
||||
version "2.4.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz#e486d0d97396d79beedd0a6e33f4534ff6b4973e"
|
||||
|
@ -476,6 +501,11 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0:
|
|||
dependencies:
|
||||
color-convert "^2.0.1"
|
||||
|
||||
arg@^4.1.0:
|
||||
version "4.1.3"
|
||||
resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089"
|
||||
integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==
|
||||
|
||||
argparse@^1.0.7:
|
||||
version "1.0.10"
|
||||
resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911"
|
||||
|
@ -654,6 +684,11 @@ buf-compare@^1.0.0:
|
|||
resolved "https://registry.yarnpkg.com/buf-compare/-/buf-compare-1.0.1.tgz#fef28da8b8113a0a0db4430b0b6467b69730b34a"
|
||||
integrity sha1-/vKNqLgROgoNtEMLC2Rntpcws0o=
|
||||
|
||||
buffer-from@^1.0.0:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
|
||||
integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==
|
||||
|
||||
cache-base@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2"
|
||||
|
@ -880,6 +915,11 @@ cosmiconfig@^7.0.0:
|
|||
path-type "^4.0.0"
|
||||
yaml "^1.10.0"
|
||||
|
||||
create-require@^1.1.0:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333"
|
||||
integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==
|
||||
|
||||
cross-spawn@^7.0.2, cross-spawn@^7.0.3:
|
||||
version "7.0.3"
|
||||
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
|
||||
|
@ -991,6 +1031,11 @@ define-property@^2.0.2:
|
|||
is-descriptor "^1.0.2"
|
||||
isobject "^3.0.1"
|
||||
|
||||
diff@^4.0.1:
|
||||
version "4.0.2"
|
||||
resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d"
|
||||
integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==
|
||||
|
||||
dir-glob@^2.2.2:
|
||||
version "2.2.2"
|
||||
resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-2.2.2.tgz#fa09f0694153c8918b18ba0deafae94769fc50c4"
|
||||
|
@ -2526,6 +2571,11 @@ make-dir@^3.0.0, make-dir@^3.0.2:
|
|||
dependencies:
|
||||
semver "^6.0.0"
|
||||
|
||||
make-error@^1.1.1:
|
||||
version "1.3.6"
|
||||
resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2"
|
||||
integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==
|
||||
|
||||
map-cache@^0.2.2:
|
||||
version "0.2.2"
|
||||
resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf"
|
||||
|
@ -3400,6 +3450,14 @@ source-map-resolve@^0.5.0:
|
|||
source-map-url "^0.4.0"
|
||||
urix "^0.1.0"
|
||||
|
||||
source-map-support@^0.5.17:
|
||||
version "0.5.19"
|
||||
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61"
|
||||
integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==
|
||||
dependencies:
|
||||
buffer-from "^1.0.0"
|
||||
source-map "^0.6.0"
|
||||
|
||||
source-map-url@^0.4.0:
|
||||
version "0.4.1"
|
||||
resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.1.tgz#0af66605a745a5a2f91cf1bbf8a7afbc283dec56"
|
||||
|
@ -3410,6 +3468,11 @@ source-map@^0.5.0, source-map@^0.5.6:
|
|||
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
|
||||
integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=
|
||||
|
||||
source-map@^0.6.0:
|
||||
version "0.6.1"
|
||||
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
|
||||
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
|
||||
|
||||
spdx-correct@^3.0.0:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9"
|
||||
|
@ -3630,6 +3693,22 @@ trim-newlines@^3.0.0:
|
|||
resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-3.0.1.tgz#260a5d962d8b752425b32f3a7db0dcacd176c144"
|
||||
integrity sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==
|
||||
|
||||
ts-node@^10.0.0:
|
||||
version "10.0.0"
|
||||
resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.0.0.tgz#05f10b9a716b0b624129ad44f0ea05dac84ba3be"
|
||||
integrity sha512-ROWeOIUvfFbPZkoDis0L/55Fk+6gFQNZwwKPLinacRl6tsxstTF1DbAcLKkovwnpKMVvOMHP1TIbnwXwtLg1gg==
|
||||
dependencies:
|
||||
"@tsconfig/node10" "^1.0.7"
|
||||
"@tsconfig/node12" "^1.0.7"
|
||||
"@tsconfig/node14" "^1.0.0"
|
||||
"@tsconfig/node16" "^1.0.1"
|
||||
arg "^4.1.0"
|
||||
create-require "^1.1.0"
|
||||
diff "^4.0.1"
|
||||
make-error "^1.1.1"
|
||||
source-map-support "^0.5.17"
|
||||
yn "3.1.1"
|
||||
|
||||
tsconfig-paths@^3.9.0:
|
||||
version "3.9.0"
|
||||
resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz#098547a6c4448807e8fcb8eae081064ee9a3c90b"
|
||||
|
@ -3926,6 +4005,11 @@ yargs-parser@^20.2.3:
|
|||
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee"
|
||||
integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==
|
||||
|
||||
yn@3.1.1:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50"
|
||||
integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==
|
||||
|
||||
yocto-queue@^0.1.0:
|
||||
version "0.1.0"
|
||||
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
|
||||
|
|
Loading…
Add table
Reference in a new issue