@tricoteuses/senat 2.23.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/src/index.d.ts +19 -8
- package/lib/src/index.js +6 -1
- package/lib/src/raw_types/ameli.d.ts +1651 -803
- package/lib/src/raw_types/ameli.js +1816 -5
- package/lib/src/raw_types/debats.d.ts +353 -180
- package/lib/src/raw_types/debats.js +517 -5
- package/lib/src/raw_types/dosleg.d.ts +2862 -1527
- package/lib/src/raw_types/dosleg.js +4354 -5
- package/lib/src/raw_types/questions.d.ts +671 -395
- package/lib/src/raw_types/questions.js +1303 -5
- package/lib/src/raw_types/sens.d.ts +7743 -8148
- package/lib/src/raw_types/sens.js +10429 -5
- package/lib/src/scripts/retrieve_open_data.js +148 -42
- package/lib/src/scripts/shared/prefixed_tables.d.ts +6 -3
- package/lib/src/scripts/shared/prefixed_tables.js +22 -16
- package/lib/src/scripts/validate_prefixed_tables.js +5 -6
- package/lib/src/types/ameli.d.ts +4 -4
- package/lib/src/types/debats.d.ts +2 -2
- package/lib/src/types/dosleg.d.ts +39 -39
- package/lib/src/types/questions.d.ts +2 -2
- package/lib/src/types/sens.d.ts +0 -2
- package/lib/tests/prefixedTables.test.js +8 -15
- package/lib/tests/validatePrefixedTables.test.js +3 -3
- package/package.json +6 -2
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import assert from "assert";
|
|
2
|
-
import {
|
|
2
|
+
import { execFileSync } from "child_process";
|
|
3
3
|
import commandLineArgs from "command-line-args";
|
|
4
4
|
import fs from "fs-extra";
|
|
5
|
+
import { formatWithPrettier, makePgTsGenerator, markAsGenerated, processDatabase } from "kanel";
|
|
6
|
+
import { makeGenerateZodSchemas } from "kanel-zod";
|
|
5
7
|
import path from "path";
|
|
6
8
|
import StreamZip from "node-stream-zip";
|
|
7
9
|
import readline from "readline";
|
|
@@ -12,7 +14,7 @@ import config from "../config";
|
|
|
12
14
|
import { getChosenDatasets, getEnabledDatasets } from "../datasets";
|
|
13
15
|
import { assertExistingDirectory, commonOptions } from "./shared/cli_helpers";
|
|
14
16
|
import { buildIncrementalDatasetImportSql, buildNormalizeStagingSchemaSql } from "./shared/incremental_import_sql";
|
|
15
|
-
import {
|
|
17
|
+
import { buildGeneratedTableManifest, getGeneratedDefinitionPath, getGeneratedTableManifestPath, prefixedName, rawTypesDir, senatSchemaName, stagingSchemaName, stripDatasetPrefix, } from "./shared/prefixed_tables";
|
|
16
18
|
import { buildEnsureSchemaVersionTableSql, buildIncrementSchemaVersionSql, buildSchemaStructureFingerprintQuery, } from "./shared/schema_version";
|
|
17
19
|
import { buildExportStagingMetadataStatementsQuery } from "./shared/staging_metadata_sql";
|
|
18
20
|
import { isCopyFromStdinLine, rewriteLineForStagingImport } from "./shared/staging_import";
|
|
@@ -27,7 +29,7 @@ const optionsDefinitions = [
|
|
|
27
29
|
},
|
|
28
30
|
{
|
|
29
31
|
alias: "c",
|
|
30
|
-
help: "create TypeScript interfaces from database
|
|
32
|
+
help: "create TypeScript interfaces and Zod schemas from database tables into src/raw_types",
|
|
31
33
|
name: "schema",
|
|
32
34
|
type: Boolean,
|
|
33
35
|
},
|
|
@@ -73,9 +75,6 @@ const stagingServerName = "staging_server";
|
|
|
73
75
|
function isIncrementalImport(options) {
|
|
74
76
|
return options["incremental"] === true;
|
|
75
77
|
}
|
|
76
|
-
function shellQuote(value) {
|
|
77
|
-
return `'${value.replace(/'/g, `'"'"'`)}'`;
|
|
78
|
-
}
|
|
79
78
|
function connectionEnv(connection) {
|
|
80
79
|
return {
|
|
81
80
|
...process.env,
|
|
@@ -124,7 +123,8 @@ async function runWithRetry(operation, options, retryOptions) {
|
|
|
124
123
|
throw error;
|
|
125
124
|
}
|
|
126
125
|
if (!options["silent"]) {
|
|
127
|
-
console.warn(`${retryOptions.label} hit a transient PostgreSQL lock error
|
|
126
|
+
console.warn(`${retryOptions.label} hit a transient PostgreSQL lock error ` +
|
|
127
|
+
`on attempt ${attempt}/${retryOptions.attempts}; retrying in ${delayMs}ms...`);
|
|
128
128
|
}
|
|
129
129
|
await sleep(delayMs);
|
|
130
130
|
attempt += 1;
|
|
@@ -132,18 +132,30 @@ async function runWithRetry(operation, options, retryOptions) {
|
|
|
132
132
|
}
|
|
133
133
|
}
|
|
134
134
|
}
|
|
135
|
-
function
|
|
136
|
-
const
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
135
|
+
function buildPsqlInvocation(baseArgs, connection, options) {
|
|
136
|
+
const psqlArgs = [
|
|
137
|
+
"--quiet",
|
|
138
|
+
"-h",
|
|
139
|
+
connection.host,
|
|
140
|
+
"-p",
|
|
141
|
+
String(connection.port),
|
|
142
|
+
"-U",
|
|
143
|
+
connection.user,
|
|
144
|
+
"-d",
|
|
145
|
+
connection.name,
|
|
146
|
+
...baseArgs,
|
|
147
|
+
];
|
|
148
|
+
if (!options["sudo"]) {
|
|
149
|
+
return { command: "psql", args: psqlArgs };
|
|
150
|
+
}
|
|
151
|
+
return {
|
|
152
|
+
command: "sudo",
|
|
153
|
+
args: ["-u", options["sudo"], "psql", ...psqlArgs],
|
|
154
|
+
};
|
|
143
155
|
}
|
|
144
156
|
function runPsqlFile(sqlFilePath, dataDir, options, connection, stopOnError = true) {
|
|
145
|
-
const
|
|
146
|
-
|
|
157
|
+
const { command, args } = buildPsqlInvocation([...(stopOnError ? ["-v", "ON_ERROR_STOP=1"] : []), "-f", sqlFilePath], connection, options);
|
|
158
|
+
execFileSync(command, args, {
|
|
147
159
|
cwd: dataDir,
|
|
148
160
|
env: connectionEnv(connection),
|
|
149
161
|
encoding: "utf-8",
|
|
@@ -151,8 +163,9 @@ function runPsqlFile(sqlFilePath, dataDir, options, connection, stopOnError = tr
|
|
|
151
163
|
});
|
|
152
164
|
}
|
|
153
165
|
function runPsqlCommand(command, dataDir, options, connection, stopOnError = true) {
|
|
154
|
-
const
|
|
155
|
-
|
|
166
|
+
const psqlCommand = command;
|
|
167
|
+
const { command: binary, args } = buildPsqlInvocation([...(stopOnError ? ["-v", "ON_ERROR_STOP=1"] : []), "-c", psqlCommand], connection, options);
|
|
168
|
+
execFileSync(binary, args, {
|
|
156
169
|
cwd: dataDir,
|
|
157
170
|
env: connectionEnv(connection),
|
|
158
171
|
encoding: "utf-8",
|
|
@@ -160,8 +173,9 @@ function runPsqlCommand(command, dataDir, options, connection, stopOnError = tru
|
|
|
160
173
|
});
|
|
161
174
|
}
|
|
162
175
|
function runPsqlQuery(command, dataDir, options, connection, stopOnError = true) {
|
|
163
|
-
const
|
|
164
|
-
|
|
176
|
+
const psqlCommand = command;
|
|
177
|
+
const { command: binary, args } = buildPsqlInvocation([...(stopOnError ? ["-v", "ON_ERROR_STOP=1"] : []), "-At", "-c", psqlCommand], connection, options);
|
|
178
|
+
return execFileSync(binary, args, {
|
|
165
179
|
cwd: dataDir,
|
|
166
180
|
env: connectionEnv(connection),
|
|
167
181
|
encoding: "utf-8",
|
|
@@ -171,7 +185,8 @@ function runPsqlQuery(command, dataDir, options, connection, stopOnError = true)
|
|
|
171
185
|
function ensureStagingSchemaHasTables(dataset, dataDir, options, connection) {
|
|
172
186
|
const stagingSchema = stagingSchemaName(dataset.database);
|
|
173
187
|
const tableCount = Number.parseInt(runPsqlQuery(`SELECT count(*) FROM pg_tables WHERE schemaname = '${escapeSqlLiteral(stagingSchema)}'`, dataDir, options, connection).trim(), 10);
|
|
174
|
-
assert(tableCount > 0, `Staging schema ${stagingSchema} is empty after importing ${dataset.database}.
|
|
188
|
+
assert(tableCount > 0, `Staging schema ${stagingSchema} is empty after importing ${dataset.database}. ` +
|
|
189
|
+
`Aborting incremental merge to protect ${senatSchemaName}.`);
|
|
175
190
|
}
|
|
176
191
|
function ensureStagingDatabase(dataDir, options, runtime) {
|
|
177
192
|
const maintenanceDb = process.env["PGDATABASE"] || "postgres";
|
|
@@ -207,16 +222,27 @@ function ensureDatabaseExists(connection, dataDir, options) {
|
|
|
207
222
|
runPsqlCommand(`CREATE DATABASE ${connection.name} WITH OWNER ${connection.user}`, dataDir, options, maintenanceConnection);
|
|
208
223
|
}
|
|
209
224
|
function ensureForeignStagingServer(dataDir, options, runtime) {
|
|
210
|
-
runPsqlCommand(
|
|
225
|
+
runPsqlCommand("CREATE EXTENSION IF NOT EXISTS postgres_fdw", dataDir, options, runtime.target);
|
|
211
226
|
runPsqlCommand(`DROP SERVER IF EXISTS ${stagingServerName} CASCADE`, dataDir, options, runtime.target, false);
|
|
212
227
|
runPsqlCommand([
|
|
213
228
|
`CREATE SERVER ${stagingServerName}`,
|
|
214
|
-
|
|
215
|
-
|
|
229
|
+
"FOREIGN DATA WRAPPER postgres_fdw",
|
|
230
|
+
[
|
|
231
|
+
"OPTIONS (",
|
|
232
|
+
`host '${escapeSqlLiteral(runtime.staging.host)}', `,
|
|
233
|
+
`dbname '${escapeSqlLiteral(runtime.staging.name)}', `,
|
|
234
|
+
`port '${escapeSqlLiteral(String(runtime.staging.port))}'`,
|
|
235
|
+
")",
|
|
236
|
+
].join(""),
|
|
216
237
|
].join(" "), dataDir, options, runtime.target);
|
|
217
238
|
runPsqlCommand([
|
|
218
239
|
`CREATE USER MAPPING FOR CURRENT_USER SERVER ${stagingServerName}`,
|
|
219
|
-
|
|
240
|
+
[
|
|
241
|
+
"OPTIONS (",
|
|
242
|
+
`user '${escapeSqlLiteral(runtime.staging.user)}', `,
|
|
243
|
+
`password '${escapeSqlLiteral(runtime.staging.password)}'`,
|
|
244
|
+
")",
|
|
245
|
+
].join(""),
|
|
220
246
|
].join(" "), dataDir, options, runtime.target);
|
|
221
247
|
}
|
|
222
248
|
function cleanupForeignStagingServer(dataDir, options, runtime) {
|
|
@@ -224,7 +250,7 @@ function cleanupForeignStagingServer(dataDir, options, runtime) {
|
|
|
224
250
|
}
|
|
225
251
|
function mountForeignStagingSchema(dataset, dataDir, options, runtime) {
|
|
226
252
|
const stagingSchema = stagingSchemaName(dataset.database);
|
|
227
|
-
const importForeignSchemaCommand = `IMPORT FOREIGN SCHEMA ${stagingSchema} FROM SERVER ${stagingServerName} INTO ${stagingSchema}`;
|
|
253
|
+
const importForeignSchemaCommand = `IMPORT FOREIGN SCHEMA ${stagingSchema} ` + `FROM SERVER ${stagingServerName} INTO ${stagingSchema}`;
|
|
228
254
|
runPsqlCommand(`DROP SCHEMA IF EXISTS ${stagingSchema} CASCADE`, dataDir, options, runtime.target, false);
|
|
229
255
|
runPsqlCommand(`CREATE SCHEMA ${stagingSchema}`, dataDir, options, runtime.target);
|
|
230
256
|
try {
|
|
@@ -342,6 +368,97 @@ function listPrefixedTables(dataset, dataDir, options, runtime) {
|
|
|
342
368
|
.map((tableName) => tableName.trim())
|
|
343
369
|
.filter((tableName) => tableName.length > 0);
|
|
344
370
|
}
|
|
371
|
+
function toPascalCase(text) {
|
|
372
|
+
return text.replace(/(^|_)([a-z0-9])/gi, (_match, _separator, letter) => letter.toUpperCase());
|
|
373
|
+
}
|
|
374
|
+
function toIdentifierName(tableName, columnName) {
|
|
375
|
+
return `${toPascalCase(tableName)}${toPascalCase(columnName)}`;
|
|
376
|
+
}
|
|
377
|
+
function trimComment(comment) {
|
|
378
|
+
const trimmed = comment?.trim();
|
|
379
|
+
return trimmed ? trimmed : undefined;
|
|
380
|
+
}
|
|
381
|
+
async function generateRawTypes(dataset, runtime, prefixedTables) {
|
|
382
|
+
await fs.ensureDir(rawTypesDir);
|
|
383
|
+
const definitionFilePath = getGeneratedDefinitionPath(dataset.database);
|
|
384
|
+
const manifestFilePath = getGeneratedTableManifestPath(dataset.database);
|
|
385
|
+
const datasetPrefix = `${dataset.database}_`;
|
|
386
|
+
const datasetOutputPath = path.join(rawTypesDir, dataset.database);
|
|
387
|
+
const generateZodSchemas = makeGenerateZodSchemas({
|
|
388
|
+
castToSchema: true,
|
|
389
|
+
getZodIdentifierMetadata: (column, details) => ({
|
|
390
|
+
name: `${toIdentifierName(stripDatasetPrefix(details.name, dataset.database), column.name)}Schema`,
|
|
391
|
+
}),
|
|
392
|
+
getZodSchemaMetadata: (details, generateFor) => {
|
|
393
|
+
const baseName = toPascalCase(stripDatasetPrefix(details.name, dataset.database));
|
|
394
|
+
const suffix = generateFor === "selector" || generateFor === undefined ? "" : toPascalCase(generateFor);
|
|
395
|
+
return {
|
|
396
|
+
name: `${baseName}${suffix}Schema`,
|
|
397
|
+
path: datasetOutputPath,
|
|
398
|
+
};
|
|
399
|
+
},
|
|
400
|
+
});
|
|
401
|
+
await fs.remove(definitionFilePath);
|
|
402
|
+
await fs.remove(manifestFilePath);
|
|
403
|
+
await processDatabase({
|
|
404
|
+
connection: {
|
|
405
|
+
database: runtime.target.name,
|
|
406
|
+
host: runtime.target.host,
|
|
407
|
+
password: runtime.target.password,
|
|
408
|
+
port: runtime.target.port,
|
|
409
|
+
user: runtime.target.user,
|
|
410
|
+
},
|
|
411
|
+
filter: (pgType) => pgType.schemaName === senatSchemaName && pgType.name.startsWith(datasetPrefix),
|
|
412
|
+
generators: [
|
|
413
|
+
makePgTsGenerator({
|
|
414
|
+
filter: (pgType) => pgType.schemaName === senatSchemaName && pgType.name.startsWith(datasetPrefix),
|
|
415
|
+
generateIdentifierType: (column, details, builtinType) => {
|
|
416
|
+
const tableName = stripDatasetPrefix(details.name, dataset.database);
|
|
417
|
+
return {
|
|
418
|
+
...builtinType,
|
|
419
|
+
comment: undefined,
|
|
420
|
+
name: toIdentifierName(tableName, column.name),
|
|
421
|
+
typeDefinition: builtinType.typeDefinition.map((typeDefinition) => typeDefinition.replace(/ & \{ __flavor\?: '[^']+' \}/g, "").replace(/ & \{ __brand: '[^']+' \}/g, "")),
|
|
422
|
+
};
|
|
423
|
+
},
|
|
424
|
+
getMetadata: (details, generateFor, builtinMetadata) => {
|
|
425
|
+
const baseName = toPascalCase(stripDatasetPrefix(details.name, dataset.database));
|
|
426
|
+
const suffix = generateFor === "selector" || generateFor === undefined ? "" : toPascalCase(generateFor);
|
|
427
|
+
const tableComment = trimComment(details.comment);
|
|
428
|
+
return {
|
|
429
|
+
...builtinMetadata,
|
|
430
|
+
comment: tableComment ? [tableComment] : undefined,
|
|
431
|
+
exportAs: "named",
|
|
432
|
+
name: `${baseName}${suffix}`,
|
|
433
|
+
path: datasetOutputPath,
|
|
434
|
+
};
|
|
435
|
+
},
|
|
436
|
+
getPropertyMetadata: (property, _details, generateFor, builtinMetadata) => {
|
|
437
|
+
const comment = trimComment(property.comment);
|
|
438
|
+
const defaultComment = generateFor === "initializer" && property.defaultValue !== null && property.defaultValue !== undefined
|
|
439
|
+
? `Default value: ${property.defaultValue}`
|
|
440
|
+
: undefined;
|
|
441
|
+
const comments = [comment, defaultComment].filter((value) => value !== undefined);
|
|
442
|
+
return {
|
|
443
|
+
...builtinMetadata,
|
|
444
|
+
comment: comments.length > 0 ? comments : undefined,
|
|
445
|
+
};
|
|
446
|
+
},
|
|
447
|
+
preRenderHooks: [generateZodSchemas],
|
|
448
|
+
}),
|
|
449
|
+
],
|
|
450
|
+
outputPath: rawTypesDir,
|
|
451
|
+
postRenderHooks: [markAsGenerated, formatWithPrettier],
|
|
452
|
+
schemaNames: [senatSchemaName],
|
|
453
|
+
typescriptConfig: {
|
|
454
|
+
enumStyle: "literal-union",
|
|
455
|
+
tsModuleFormat: "esm",
|
|
456
|
+
},
|
|
457
|
+
});
|
|
458
|
+
const generatedDefinition = await fs.readFile(definitionFilePath, { encoding: "utf8" });
|
|
459
|
+
await fs.writeFile(definitionFilePath, generatedDefinition.replace(/\r\n/g, "\n"));
|
|
460
|
+
await fs.writeFile(manifestFilePath, buildGeneratedTableManifest(dataset.database, prefixedTables));
|
|
461
|
+
}
|
|
345
462
|
async function downloadFile(url, dest) {
|
|
346
463
|
const response = await fetch(url);
|
|
347
464
|
if (!response.ok) {
|
|
@@ -474,7 +591,7 @@ async function retrieveDataset(dataDir, dataset, options, runtime) {
|
|
|
474
591
|
repairedSqlWriter.end();
|
|
475
592
|
await fs.move(repairedSqlFilePath, sqlFilePath, { overwrite: true });
|
|
476
593
|
}
|
|
477
|
-
if (options["all"] || options["import"]
|
|
594
|
+
if (options["all"] || options["import"]) {
|
|
478
595
|
if (!options["silent"]) {
|
|
479
596
|
console.log(`Merging ${dataset.title}: ${sqlFilename} into ${runtime.target.name}.${senatSchemaName}...`);
|
|
480
597
|
}
|
|
@@ -485,24 +602,13 @@ async function retrieveDataset(dataDir, dataset, options, runtime) {
|
|
|
485
602
|
}
|
|
486
603
|
}
|
|
487
604
|
if (options["schema"]) {
|
|
488
|
-
|
|
489
|
-
assert(fs.statSync(definitionsDir).isDirectory());
|
|
605
|
+
await fs.ensureDir(rawTypesDir);
|
|
490
606
|
if (!options["silent"]) {
|
|
491
607
|
console.log(`Creating TypeScript definitions from prefixed ${senatSchemaName} tables ` +
|
|
492
608
|
`for '${dataset.database}' in database '${runtime.target.name}'...`);
|
|
493
609
|
}
|
|
494
|
-
const
|
|
495
|
-
|
|
496
|
-
const definitionFilePath = path.join(definitionsDir, `${dataset.database}.ts`);
|
|
497
|
-
const tables = listPrefixedTables(dataset, dataDir, options, runtime);
|
|
498
|
-
const tableOptions = tables.map((tableName) => `-t ${tableName}`).join(" ");
|
|
499
|
-
execSync(`npx schemats generate -c ${dbConnectionString} -s ${senatSchemaName} ${tableOptions} -o ${definitionFilePath}`, {
|
|
500
|
-
env: process.env,
|
|
501
|
-
encoding: "utf-8",
|
|
502
|
-
});
|
|
503
|
-
const definition = fs.readFileSync(definitionFilePath, { encoding: "utf8" });
|
|
504
|
-
const definitionRepaired = normalizeGeneratedDefinition(definition, dataset.database);
|
|
505
|
-
fs.writeFileSync(definitionFilePath, definitionRepaired);
|
|
610
|
+
const prefixedTables = listPrefixedTables(dataset, dataDir, options, runtime);
|
|
611
|
+
await generateRawTypes(dataset, runtime, prefixedTables);
|
|
506
612
|
}
|
|
507
613
|
}
|
|
508
614
|
function buildRuntimeContext() {
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
export declare const senatSchemaName = "senat";
|
|
2
|
+
export declare const rawTypesDir: string;
|
|
2
3
|
export declare function prefixedName(datasetName: string, relationName: string): string;
|
|
3
4
|
export declare function stagingSchemaName(datasetName: string): string;
|
|
4
5
|
export declare function escapeRegExp(text: string): string;
|
|
5
|
-
export declare function
|
|
6
|
-
export declare function
|
|
7
|
-
export declare function
|
|
6
|
+
export declare function stripDatasetPrefix(relationName: string, datasetName: string): string;
|
|
7
|
+
export declare function getGeneratedDefinitionPath(datasetName: string): string;
|
|
8
|
+
export declare function getGeneratedTableManifestPath(datasetName: string): string;
|
|
9
|
+
export declare function buildGeneratedTableManifest(datasetName: string, prefixedTables: string[]): string;
|
|
10
|
+
export declare function extractPrefixedTableNamesFromGeneratedManifest(manifest: string, datasetName: string): string[];
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import path from "path";
|
|
1
2
|
export const senatSchemaName = "senat";
|
|
3
|
+
export const rawTypesDir = path.resolve("src", "raw_types");
|
|
2
4
|
export function prefixedName(datasetName, relationName) {
|
|
3
5
|
return `${datasetName}_${relationName}`;
|
|
4
6
|
}
|
|
@@ -8,23 +10,27 @@ export function stagingSchemaName(datasetName) {
|
|
|
8
10
|
export function escapeRegExp(text) {
|
|
9
11
|
return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
10
12
|
}
|
|
11
|
-
export function
|
|
13
|
+
export function stripDatasetPrefix(relationName, datasetName) {
|
|
12
14
|
const datasetPrefix = `${datasetName}_`;
|
|
13
|
-
return
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
15
|
+
return relationName.startsWith(datasetPrefix) ? relationName.slice(datasetPrefix.length) : relationName;
|
|
16
|
+
}
|
|
17
|
+
export function getGeneratedDefinitionPath(datasetName) {
|
|
18
|
+
return path.join(rawTypesDir, `${datasetName}.ts`);
|
|
19
|
+
}
|
|
20
|
+
export function getGeneratedTableManifestPath(datasetName) {
|
|
21
|
+
return path.join(rawTypesDir, `${datasetName}.tables.json`);
|
|
22
|
+
}
|
|
23
|
+
export function buildGeneratedTableManifest(datasetName, prefixedTables) {
|
|
24
|
+
const manifest = {
|
|
25
|
+
dataset: datasetName,
|
|
26
|
+
prefixedTables: [...new Set(prefixedTables)],
|
|
27
|
+
};
|
|
28
|
+
return JSON.stringify(manifest, null, 2) + "\n";
|
|
29
|
+
}
|
|
30
|
+
export function extractPrefixedTableNamesFromGeneratedManifest(manifest, datasetName) {
|
|
31
|
+
const parsed = JSON.parse(manifest);
|
|
32
|
+
if (parsed.dataset !== datasetName || !Array.isArray(parsed.prefixedTables)) {
|
|
23
33
|
return [];
|
|
24
34
|
}
|
|
25
|
-
|
|
26
|
-
return [...new Set(tables)];
|
|
27
|
-
}
|
|
28
|
-
export function extractPrefixedTableNamesFromGeneratedDefinition(definition, datasetName) {
|
|
29
|
-
return extractTableNamesFromGeneratedDefinition(definition).map((tableName) => prefixedName(datasetName, tableName));
|
|
35
|
+
return [...new Set(parsed.prefixedTables.filter((tableName) => typeof tableName === "string"))];
|
|
30
36
|
}
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import commandLineArgs from "command-line-args";
|
|
2
2
|
import fs from "fs-extra";
|
|
3
|
-
import path from "path";
|
|
4
3
|
import { sql } from "../databases_postgres";
|
|
5
4
|
import { getChosenDatasets, getEnabledDatasets } from "../datasets";
|
|
6
5
|
import { categoriesOption, silentOption } from "./shared/cli_helpers";
|
|
7
|
-
import {
|
|
6
|
+
import { extractPrefixedTableNamesFromGeneratedManifest, getGeneratedTableManifestPath, senatSchemaName, } from "./shared/prefixed_tables";
|
|
8
7
|
const optionsDefinitions = [categoriesOption, silentOption];
|
|
9
8
|
const options = commandLineArgs(optionsDefinitions);
|
|
10
9
|
async function listPrefixedSenatTables(dataset) {
|
|
@@ -19,12 +18,12 @@ async function listPrefixedSenatTables(dataset) {
|
|
|
19
18
|
return rows.map(({ relation_name }) => relation_name);
|
|
20
19
|
}
|
|
21
20
|
function getGeneratedDefinitionPath(dataset) {
|
|
22
|
-
return
|
|
21
|
+
return getGeneratedTableManifestPath(dataset.database);
|
|
23
22
|
}
|
|
24
23
|
function listExpectedPrefixedSenatTables(dataset) {
|
|
25
|
-
const
|
|
26
|
-
const
|
|
27
|
-
return
|
|
24
|
+
const manifestFilePath = getGeneratedDefinitionPath(dataset);
|
|
25
|
+
const manifest = fs.readFileSync(manifestFilePath, { encoding: "utf8" });
|
|
26
|
+
return extractPrefixedTableNamesFromGeneratedManifest(manifest, dataset.database);
|
|
28
27
|
}
|
|
29
28
|
async function listColumns(schema, relationName) {
|
|
30
29
|
const rows = await sql `
|
package/lib/src/types/ameli.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
export interface
|
|
4
|
-
subids?:
|
|
1
|
+
import { TxtAmeli } from "../raw_types/ameli";
|
|
2
|
+
import { Sub } from "../raw_types/ameli";
|
|
3
|
+
export interface TxtAmeliCustom extends TxtAmeli {
|
|
4
|
+
subids?: Sub["id"][];
|
|
5
5
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export type { Debat, LecAssDeb };
|
|
1
|
+
import { Debats, Lecassdeb } from "../raw_types/debats";
|
|
2
|
+
export type { Debats as Debat, Lecassdeb as LecAssDeb };
|
|
@@ -1,70 +1,70 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
export type {
|
|
6
|
-
export interface Aud extends
|
|
1
|
+
import { Ass, Aud as AudRaw, Auteur as AuteurRaw, DateSeance as DateSeanceRaw, Deccoc, Denrap, Docatt as DocattRaw, Ecr as EcrRaw, Etaloi, Lecass as LecassRaw, Lecassrap as LecassrapRaw, Lecture as LectureRaw, Loi as LoiRaw, Org, Oritxt, Qua, Rap as RapRaw, Raporg, Scr, Texte as TexteRaw, Typatt, Typlec, Typloi, Typtxt, Typurl } from "../raw_types/dosleg";
|
|
2
|
+
import { TxtAmeli } from "../raw_types/ameli";
|
|
3
|
+
import { Debats } from "../raw_types/debats";
|
|
4
|
+
import { TxtAmeliCustom } from "./ameli";
|
|
5
|
+
export type { Deccoc as DecCoc, Denrap as DenRap, Etaloi as EtaLoi, Oritxt as OriTxt, Raporg as RapOrg, Typatt as TypAtt, Typlec as TypLec, Typloi as TypLoi, Typtxt as TypTxt, Typurl as TypUrl, };
|
|
6
|
+
export interface Aud extends AudRaw {
|
|
7
7
|
org?: Org;
|
|
8
8
|
}
|
|
9
|
-
export interface Auteur extends
|
|
9
|
+
export interface Auteur extends AuteurRaw {
|
|
10
10
|
qua?: Qua;
|
|
11
11
|
}
|
|
12
|
-
export interface DateSeance extends
|
|
13
|
-
debat?:
|
|
12
|
+
export interface DateSeance extends DateSeanceRaw {
|
|
13
|
+
debat?: Debats;
|
|
14
14
|
scrids?: string[];
|
|
15
15
|
scrs?: Scr[];
|
|
16
16
|
}
|
|
17
|
-
export interface DocAtt extends
|
|
18
|
-
rap?:
|
|
19
|
-
typatt?:
|
|
17
|
+
export interface DocAtt extends DocattRaw {
|
|
18
|
+
rap?: RapRaw;
|
|
19
|
+
typatt?: Typatt;
|
|
20
20
|
}
|
|
21
|
-
export interface Ecr extends
|
|
21
|
+
export interface Ecr extends EcrRaw {
|
|
22
22
|
aut?: Auteur;
|
|
23
23
|
}
|
|
24
|
-
export interface LecAss extends
|
|
24
|
+
export interface LecAss extends LecassRaw {
|
|
25
25
|
ass?: Ass;
|
|
26
26
|
auds?: Aud[];
|
|
27
|
-
audcles?:
|
|
27
|
+
audcles?: AudRaw["audcle"][];
|
|
28
28
|
datesSeances?: DateSeance[];
|
|
29
|
-
datesSeancesCodes?:
|
|
29
|
+
datesSeancesCodes?: DateSeanceRaw["code"][];
|
|
30
30
|
debatdatseas: Date[];
|
|
31
31
|
lecassraps?: LecAssRap[];
|
|
32
32
|
lecassrapids?: string[];
|
|
33
33
|
org?: Org;
|
|
34
|
-
texcods:
|
|
34
|
+
texcods: TexteRaw["texcod"][];
|
|
35
35
|
textes?: Texte[];
|
|
36
36
|
}
|
|
37
|
-
export interface LecAssRap extends
|
|
38
|
-
rap?:
|
|
37
|
+
export interface LecAssRap extends LecassrapRaw {
|
|
38
|
+
rap?: RapRaw;
|
|
39
39
|
}
|
|
40
|
-
export interface Lecture extends
|
|
41
|
-
typlec?:
|
|
42
|
-
lecassidts?:
|
|
40
|
+
export interface Lecture extends LectureRaw {
|
|
41
|
+
typlec?: Typlec;
|
|
42
|
+
lecassidts?: LecassRaw["lecassidt"][];
|
|
43
43
|
lecasss?: LecAss[];
|
|
44
44
|
}
|
|
45
|
-
export interface Loi extends
|
|
46
|
-
deccoc?:
|
|
47
|
-
etaloi?:
|
|
48
|
-
lecidts?:
|
|
45
|
+
export interface Loi extends LoiRaw {
|
|
46
|
+
deccoc?: Deccoc;
|
|
47
|
+
etaloi?: Etaloi;
|
|
48
|
+
lecidts?: LectureRaw["lecidt"][];
|
|
49
49
|
lectures?: Lecture[];
|
|
50
|
-
typloi?:
|
|
50
|
+
typloi?: Typloi;
|
|
51
51
|
}
|
|
52
|
-
export interface Rap extends
|
|
53
|
-
denrap?:
|
|
54
|
-
docattcles?:
|
|
52
|
+
export interface Rap extends RapRaw {
|
|
53
|
+
denrap?: Denrap;
|
|
54
|
+
docattcles?: DocattRaw["docattcle"][];
|
|
55
55
|
docatts?: DocAtt[];
|
|
56
|
-
ecrnums?:
|
|
56
|
+
ecrnums?: EcrRaw["ecrnum"][];
|
|
57
57
|
ecrs?: Ecr[];
|
|
58
|
-
orgcods?:
|
|
58
|
+
orgcods?: Raporg["orgcod"][];
|
|
59
59
|
orgs?: Org[];
|
|
60
60
|
}
|
|
61
|
-
export interface Texte extends
|
|
61
|
+
export interface Texte extends TexteRaw {
|
|
62
62
|
ecrs?: Ecr[];
|
|
63
|
-
ecrnums?:
|
|
64
|
-
libtypurl?:
|
|
63
|
+
ecrnums?: EcrRaw["ecrnum"][];
|
|
64
|
+
libtypurl?: Typurl["libtypurl"];
|
|
65
65
|
org?: Org;
|
|
66
|
-
oritxt:
|
|
67
|
-
typtxt?:
|
|
68
|
-
txtAmeli?:
|
|
69
|
-
txtAmeliId:
|
|
66
|
+
oritxt: Oritxt;
|
|
67
|
+
typtxt?: Typtxt;
|
|
68
|
+
txtAmeli?: TxtAmeliCustom;
|
|
69
|
+
txtAmeliId: TxtAmeli["id"];
|
|
70
70
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export type { Question };
|
|
1
|
+
import { TamQuestions } from "../raw_types/questions";
|
|
2
|
+
export type { TamQuestions as Question };
|
package/lib/src/types/sens.d.ts
CHANGED
|
@@ -1,26 +1,19 @@
|
|
|
1
1
|
import { describe, expect, it } from "vitest";
|
|
2
|
-
import {
|
|
2
|
+
import { buildGeneratedTableManifest, extractPrefixedTableNamesFromGeneratedManifest, prefixedName, senatSchemaName, stagingSchemaName, stripDatasetPrefix, } from "../src/scripts/shared/prefixed_tables";
|
|
3
3
|
describe("prefixed table helpers", () => {
|
|
4
4
|
it("builds prefixed table and staging schema names", () => {
|
|
5
5
|
expect(prefixedName("dosleg", "texte")).toBe("dosleg_texte");
|
|
6
6
|
expect(stagingSchemaName("questions")).toBe("questions_staging");
|
|
7
7
|
expect(senatSchemaName).toBe("senat");
|
|
8
8
|
});
|
|
9
|
-
it("
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
const normalized = normalizeGeneratedDefinition(definition, "ameli");
|
|
14
|
-
expect(normalized).toContain("AUTO-GENERATED FILE");
|
|
15
|
-
expect(normalized).not.toContain("AUTO-GENERATED FILE @");
|
|
16
|
-
expect(normalized).toContain("export namespace amdFields");
|
|
17
|
-
expect(normalized).toContain("export interface amd");
|
|
18
|
-
expect(normalized).toContain("export interface ses");
|
|
19
|
-
expect(normalized).not.toContain("ameli_amd");
|
|
9
|
+
it("strips the dataset prefix from generated type names", () => {
|
|
10
|
+
expect(stripDatasetPrefix("ameli_amd", "ameli")).toBe("amd");
|
|
11
|
+
expect(stripDatasetPrefix("ameli_txt_ameli", "ameli")).toBe("txt_ameli");
|
|
12
|
+
expect(stripDatasetPrefix("amd", "ameli")).toBe("amd");
|
|
20
13
|
});
|
|
21
|
-
it("extracts expected prefixed public tables from generated
|
|
22
|
-
const
|
|
23
|
-
expect(
|
|
14
|
+
it("extracts expected prefixed public tables from generated manifests", () => {
|
|
15
|
+
const manifest = buildGeneratedTableManifest("ameli", ["ameli_amd", "ameli_ses", "ameli_txt_ameli"]);
|
|
16
|
+
expect(extractPrefixedTableNamesFromGeneratedManifest(manifest, "ameli")).toEqual([
|
|
24
17
|
"ameli_amd",
|
|
25
18
|
"ameli_ses",
|
|
26
19
|
"ameli_txt_ameli",
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { readFileSync } from "fs";
|
|
2
2
|
import { describe, expect, it } from "vitest";
|
|
3
3
|
import { datasets } from "../src/datasets";
|
|
4
|
-
import {
|
|
4
|
+
import { extractPrefixedTableNamesFromGeneratedManifest, getGeneratedTableManifestPath, } from "../src/scripts/shared/prefixed_tables";
|
|
5
5
|
describe("generated raw types coverage", () => {
|
|
6
6
|
it("maps each generated dataset definition to prefixed senat tables", () => {
|
|
7
7
|
for (const dataset of Object.values(datasets)) {
|
|
8
|
-
const
|
|
9
|
-
const tableNames =
|
|
8
|
+
const manifest = readFileSync(getGeneratedTableManifestPath(dataset.database), { encoding: "utf8" });
|
|
9
|
+
const tableNames = extractPrefixedTableNamesFromGeneratedManifest(manifest, dataset.database);
|
|
10
10
|
expect(tableNames.length).toBeGreaterThan(0);
|
|
11
11
|
expect(tableNames.every((tableName) => tableName.startsWith(`${dataset.database}_`))).toBe(true);
|
|
12
12
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tricoteuses/senat",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
4
|
"description": "Handle French Sénat's open data",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"France",
|
|
@@ -46,6 +46,7 @@
|
|
|
46
46
|
"access": "public"
|
|
47
47
|
},
|
|
48
48
|
"scripts": {
|
|
49
|
+
"postinstall": "patch-package",
|
|
49
50
|
"build": "tsc",
|
|
50
51
|
"build:types": "tsc --emitDeclarationOnly",
|
|
51
52
|
"data:convert_data": "tsx src/scripts/convert_data.ts",
|
|
@@ -86,7 +87,6 @@
|
|
|
86
87
|
"zod": "^4.3.5"
|
|
87
88
|
},
|
|
88
89
|
"devDependencies": {
|
|
89
|
-
"@typed-code/schemats": "^5.0.1",
|
|
90
90
|
"@types/cheerio": "^1.0.0",
|
|
91
91
|
"@types/command-line-args": "^5.0.0",
|
|
92
92
|
"@types/fs-extra": "^11.0.4",
|
|
@@ -99,6 +99,10 @@
|
|
|
99
99
|
"cross-env": "^10.1.0",
|
|
100
100
|
"eslint": "^9.39.2",
|
|
101
101
|
"globals": "^17.0.0",
|
|
102
|
+
"kanel": "^4.0.1",
|
|
103
|
+
"kanel-zod": "^4.0.0",
|
|
104
|
+
"patch-package": "^8.0.1",
|
|
105
|
+
"postinstall-postinstall": "^2.1.0",
|
|
102
106
|
"prettier": "^3.5.3",
|
|
103
107
|
"tsx": "^4.21.0",
|
|
104
108
|
"typescript": "^5.9.3",
|