Skip to content

Commit

Permalink
feat(code-gen,store): dynamically resolve query builder results based…
Browse files Browse the repository at this point in the history
… on the passed in builder

This only works in TypeScript projects.
  • Loading branch information
dirkdev98 committed Nov 6, 2024
1 parent 0b5a6b6 commit f0f7304
Show file tree
Hide file tree
Showing 17 changed files with 668 additions and 53 deletions.
3 changes: 1 addition & 2 deletions packages/cli/src/generated/common/types.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Generated by @compas/code-gen



export type CliFlagDefinition = {
"name": string;
"rawName": string;
Expand Down Expand Up @@ -94,5 +95,3 @@ export type CliCompletion =
"specification": "boolean"|"number"|"string"|"booleanOrString";
"description"?: string|undefined;
};


8 changes: 1 addition & 7 deletions packages/code-gen/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,7 @@
"./package.json": "./package.json"
},
"type": "module",
"keywords": [
"compas",
"generate",
"router",
"validation",
"code-gen"
],
"keywords": ["compas", "generate", "router", "validation", "code-gen"],
"license": "MIT",
"dependencies": {
"@compas/stdlib": "0.15.2"
Expand Down
9 changes: 8 additions & 1 deletion packages/code-gen/src/database/ts-postgres.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ export function tsPostgresGenerateUtils(generateContext) {
helperImportCollector.destructure("@compas/stdlib", "AppError");
helperTypeImportCollector.destructure("@compas/store", "QueryPart");
helperTypeImportCollector.destructure("@compas/store", "WrappedQueryPart");
helperTypeImportCollector.destructure("@compas/store", "WrappedQueryResult");

fileWrite(
helperFile,
Expand Down Expand Up @@ -156,7 +157,10 @@ export function tsPostgresCreateFile(generateContext, model) {
importCollector.destructure("@compas/stdlib", "isNil");
importCollector.destructure("@compas/stdlib", "AppError");

typeImportCollector.destructure("@compas/store", "Postgres");
typeImportCollector.destructure("@compas/store", "QueryPart");
typeImportCollector.destructure("@compas/store", "WrappedQueryPart");
typeImportCollector.destructure("@compas/store", "WrappedQueryResult");

fileWrite(file, `\nexport const ${model.name}Queries = {`);
fileContextSetIndent(file, 1);
Expand Down Expand Up @@ -1119,6 +1123,9 @@ export function tsPostgresGenerateQueryBuilder(
entityInformation: `$$(): any => ${relationInfo.modelInverse.name}QueryBuilderSpec$$`,
});
}

const fullTypeName = `${upperCaseFirst(model.group)}${upperCaseFirst(model.name)}`;

for (const relation of inverseRelations) {
const relationInfo = modelRelationGetInformation(relation);

Expand Down Expand Up @@ -1165,7 +1172,7 @@ export function tsPostgresGenerateQueryBuilder(
// Function
fileBlockStart(
file,
`export function query${upperCaseFirst(model.name)}(input: ${contextNames.queryBuilderType.inputType} = {}): WrappedQueryPart<${contextNames.queryResultType.outputType}>`,
`export function query${upperCaseFirst(model.name)}<QueryBuilder extends ${contextNames.queryBuilderType.inputType}>(input: QueryBuilder = {}): WrappedQueryResult<${fullTypeName}QueryResolver<QueryBuilder>>`,
);

// Input validation
Expand Down
2 changes: 2 additions & 0 deletions packages/code-gen/src/generate.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
} from "./processors/model-partials.js";
import {
modelQueryBuilderTypes,
modelQueryRawTypes,
modelQueryResultTypes,
} from "./processors/model-query.js";
import {
Expand Down Expand Up @@ -153,6 +154,7 @@ export function generateExecute(generator, options) {
typesGeneratorInit(generateContext);

databaseGenerator(generateContext);
modelQueryRawTypes(generateContext);

routerGenerator(generateContext);
apiClientGenerator(generateContext);
Expand Down
3 changes: 1 addition & 2 deletions packages/code-gen/src/generated/common/types.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Generated by @compas/code-gen



export type StructureAnyDefinitionTarget = "js"|"ts"|"jsKoaReceive"|"tsKoaReceive"|"jsKoaSend"|"tsKoaSend"|"jsPostgres"|"tsPostgres"|"jsAxios"|"tsAxios"|"jsAxiosNode"|"tsAxiosBrowser"|"tsAxiosReactNative"|"jsFetch"|"tsFetch"|"jsFetchNode"|"tsFetchBrowser"|"tsFetchReactNative";

export type StructureAnyDefinition = {
Expand Down Expand Up @@ -1388,5 +1389,3 @@ export type StructureTypeDefinitionInput =
|StructureReferenceDefinitionInput
|StructureRelationDefinitionInput
|StructureRouteInvalidationDefinitionInput;


112 changes: 111 additions & 1 deletion packages/code-gen/src/processors/model-query.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import {
AnyOfType,
AnyType,
ArrayType,
NumberType,
ObjectType,
ReferenceType,
} from "../builders/index.js";
import { databaseIsEnabled } from "../database/generator.js";
import { fileWriteRaw } from "../file/write.js";
import { TypescriptImportCollector } from "../target/typescript.js";
import { typesTypescriptResolveFile } from "../types/typescript.js";
import { upperCaseFirst } from "../utils.js";
import {
modelRelationGetInformation,
Expand Down Expand Up @@ -79,6 +84,13 @@ export function modelQueryBuilderTypes(generateContext) {
*/
export function modelQueryResultTypes(generateContext) {
for (const model of structureModels(generateContext)) {
const expansionType = new ObjectType(
"queryExpansion",
model.group + upperCaseFirst(model.name),
)
.keys({})
.build();

const type = new ObjectType(
"queryResult",
model.group + upperCaseFirst(model.name),
Expand All @@ -99,6 +111,7 @@ export function modelQueryResultTypes(generateContext) {
type.keys[relationInfo.keyNameOwn],
["isOptional"],
);

const joinedType = new ReferenceType(
"queryResult",
`${relationInfo.modelInverse.group}${upperCaseFirst(
Expand All @@ -110,9 +123,19 @@ export function modelQueryResultTypes(generateContext) {
if (isOptional) {
anyOfType.optional();
}
type.keys[relationInfo.keyNameOwn] = anyOfType.build();

type.keys[relationInfo.keyNameOwn] = anyOfType.build();
type.keys[relationInfo.keyNameOwn].values = [existingType, joinedType];

const joinedExpansionType = getQueryDefinitionReference(
relationInfo.modelInverse.group,
relationInfo.modelInverse.name,
);
if (isOptional) {
joinedExpansionType.optional();
}

expansionType.keys[relationInfo.keyNameOwn] = joinedExpansionType.build();
}

for (const relation of modelRelationGetInverse(model)) {
Expand All @@ -136,10 +159,97 @@ export function modelQueryResultTypes(generateContext) {
type.keys[relationInfo.virtualKeyNameInverse] = joinedType
.optional()
.build();

const joinedExpansionType =
relation.subType === "oneToMany" ?
new ArrayType().values(
getQueryDefinitionReference(
relationInfo.modelOwn.group,
relationInfo.modelOwn.name,
),
)
: getQueryDefinitionReference(
relationInfo.modelOwn.group,
relationInfo.modelOwn.name,
);

expansionType.keys[relationInfo.virtualKeyNameInverse] =
joinedExpansionType.optional().build();
}

structureAddType(generateContext.structure, type, {
skipReferenceExtraction: true,
});
structureAddType(generateContext.structure, expansionType, {
skipReferenceExtraction: true,
});
}
}

function getQueryDefinitionReference(group, name) {
const resolvedName = `${upperCaseFirst(group)}${upperCaseFirst(name)}`;

const implementation = {
validatorInputType: `QueryDefinition${resolvedName}`,
validatorOutputType: `QueryDefinition${resolvedName}`,
};
return new AnyType().implementations({
js: implementation,
ts: implementation,
jsPostgres: implementation,
tsPostgres: implementation,
});
}

/**
* Add raw types related to models and query builders
*
* @param {import("../generate.js").GenerateContext} generateContext
* @returns {void}
*/
export function modelQueryRawTypes(generateContext) {
if (!databaseIsEnabled(generateContext)) {
return;
}

const file = typesTypescriptResolveFile(generateContext);

if (generateContext.options.targetLanguage === "ts") {
const typeImports = TypescriptImportCollector.getImportCollector(
file,
true,
);
typeImports.destructure("@compas/store", "QueryBuilderResolver");
typeImports.destructure("@compas/store", "QueryBuilderDefinition");
typeImports.destructure("@compas/store", "ResolveOptionalJoins");
}

const exportPrefix =
generateContext.options.generators.types?.declareGlobalTypes ?
""
: "export";

for (const model of structureModels(generateContext)) {
const name = `${upperCaseFirst(model.group)}${upperCaseFirst(model.name)}`;

if (generateContext.options.targetLanguage === "ts") {
fileWriteRaw(
file,
`${exportPrefix} type QueryDefinition${name} = QueryBuilderDefinition<${name}, QueryExpansion${name}>;\n`,
);
fileWriteRaw(
file,
`${exportPrefix} type ${name}QueryResolver<QueryBuilder extends ${name}QueryBuilderInput, const OptionalJoins extends ResolveOptionalJoins<QueryExpansion${name}> = never> = QueryBuilderResolver<QueryDefinition${name}, QueryBuilder, OptionalJoins>;\n\n`,
);
} else if (generateContext.options.targetLanguage === "js") {
fileWriteRaw(
file,
`${exportPrefix} type QueryDefinition${name} = import("@compas/store").QueryBuilderDefinition<${name}, QueryExpansion${name}>;\n`,
);
fileWriteRaw(
file,
`${exportPrefix} type ${name}QueryResolver<QueryBuilder extends ${name}QueryBuilderInput, const OptionalJoins extends import("@compas/store").ResolveOptionalJoins<QueryExpansion${name}> = never> = import("@compas/store").QueryBuilderResolver<QueryDefinition${name}, QueryBuilder, OptionalJoins>;\n\n`,
);
}
}
}
1 change: 1 addition & 0 deletions packages/code-gen/src/types/typescript.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export function typesTypescriptResolveFile(generateContext) {
export function typesTypescriptInitFile(generateContext) {
return fileContextCreateGeneric(generateContext, "common/types.d.ts", {
importCollector: new TypescriptImportCollector(),
typeImportCollector: new TypescriptImportCollector(true),
});
}

Expand Down
2 changes: 2 additions & 0 deletions packages/code-gen/src/validators/javascript.js
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,9 @@ export function validatorJavascriptGetFile(generateContext, type) {
/**
* @typedef {Record<string, any|undefined>} ValidatorErrorMap
*/
// eslint-disable-next-line unused-imports/no-unused-vars
const isRecord = (v) => !!v && typeof v === "object" && !Array.isArray(v);
`,
);
Expand Down
1 change: 1 addition & 0 deletions packages/code-gen/src/validators/typescript.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ type Either<T, E> = { value: T; error?: never }|{ value?: never; error: E };
type ValidatorErrorMap = Record<string, any|undefined>;
// eslint-disable-next-line unused-imports/no-unused-vars
const isRecord = (v: unknown): v is Record<string, any> => !!v && typeof v === "object" && !Array.isArray(v);
`,
);
Expand Down
5 changes: 1 addition & 4 deletions packages/create-compas/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,7 @@
"bin": {
"create-compas": "src/create-compas.js"
},
"keywords": [
"compas",
"create"
],
"keywords": ["compas", "create"],
"license": "MIT",
"dependencies": {
"@compas/stdlib": "0.15.2",
Expand Down
8 changes: 1 addition & 7 deletions packages/server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,7 @@
"./package.json": "./package.json"
},
"type": "module",
"keywords": [
"compas",
"koa",
"api",
"http",
"backend"
],
"keywords": ["compas", "koa", "api", "http", "backend"],
"license": "MIT",
"dependencies": {
"@compas/stdlib": "0.15.2",
Expand Down
7 changes: 1 addition & 6 deletions packages/stdlib/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,7 @@
"./package.json": "./package.json"
},
"type": "module",
"keywords": [
"compas",
"stdlib",
"standard",
"logger"
],
"keywords": ["compas", "stdlib", "standard", "logger"],
"license": "MIT",
"dependencies": {
"@types/node": "22.9.0",
Expand Down
23 changes: 20 additions & 3 deletions packages/store/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,30 @@

/**
* @template Type
* @template {undefined | "*" | Array<string>} Selector
* @typedef {import("./types/advanced-types.js").Returning<Type, Selector>} Returning
* @typedef {import("./types/advanced-types.js").WrappedQueryPart<Type>} WrappedQueryPart
*/

/**
* @template Type
* @typedef {import("./types/advanced-types.js").WrappedQueryPart<Type>} WrappedQueryPart
* @typedef {import("./types/advanced-types.js").WrappedQueryResult<Type>} WrappedQueryResult
*/

/**
* @template Base
* @template Expansion
* @typedef {import("./types/advanced-types.js").QueryBuilderDefinition<Base, Expansion>} QueryBuilderDefinition
*/

/**
* @template DefinitionType
* @template QueryBuilder
* @template {string} OptionalJoins
* @typedef {import("./types/advanced-types.js").QueryBuilderResolver<DefinitionType, QueryBuilder, OptionalJoins>} QueryBuilderResolver
*/

/**
* @template Expansion
* @typedef {import("./types/advanced-types.js").ResolveOptionalJoins<Expansion>} ResolveOptionalJoins
*/

/**
Expand Down
7 changes: 1 addition & 6 deletions packages/store/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,7 @@
"./package.json": "./package.json"
},
"type": "module",
"keywords": [
"compas",
"s3",
"postgres",
"persistence"
],
"keywords": ["compas", "s3", "postgres", "persistence"],
"license": "MIT",
"dependencies": {
"@aws-sdk/client-s3": "3.685.0",
Expand Down
Loading

0 comments on commit f0f7304

Please sign in to comment.