@deterministic-code/datasources-cli 0.1.11 → 0.1.12
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/dist/generate/CSharpRepositories.js +28 -23
- package/dist/generate/DeterministicProvider.js +13 -11
- package/dist/generate/RustRepositories.js +34 -20
- package/dist/generate/TypeScriptRepositories.js +24 -13
- package/dist/generate/applyGenerate.js +6 -1
- package/dist/generate/applyMigrations.js +5 -1
- package/dist/generate/storedProcedures.js +5 -0
- package/dist/generate/writeLists.js +12 -18
- package/dist/parse.js +6 -0
- package/dist/run.js +1 -0
- package/node_modules/@deterministic-code/datasources-code-generators/dist/nestedSelectTypes.d.ts +5 -0
- package/node_modules/@deterministic-code/datasources-code-generators/dist/nestedSelectTypes.js +64 -0
- package/node_modules/@deterministic-code/datasources-code-generators/package.json +7 -7
- package/package.json +8 -8
|
@@ -7,8 +7,29 @@ import {
|
|
|
7
7
|
pluralPascal,
|
|
8
8
|
repositoryFile
|
|
9
9
|
} from "./RepositoryModel.js";
|
|
10
|
-
import { writeList } from "./writeLists.js";
|
|
11
|
-
const
|
|
10
|
+
import { dialectsOf, pad, quote, writeList } from "./writeLists.js";
|
|
11
|
+
const csharpOptionsBody = (model) => `Entity = "${quote(model.entity)}",
|
|
12
|
+
EntityMapper = new DictionaryMapper<string>(new Dictionary<string, string> { ["${quote(model.entity)}"] = "${quote(model.table)}" }),
|
|
13
|
+
PropertyMappings = new DictionaryMapper<string>(new Dictionary<string, string>
|
|
14
|
+
{
|
|
15
|
+
${model.propertyMappings.map((pair) => ` ["${quote(pair.from)}"] = "${quote(pair.to)}",`).join("\n")}
|
|
16
|
+
}),
|
|
17
|
+
Ids = ${writeList("csharp", model.ids)},
|
|
18
|
+
ReadProperties = ${writeList("csharp", model.readProperties)},
|
|
19
|
+
CreateProperties = ${writeList("csharp", model.createProperties)},
|
|
20
|
+
UpdateProperties = ${writeList("csharp", model.updateProperties)},`;
|
|
21
|
+
const csharpOptions = (model, indent) => `new()
|
|
22
|
+
${" ".repeat(indent)}{
|
|
23
|
+
${pad(csharpOptionsBody(model), indent + 4)}
|
|
24
|
+
${" ".repeat(indent)}}`;
|
|
25
|
+
const optionsMethod = (model, type) => {
|
|
26
|
+
const dialects = dialectsOf(model);
|
|
27
|
+
return dialects.length === 0 ? ` static CRUDRepositoryOptions<${type}> Options() => ${csharpOptions(model, 4)};` : ` static CRUDRepositoryOptions<${type}> Options(IConnection connection) =>
|
|
28
|
+
new CRUDRepositoryBuilder<${type}>()
|
|
29
|
+
.Add(${csharpOptions(model, 12)})${dialects.map(([engine, planned]) => `
|
|
30
|
+
.Add("${quote(engine)}", ${csharpOptions({ entity: model.entity, ...planned }, 12)})`).join("")}
|
|
31
|
+
.For(connection);`;
|
|
32
|
+
};
|
|
12
33
|
const enrichment = (item, host = "ctor") => {
|
|
13
34
|
const via = childField("csharp", item.via, item.viaType);
|
|
14
35
|
const head = `"${quote(item.destination)}", "${quote(item.source)}", "${quote(item.relatedKey)}"`;
|
|
@@ -46,16 +67,17 @@ const occMethods = (model, type) => {
|
|
|
46
67
|
}
|
|
47
68
|
`;
|
|
48
69
|
};
|
|
70
|
+
const optionsCall = (model) => dialectsOf(model).length === 0 ? "Options()" : "Options(connection)";
|
|
49
71
|
const createReadonly = (model, type) => {
|
|
50
72
|
const children = childModels(model);
|
|
51
73
|
if (children.length === 0 && model.enrichments.length === 0)
|
|
52
74
|
return ` public static IReadonlyRepository<${type}> CreateReadonly(IConnection connection) =>
|
|
53
|
-
new ReadonlyRepository<${type}>(connection,
|
|
75
|
+
new ReadonlyRepository<${type}>(connection, ${optionsCall(model)});`;
|
|
54
76
|
const locals = children.map((child) => ` var ${childField("csharp", child.specName, child.typeName)} = new ${childClass(child.typeName)}(connection);`);
|
|
55
77
|
const enrich = model.enrichments.map((item) => ` ${enrichment(item, "readonly")}`);
|
|
56
78
|
return ` public static IReadonlyRepository<${type}> CreateReadonly(IConnection connection)
|
|
57
79
|
{
|
|
58
|
-
var repo = new ReadonlyRepository<${type}>(connection,
|
|
80
|
+
var repo = new ReadonlyRepository<${type}>(connection, ${optionsCall(model)});
|
|
59
81
|
${locals.join("\n")}
|
|
60
82
|
${enrich.join("\n")}
|
|
61
83
|
return repo;
|
|
@@ -70,19 +92,10 @@ const csharpRepositories = (models) => {
|
|
|
70
92
|
...children.map((child) => ` ${childField("csharp", child.specName, child.typeName)} = new ${childClass(child.typeName)}(connection);`),
|
|
71
93
|
...model.enrichments.map((item) => ` ${enrichment(item)}`)
|
|
72
94
|
];
|
|
73
|
-
const
|
|
74
|
-
{
|
|
75
|
-
${model.propertyMappings.map((pair) => ` ["${quote(pair.from)}"] = "${quote(pair.to)}",`).join("\n")}
|
|
76
|
-
}),
|
|
77
|
-
`;
|
|
78
|
-
const constructor = lines.length === 0 ? ` public ${model.typeName}Repository(IConnection connection) : base(connection, Options(connection)) { }` : ` public ${model.typeName}Repository(IConnection connection) : base(connection, Options(connection))
|
|
95
|
+
const constructor = lines.length === 0 ? ` public ${model.typeName}Repository(IConnection connection) : base(connection, ${optionsCall(model)}) { }` : ` public ${model.typeName}Repository(IConnection connection) : base(connection, ${optionsCall(model)})
|
|
79
96
|
{
|
|
80
97
|
${lines.join("\n")}
|
|
81
98
|
}`;
|
|
82
|
-
const ids = model.ids.map((name) => `"${quote(name)}"`).join(", ");
|
|
83
|
-
const read = model.readProperties.map((name) => `"${quote(name)}"`).join(", ");
|
|
84
|
-
const create = writeList("csharp", model, "create");
|
|
85
|
-
const update = writeList("csharp", model, "update");
|
|
86
99
|
const implementsOcc = model.optimisticConcurrency === void 0 ? "" : `, ICRUDRepository<${type}>`;
|
|
87
100
|
const usings = model.optimisticConcurrency === void 0 ? "using Datasources;\n" : "using Datasources;\nusing static Datasources.Cmp;\n";
|
|
88
101
|
return content(
|
|
@@ -96,15 +109,7 @@ ${fields.length === 0 ? "" : `${fields.join("\n")}
|
|
|
96
109
|
|
|
97
110
|
${createReadonly(model, type)}
|
|
98
111
|
${occMethods(model, type)}
|
|
99
|
-
|
|
100
|
-
{
|
|
101
|
-
Entity = "${quote(model.entity)}",
|
|
102
|
-
EntityMapper = new DictionaryMapper<string>(new Dictionary<string, string> { ["${quote(model.entity)}"] = "${quote(model.table)}" }),
|
|
103
|
-
${mapping} Ids = [${ids}],
|
|
104
|
-
ReadProperties = [${read}],
|
|
105
|
-
CreateProperties = ${create},
|
|
106
|
-
UpdateProperties = ${update},
|
|
107
|
-
};
|
|
112
|
+
${optionsMethod(model, type)}
|
|
108
113
|
}
|
|
109
114
|
`
|
|
110
115
|
);
|
|
@@ -23,6 +23,14 @@ const repositoryPacks = {
|
|
|
23
23
|
rust: rustRepositories
|
|
24
24
|
};
|
|
25
25
|
const context = (request) => ({ reader: request.reader, settings: request.settings });
|
|
26
|
+
const optionsOf = (model) => ({
|
|
27
|
+
table: model.table,
|
|
28
|
+
ids: model.ids,
|
|
29
|
+
readProperties: model.readProperties,
|
|
30
|
+
createProperties: model.createProperties,
|
|
31
|
+
updateProperties: model.updateProperties,
|
|
32
|
+
propertyMappings: model.propertyMappings
|
|
33
|
+
});
|
|
26
34
|
const withProviderWrites = async (request, models) => {
|
|
27
35
|
const byProvider = {};
|
|
28
36
|
for (const engine of await datasourceEnginesOf(request.reader, request.settings)) {
|
|
@@ -36,17 +44,11 @@ const withProviderWrites = async (request, models) => {
|
|
|
36
44
|
}
|
|
37
45
|
return Object.keys(byProvider).length === 0 ? models : models.map((model) => ({
|
|
38
46
|
...model,
|
|
39
|
-
|
|
40
|
-
Object.entries(byProvider).map(([engine, planned]) =>
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
{
|
|
45
|
-
create: found?.createProperties ?? model.createProperties,
|
|
46
|
-
update: found?.updateProperties ?? model.updateProperties
|
|
47
|
-
}
|
|
48
|
-
];
|
|
49
|
-
})
|
|
47
|
+
optionsByProvider: Object.fromEntries(
|
|
48
|
+
Object.entries(byProvider).map(([engine, planned]) => [
|
|
49
|
+
engine,
|
|
50
|
+
optionsOf(planned.find((item) => item.specName === model.specName) ?? model)
|
|
51
|
+
])
|
|
50
52
|
)
|
|
51
53
|
}));
|
|
52
54
|
};
|
|
@@ -1,24 +1,39 @@
|
|
|
1
1
|
import { content } from "@deterministic-code/generators-common/generate-entry";
|
|
2
2
|
import { childField, children as childModels, isLinked, pluralSnake, repositoryFile } from "./RepositoryModel.js";
|
|
3
|
-
import { writeList } from "./writeLists.js";
|
|
4
|
-
const rustString = (value) => `"${value
|
|
5
|
-
const uses = `use datasources::{Association, Connection, CRUDRepository, CRUDRepositoryOptions, DictionaryMapper, Enrichment, EnrichmentPropertyType, ICRUDRepository, ReadonlyRepository, ReadonlyRepositoryMappingOptions, Result};
|
|
3
|
+
import { dialectsOf, pad, quote, writeList } from "./writeLists.js";
|
|
4
|
+
const rustString = (value) => `"${quote(value)}"`;
|
|
5
|
+
const uses = (model) => dialectsOf(model).length === 0 ? `use datasources::{Association, Connection, CRUDRepository, CRUDRepositoryOptions, DictionaryMapper, Enrichment, EnrichmentPropertyType, ICRUDRepository, ReadonlyRepository, ReadonlyRepositoryMappingOptions, Result};
|
|
6
|
+
use std::sync::Arc;` : `use datasources::{Association, Connection, CRUDRepository, CRUDRepositoryBuilder, CRUDRepositoryOptions, DictionaryMapper, Enrichment, EnrichmentPropertyType, ICRUDRepository, ReadonlyRepository, ReadonlyRepositoryMappingOptions, Result};
|
|
6
7
|
use std::sync::Arc;`;
|
|
7
|
-
const
|
|
8
|
-
const mapping = model.propertyMappings.length === 0 ? "" : `
|
|
9
|
-
${model.propertyMappings.map((pair) => `
|
|
10
|
-
|
|
8
|
+
const rustFill = (model) => {
|
|
9
|
+
const mapping = model.propertyMappings.length === 0 ? "" : `options.property_mappings = Some(Arc::new(DictionaryMapper::new([
|
|
10
|
+
${model.propertyMappings.map((pair) => ` (${rustString(pair.from)}.into(), ${rustString(pair.to)}.into()),`).join("\n")}
|
|
11
|
+
])));
|
|
11
12
|
`;
|
|
12
|
-
return `
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
${mapping}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
13
|
+
return `let mut options = CRUDRepositoryOptions::default();
|
|
14
|
+
options.entity = ${rustString(model.entity)}.into();
|
|
15
|
+
options.entity_mapper = Some(Arc::new(DictionaryMapper::new([
|
|
16
|
+
(${rustString(model.entity)}.into(), ${rustString(model.table)}.into()),
|
|
17
|
+
])));
|
|
18
|
+
${mapping}options.ids = ${writeList("rust", model.ids)};
|
|
19
|
+
options.read_properties = ${writeList("rust", model.readProperties)};
|
|
20
|
+
options.create_properties = Some(${writeList("rust", model.createProperties)});
|
|
21
|
+
options.update_properties = Some(${writeList("rust", model.updateProperties)});`;
|
|
22
|
+
};
|
|
23
|
+
const rustBlock = (model) => {
|
|
24
|
+
const dialects = dialectsOf(model);
|
|
25
|
+
return dialects.length === 0 ? rustFill(model) : `let options = CRUDRepositoryBuilder::new()
|
|
26
|
+
.add({
|
|
27
|
+
${pad(rustFill(model), 8)}
|
|
28
|
+
options
|
|
29
|
+
})${dialects.map(([engine, planned]) => `
|
|
30
|
+
.add_for(${rustString(engine)}, {
|
|
31
|
+
${pad(rustFill({ entity: model.entity, ...planned }), 8)}
|
|
32
|
+
options
|
|
33
|
+
})`).join("")}
|
|
34
|
+
.for_connection(connection.as_ref())?;`;
|
|
21
35
|
};
|
|
36
|
+
const crudOptions = (model) => pad(rustBlock(model), 4);
|
|
22
37
|
const readonlyOptions = (model) => {
|
|
23
38
|
const mapping = model.propertyMappings.length === 0 ? "" : ` options.property_mappings = Some(Arc::new(DictionaryMapper::new([
|
|
24
39
|
${model.propertyMappings.map((pair) => ` (${rustString(pair.from)}.into(), ${rustString(pair.to)}.into()),`).join("\n")}
|
|
@@ -165,8 +180,7 @@ ${where}
|
|
|
165
180
|
}
|
|
166
181
|
`;
|
|
167
182
|
};
|
|
168
|
-
const
|
|
169
|
-
const plain = (model, nested) => `${uses}
|
|
183
|
+
const plain = (model, nested) => `${uses(model)}
|
|
170
184
|
|
|
171
185
|
pub fn ${model.specName}_repository(connection: Arc<dyn Connection>) -> Result<CRUDRepository> {
|
|
172
186
|
${crudOptions(model)}
|
|
@@ -183,7 +197,7 @@ const wrapped = (model, nested) => {
|
|
|
183
197
|
).join("\n");
|
|
184
198
|
const assign = children.map((child) => child.specName).join(", ");
|
|
185
199
|
const enrich = model.enrichments.map((item) => ` ${enrichment(item, true, nested)}`).join("\n");
|
|
186
|
-
return `${uses}
|
|
200
|
+
return `${uses(model)}
|
|
187
201
|
use std::ops::Deref;
|
|
188
202
|
|
|
189
203
|
pub struct ${type} {
|
|
@@ -193,7 +207,7 @@ ${fields}
|
|
|
193
207
|
|
|
194
208
|
impl ${type} {
|
|
195
209
|
pub fn new(connection: Arc<dyn Connection>) -> Result<Self> {
|
|
196
|
-
${pad(crudOptions(model))}
|
|
210
|
+
${pad(crudOptions(model), 4)}
|
|
197
211
|
${childLets(children, nested)}
|
|
198
212
|
let mut repo = CRUDRepository::new(Arc::clone(&connection), options)?;
|
|
199
213
|
${enrich}
|
|
@@ -1,8 +1,23 @@
|
|
|
1
1
|
import { createCasingStrategy } from "@deterministic-code/generators-common/casing-strategy";
|
|
2
2
|
import { content } from "@deterministic-code/generators-common/generate-entry";
|
|
3
3
|
import { childClass, childField, children as childModels, isLinked, pluralCamel, repositoryFile } from "./RepositoryModel.js";
|
|
4
|
-
import { writeList } from "./writeLists.js";
|
|
5
|
-
const
|
|
4
|
+
import { dialectsOf, quote, writeList } from "./writeLists.js";
|
|
5
|
+
const withEntity = (model, planned) => ({ entity: model.entity, ...planned });
|
|
6
|
+
const entityAndMapping = (model) => {
|
|
7
|
+
const mapping = model.propertyMappings.length === 0 ? "" : `propertyMappings: new DictionaryMapper([${model.propertyMappings.map((pair) => `["${quote(pair.from)}", "${quote(pair.to)}"]`).join(", ")}]), `;
|
|
8
|
+
return `entity: "${quote(model.entity)}", entityMapper: new DictionaryMapper([["${quote(model.entity)}", "${quote(model.table)}"]]), ${mapping}`;
|
|
9
|
+
};
|
|
10
|
+
const tsOptions = (model) => `{ ${entityAndMapping(model)}ids: ${writeList("typescript", model.ids)}, createProperties: ${writeList("typescript", model.createProperties)}, updateProperties: ${writeList("typescript", model.updateProperties)}, readProperties: ${writeList("typescript", model.readProperties)} }`;
|
|
11
|
+
const tsReadonly = (model) => `{ ${entityAndMapping(model)}readProperties: ${writeList("typescript", model.readProperties)} }`;
|
|
12
|
+
const tsOptionsExpr = (model) => dialectsOf(model).length === 0 ? tsOptions(model) : "options(connection)";
|
|
13
|
+
const tsBuilder = (name, model, write) => `const ${name} = (connection: IConnection) =>
|
|
14
|
+
new CRUDRepositoryBuilder<${model.typeName}>()
|
|
15
|
+
.add(${write(model)})${dialectsOf(model).map(([engine, planned]) => `
|
|
16
|
+
.add("${quote(engine)}", ${write(withEntity(model, planned))})`).join("")}
|
|
17
|
+
.for(connection);
|
|
18
|
+
|
|
19
|
+
`;
|
|
20
|
+
const tsOptionsFn = (model) => dialectsOf(model).length === 0 ? "" : `${tsBuilder("options", model, tsOptions)}${tsBuilder("readonlyOptions", model, tsReadonly)}`;
|
|
6
21
|
const enrichment = (item, host) => {
|
|
7
22
|
const via = host === "this" ? `this.${childField("typescript", item.via, item.viaType)}` : childField("typescript", item.via, item.viaType);
|
|
8
23
|
const related = item.relatedProperty === void 0 ? "" : `, relatedProperty: "${quote(item.relatedProperty)}"`;
|
|
@@ -13,7 +28,8 @@ const enrichment = (item, host) => {
|
|
|
13
28
|
}
|
|
14
29
|
return `${host}.addEnriched({ destinationProperty: "${quote(item.destination)}", sourceProperty: "${quote(item.source)}", relatedKey: "${quote(item.relatedKey)}"${related}, type: "${type}" }, ${via});`;
|
|
15
30
|
};
|
|
16
|
-
const createReadonly = (model
|
|
31
|
+
const createReadonly = (model) => {
|
|
32
|
+
const options = dialectsOf(model).length === 0 ? tsReadonly(model) : "readonlyOptions(connection)";
|
|
17
33
|
const children = childModels(model);
|
|
18
34
|
if (children.length === 0 && model.enrichments.length === 0)
|
|
19
35
|
return ` static createReadonly = (connection: IConnection) => new ReadonlyRepository<${model.typeName}>(connection, ${options});`;
|
|
@@ -57,12 +73,6 @@ const typescriptRepositories = (models, settings) => {
|
|
|
57
73
|
const files = models.map((model) => {
|
|
58
74
|
const folder = model.typeHome === "view" ? "views" : "datasource";
|
|
59
75
|
const children = childModels(model);
|
|
60
|
-
const mapping = model.propertyMappings.length === 0 ? "" : `propertyMappings: new DictionaryMapper([${model.propertyMappings.map((pair) => `["${quote(pair.from)}", "${quote(pair.to)}"]`).join(", ")}]), `;
|
|
61
|
-
const head = `{ entity: "${quote(model.entity)}", entityMapper: new DictionaryMapper([["${quote(model.entity)}", "${quote(model.table)}"]]), ${mapping}`;
|
|
62
|
-
const writes = `createProperties: ${writeList("typescript", model, "create")}, updateProperties: ${writeList("typescript", model, "update")}`;
|
|
63
|
-
const read = `readProperties: [${model.readProperties.map((name) => `"${quote(name)}"`).join(", ")}] }`;
|
|
64
|
-
const crudOptions = `${head}ids: [${model.ids.map((id) => `"${quote(id)}"`).join(", ")}], ${writes}, ${read}`;
|
|
65
|
-
const readonlyOptions = `${head}${read}`;
|
|
66
76
|
const fields = children.map((child) => ` readonly ${childField("typescript", child.specName, child.typeName)}: ${childClass(child.typeName)};`);
|
|
67
77
|
const wrap = occWrap(model);
|
|
68
78
|
const lines = [
|
|
@@ -70,20 +80,21 @@ const typescriptRepositories = (models, settings) => {
|
|
|
70
80
|
...model.enrichments.map((item) => ` ${enrichment(item, "this")}`)
|
|
71
81
|
];
|
|
72
82
|
const imports = children.map((child) => `import { ${childClass(child.typeName)} } from "${repoImport(child.specName)}";`);
|
|
73
|
-
const
|
|
83
|
+
const builder = dialectsOf(model).length === 0 ? "" : "CRUDRepositoryBuilder, ";
|
|
84
|
+
const core = model.optimisticConcurrency === void 0 ? `import { CRUDRepository, ${builder}DictionaryMapper, ReadonlyRepository, type IConnection } from "@deterministic-code/datasources";` : `import { CRUDRepository, ${builder}DatasourceError, DictionaryMapper, eq, ReadonlyRepository, Where, type IConnection } from "@deterministic-code/datasources";`;
|
|
74
85
|
return content(
|
|
75
86
|
repositoryFile("typescript", model),
|
|
76
87
|
`import type { ${model.typeName} } from "../types/generated/${folder}/${casing.convertFileName(model.specName)}";
|
|
77
88
|
${core}
|
|
78
89
|
${imports.length === 0 ? "" : `${imports.join("\n")}
|
|
79
90
|
`}
|
|
80
|
-
export class ${model.typeName}Repository extends CRUDRepository<${model.typeName}> {
|
|
91
|
+
${tsOptionsFn(model)}export class ${model.typeName}Repository extends CRUDRepository<${model.typeName}> {
|
|
81
92
|
${fields.length === 0 ? "" : `${fields.join("\n")}
|
|
82
93
|
`} constructor(connection: IConnection) {
|
|
83
|
-
super(connection, ${
|
|
94
|
+
super(connection, ${tsOptionsExpr(model)});
|
|
84
95
|
${wrap}${lines.length === 0 ? "" : `${lines.join("\n")}
|
|
85
96
|
`} }
|
|
86
|
-
${createReadonly(model
|
|
97
|
+
${createReadonly(model)}
|
|
87
98
|
}
|
|
88
99
|
`
|
|
89
100
|
);
|
|
@@ -2,13 +2,18 @@ import { isAbsolute, join } from "node:path";
|
|
|
2
2
|
import { memoryReader } from "@deterministic-code/generators-common/deterministic-reader";
|
|
3
3
|
import { loadSpec } from "./loadSpec.js";
|
|
4
4
|
import { providerByName } from "./providers.js";
|
|
5
|
+
import { withStoredProcedures } from "./storedProcedures.js";
|
|
5
6
|
import { writeEntries } from "./writeEntries.js";
|
|
6
7
|
import { wireRegister } from "../wireRegister.js";
|
|
7
8
|
const at = (root, path) => isAbsolute(path) ? path : join(root, path);
|
|
8
9
|
const applyGenerate = async (command, flags, deps) => {
|
|
9
10
|
const provider = providerByName(command.provider);
|
|
10
11
|
const loaded = await loadSpec(deps.fs, at(flags.cwd, command.input));
|
|
11
|
-
const request = {
|
|
12
|
+
const request = {
|
|
13
|
+
dialect: command.dialect,
|
|
14
|
+
reader: memoryReader(loaded.files),
|
|
15
|
+
settings: withStoredProcedures(loaded.settings, command.storedProcedures)
|
|
16
|
+
};
|
|
12
17
|
const entries = await {
|
|
13
18
|
all: async () => [
|
|
14
19
|
...await provider.generateTypes(request),
|
|
@@ -3,6 +3,7 @@ import { generate as generateSql } from "@deterministic-code/datasources-code-ge
|
|
|
3
3
|
import { memoryReader } from "@deterministic-code/generators-common/deterministic-reader";
|
|
4
4
|
import { loadSpec } from "./loadSpec.js";
|
|
5
5
|
import { providerByName } from "./providers.js";
|
|
6
|
+
import { withStoredProcedures } from "./storedProcedures.js";
|
|
6
7
|
import { writeEntries } from "./writeEntries.js";
|
|
7
8
|
const at = (root, path) => isAbsolute(path) ? path : join(root, path);
|
|
8
9
|
const afterMigrations = (filename) => filename.replace(/^[^/]+\/migrations\//, "");
|
|
@@ -16,7 +17,10 @@ const applyMigrations = async (command, flags, deps) => {
|
|
|
16
17
|
];
|
|
17
18
|
if (command.migrationsPath !== void 0 && listed.length > 1)
|
|
18
19
|
throw new Error("--migrations-path requires a single SQL dialect in backend.datasources");
|
|
19
|
-
const entries = await generateSql({
|
|
20
|
+
const entries = await generateSql({
|
|
21
|
+
reader: memoryReader(loaded.files),
|
|
22
|
+
settings: withStoredProcedures(loaded.settings, command.storedProcedures)
|
|
23
|
+
});
|
|
20
24
|
const remapped = command.migrationsPath === void 0 ? entries : entries.map((entry) => ({ ...entry, filename: afterMigrations(entry.filename) }));
|
|
21
25
|
const root = at(flags.cwd, command.migrationsPath ?? command.migrationsRoot);
|
|
22
26
|
if (flags.dryRun) {
|
|
@@ -1,25 +1,19 @@
|
|
|
1
|
-
const quote = (value) => value.replaceAll("\\", "\\\\").replaceAll('"', '\\"');
|
|
2
|
-
const namesOf = (model, kind) => kind === "create" ? model.createProperties : model.updateProperties;
|
|
3
1
|
const same = (left, right) => left.length === right.length && left.every((name, index) => name === right[index]);
|
|
2
|
+
const samePairs = (left, right) => left.length === right.length && left.every((pair, index) => pair.from === right[index]?.from && pair.to === right[index]?.to);
|
|
3
|
+
const sameOptions = (left, right) => left.table === right.table && same(left.ids, right.ids) && same(left.readProperties, right.readProperties) && same(left.createProperties, right.createProperties) && same(left.updateProperties, right.updateProperties) && samePairs(left.propertyMappings, right.propertyMappings);
|
|
4
|
+
const dialectsOf = (model) => Object.entries(model.optionsByProvider ?? {}).filter(([, planned]) => !sameOptions(model, planned));
|
|
5
|
+
const quote = (value) => value.replaceAll("\\", "\\\\").replaceAll('"', '\\"');
|
|
6
|
+
const pad = (text, spaces) => {
|
|
7
|
+
const prefix = " ".repeat(spaces);
|
|
8
|
+
return text.split("\n").map((line) => `${prefix}${line}`).join("\n");
|
|
9
|
+
};
|
|
4
10
|
const csharpList = (names) => `[${names.map((name) => `"${quote(name)}"`).join(", ")}]`;
|
|
5
11
|
const tsList = (names) => `[${names.map((name) => `"${quote(name)}"`).join(", ")}]`;
|
|
6
12
|
const rustList = (names) => `vec![${names.map((name) => `"${quote(name)}".into()`).join(", ")}]`;
|
|
7
|
-
const
|
|
8
|
-
const writeList = (dialect, model, kind) => {
|
|
9
|
-
const by = model.writesByProvider;
|
|
10
|
-
const listed = namesOf(model, kind);
|
|
11
|
-
if (by === void 0 || Object.values(by).every((item) => same(item[kind], listed)))
|
|
12
|
-
return dialect === "csharp" ? csharpList(listed) : dialect === "typescript" ? tsList(listed) : rustList(listed);
|
|
13
|
-
const fallbackList = fallback(model, kind);
|
|
14
|
-
return dialect === "csharp" ? `connection.Provider switch
|
|
15
|
-
{
|
|
16
|
-
${Object.entries(by).filter(([engine]) => engine !== "sqlite").map(([engine, item]) => ` "${quote(engine)}" => ${csharpList(item[kind])},`).join("\n")}
|
|
17
|
-
_ => ${csharpList(fallbackList)},
|
|
18
|
-
}` : dialect === "typescript" ? `({ ${Object.entries(by).filter(([engine]) => engine !== "sqlite").map(([engine, item]) => `${engine}: ${tsList(item[kind])}`).join(", ")} }[connection.provider] ?? ${tsList(fallbackList)})` : `match connection.provider() {
|
|
19
|
-
${Object.entries(by).filter(([engine]) => engine !== "sqlite").map(([engine, item]) => `${engine === "mysql" || engine === "postgres" ? `"${engine}"` : `"${quote(engine)}"`} => ${rustList(item[kind])},`).join("\n ")}
|
|
20
|
-
_ => ${rustList(fallbackList)},
|
|
21
|
-
}`;
|
|
22
|
-
};
|
|
13
|
+
const writeList = (dialect, names) => dialect === "csharp" ? csharpList(names) : dialect === "typescript" ? tsList(names) : rustList(names);
|
|
23
14
|
export {
|
|
15
|
+
dialectsOf,
|
|
16
|
+
pad,
|
|
17
|
+
quote,
|
|
24
18
|
writeList
|
|
25
19
|
};
|
package/dist/parse.js
CHANGED
|
@@ -29,12 +29,14 @@ generate <all|types|repositories|datasource>
|
|
|
29
29
|
--no-migrate Skip migrate-up (generate all applies them)
|
|
30
30
|
--migrations-root <dir> Root prefix for generate all (default: sql)
|
|
31
31
|
--migrations-path <dir> One migrations directory (single SQL dialect)
|
|
32
|
+
--stored-procedures Emit stored-procedure migrations when the dialect can
|
|
32
33
|
|
|
33
34
|
migrations create SQL migrations from the spec (migraters layout)
|
|
34
35
|
--provider <name> Spec provider (default: deterministic)
|
|
35
36
|
--input <dir> Deterministic project (default: ./deterministic)
|
|
36
37
|
--migrations-root <dir> Root prefix (default: sql)
|
|
37
38
|
--migrations-path <dir> One migrations directory (single SQL dialect)
|
|
39
|
+
--stored-procedures Emit stored-procedure migrations when the dialect can
|
|
38
40
|
|
|
39
41
|
version Snapshot top-level YAML as the next version-*/diff.yaml
|
|
40
42
|
--cwd <dir> Consumer project directory (default: .)
|
|
@@ -54,6 +56,7 @@ const parseArgs = (argv, cwd = ".") => {
|
|
|
54
56
|
output: "generated",
|
|
55
57
|
migrations: true,
|
|
56
58
|
migrate: true,
|
|
59
|
+
storedProcedures: false,
|
|
57
60
|
migrationsRoot: "sql",
|
|
58
61
|
migrationsPath: ""
|
|
59
62
|
};
|
|
@@ -76,6 +79,7 @@ const parseArgs = (argv, cwd = ".") => {
|
|
|
76
79
|
else if (arg.startsWith("--output=")) generate.output = arg.slice("--output=".length);
|
|
77
80
|
else if (arg === "--no-migrations") generate.migrations = false;
|
|
78
81
|
else if (arg === "--no-migrate") generate.migrate = false;
|
|
82
|
+
else if (arg === "--stored-procedures") generate.storedProcedures = true;
|
|
79
83
|
else if (arg === "--migrations-root") generate.migrationsRoot = take(argv, ++i, "--migrations-root");
|
|
80
84
|
else if (arg.startsWith("--migrations-root=")) generate.migrationsRoot = arg.slice("--migrations-root=".length);
|
|
81
85
|
else if (arg === "--migrations-path") generate.migrationsPath = take(argv, ++i, "--migrations-path");
|
|
@@ -105,6 +109,7 @@ ${help}`);
|
|
|
105
109
|
kind: "migrations-create",
|
|
106
110
|
provider: generate.provider,
|
|
107
111
|
input: generate.input,
|
|
112
|
+
storedProcedures: generate.storedProcedures,
|
|
108
113
|
migrationsRoot: generate.migrationsRoot,
|
|
109
114
|
...generate.migrationsPath === "" ? {} : { migrationsPath: generate.migrationsPath }
|
|
110
115
|
};
|
|
@@ -130,6 +135,7 @@ ${help}`);
|
|
|
130
135
|
output: generate.output,
|
|
131
136
|
migrations: generate.migrations,
|
|
132
137
|
migrate: generate.migrate,
|
|
138
|
+
storedProcedures: generate.storedProcedures,
|
|
133
139
|
migrationsRoot: generate.migrationsRoot,
|
|
134
140
|
...generate.migrationsPath === "" ? {} : { migrationsPath: generate.migrationsPath }
|
|
135
141
|
};
|
package/dist/run.js
CHANGED
|
@@ -43,6 +43,7 @@ const dispatch = async (command, flags, deps) => {
|
|
|
43
43
|
kind: "migrations-create",
|
|
44
44
|
provider: command.provider,
|
|
45
45
|
input: command.input,
|
|
46
|
+
storedProcedures: command.storedProcedures,
|
|
46
47
|
migrationsRoot: command.migrationsRoot,
|
|
47
48
|
...command.migrationsPath === void 0 ? {} : { migrationsPath: command.migrationsPath }
|
|
48
49
|
},
|
package/node_modules/@deterministic-code/datasources-code-generators/dist/nestedSelectTypes.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { NestedSelectBuilder } from "@deterministic-code/datasources";
|
|
2
|
+
import type { NestedSelectType } from "@deterministic-code/datasources";
|
|
3
|
+
import type { Type } from "@deterministic-code/deterministic-generators-common/parser";
|
|
4
|
+
export declare const nestedSelectFromSpec: (types: readonly Type[], root: string) => NestedSelectType;
|
|
5
|
+
export { NestedSelectBuilder };
|
package/node_modules/@deterministic-code/datasources-code-generators/dist/nestedSelectTypes.js
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { ChangeCasingMapper, NestedSelectBuilder, PluralizerMapper } from "@deterministic-code/datasources";
|
|
2
|
+
const pascal = new ChangeCasingMapper("snakeCase", "pascalCase");
|
|
3
|
+
const plural = new PluralizerMapper();
|
|
4
|
+
export const nestedSelectFromSpec = (types, root) => {
|
|
5
|
+
const tagged = types.some((item) => item.tags?.includes("datasource_type"));
|
|
6
|
+
const catalog = new Map(types
|
|
7
|
+
.filter((item) => !tagged || item.tags?.includes("datasource_type"))
|
|
8
|
+
.map((item) => [item.name, item]));
|
|
9
|
+
const name = catalog.has(root)
|
|
10
|
+
? root
|
|
11
|
+
: [...catalog.keys()].find((item) => entity(item) === root);
|
|
12
|
+
if (name === undefined)
|
|
13
|
+
throw new Error(`Unknown nested select type "${root}"`);
|
|
14
|
+
return build(catalog.get(name), catalog, new Set());
|
|
15
|
+
};
|
|
16
|
+
export { NestedSelectBuilder };
|
|
17
|
+
const build = (type, catalog, ancestors) => {
|
|
18
|
+
const next = new Set(ancestors);
|
|
19
|
+
next.add(type.name);
|
|
20
|
+
const key = entity(type.fields.find((field) => field.isId)?.name ?? type.ids?.[0] ?? "id");
|
|
21
|
+
const properties = type.fields
|
|
22
|
+
.filter((field) => field.references === undefined)
|
|
23
|
+
.map((field) => entity(field.name));
|
|
24
|
+
const objects = type.fields.flatMap((field) => {
|
|
25
|
+
const [table, column] = target(field);
|
|
26
|
+
if (!table || !catalog.has(table) || next.has(table))
|
|
27
|
+
return [];
|
|
28
|
+
return [{
|
|
29
|
+
name: relationName(field.name, table),
|
|
30
|
+
kind: "object",
|
|
31
|
+
from: entity(field.name),
|
|
32
|
+
to: entity(column),
|
|
33
|
+
target: build(catalog.get(table), catalog, next),
|
|
34
|
+
}];
|
|
35
|
+
});
|
|
36
|
+
const lists = [...catalog.values()].flatMap((child) => {
|
|
37
|
+
if (next.has(child.name))
|
|
38
|
+
return [];
|
|
39
|
+
return child.fields.flatMap((field) => {
|
|
40
|
+
const [table, column] = target(field);
|
|
41
|
+
if (table !== type.name || !column)
|
|
42
|
+
return [];
|
|
43
|
+
return [{
|
|
44
|
+
name: plural.map(entity(child.name)),
|
|
45
|
+
kind: "list",
|
|
46
|
+
from: key,
|
|
47
|
+
to: entity(field.name),
|
|
48
|
+
target: build(child, catalog, next),
|
|
49
|
+
}];
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
return { name: entity(type.name), key, properties, relations: [...objects, ...lists] };
|
|
53
|
+
};
|
|
54
|
+
const target = (field) => {
|
|
55
|
+
if (Array.isArray(field.references) && field.references.length === 2)
|
|
56
|
+
return [field.references[0] ?? "", field.references[1] ?? ""];
|
|
57
|
+
const [table, column] = String(field.references ?? "").split(".");
|
|
58
|
+
return [table ?? "", column ?? ""];
|
|
59
|
+
};
|
|
60
|
+
const relationName = (field, table) => {
|
|
61
|
+
const name = entity(field);
|
|
62
|
+
return name.endsWith("Id") && name.length > 2 ? name.slice(0, -2) : entity(table);
|
|
63
|
+
};
|
|
64
|
+
const entity = (name) => pascal.map(name);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deterministic-code/datasources-code-generators",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.12",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": "./dist/generateSql.js",
|
|
@@ -12,12 +12,12 @@
|
|
|
12
12
|
"./sql/common/write-inclusion": "./dist/sql/common/write-inclusion.js"
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|
|
15
|
-
"@deterministic-code/datasources": "^0.1.
|
|
16
|
-
"@deterministic-code/datasources-mysql": "^0.1.
|
|
17
|
-
"@deterministic-code/datasources-oracle": "^0.1.
|
|
18
|
-
"@deterministic-code/datasources-postgres": "^0.1.
|
|
19
|
-
"@deterministic-code/datasources-sqlite": "^0.1.
|
|
20
|
-
"@deterministic-code/datasources-sqlserver": "^0.1.
|
|
15
|
+
"@deterministic-code/datasources": "^0.1.12",
|
|
16
|
+
"@deterministic-code/datasources-mysql": "^0.1.12",
|
|
17
|
+
"@deterministic-code/datasources-oracle": "^0.1.12",
|
|
18
|
+
"@deterministic-code/datasources-postgres": "^0.1.12",
|
|
19
|
+
"@deterministic-code/datasources-sqlite": "^0.1.12",
|
|
20
|
+
"@deterministic-code/datasources-sqlserver": "^0.1.12",
|
|
21
21
|
"@deterministic-code/deterministic-generators-common": "0.1.0",
|
|
22
22
|
"@deterministic-code/generators-common": "0.0.1",
|
|
23
23
|
"pluralize": "^8.0.0",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deterministic-code/datasources-cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.12",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"datasources": "./bin/datasources.mjs"
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"access": "public"
|
|
14
14
|
},
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"@deterministic-code/datasources-code-generators": "0.1.
|
|
16
|
+
"@deterministic-code/datasources-code-generators": "0.1.12",
|
|
17
17
|
"@deterministic-code/generators-common": "0.0.1",
|
|
18
18
|
"@deterministic-code/deterministic-generators-common": "0.1.0",
|
|
19
19
|
"@deterministic-code/typescript-generators": "0.0.1",
|
|
@@ -21,12 +21,12 @@
|
|
|
21
21
|
"@deterministic-code/rust-generators": "0.0.1",
|
|
22
22
|
"@deterministic-code/generators-openapi": "0.0.1",
|
|
23
23
|
"@deterministic-code/patch-merger": "0.1.0",
|
|
24
|
-
"@deterministic-code/datasources": "^0.1.
|
|
25
|
-
"@deterministic-code/datasources-sqlite": "^0.1.
|
|
26
|
-
"@deterministic-code/datasources-mysql": "^0.1.
|
|
27
|
-
"@deterministic-code/datasources-postgres": "^0.1.
|
|
28
|
-
"@deterministic-code/datasources-sqlserver": "^0.1.
|
|
29
|
-
"@deterministic-code/datasources-oracle": "^0.1.
|
|
24
|
+
"@deterministic-code/datasources": "^0.1.12",
|
|
25
|
+
"@deterministic-code/datasources-sqlite": "^0.1.12",
|
|
26
|
+
"@deterministic-code/datasources-mysql": "^0.1.12",
|
|
27
|
+
"@deterministic-code/datasources-postgres": "^0.1.12",
|
|
28
|
+
"@deterministic-code/datasources-sqlserver": "^0.1.12",
|
|
29
|
+
"@deterministic-code/datasources-oracle": "^0.1.12",
|
|
30
30
|
"@inquirer/prompts": "^7.8.0",
|
|
31
31
|
"yaml": "^2.6.0",
|
|
32
32
|
"mustache": "^4.2.0",
|