Skip to content

Commit

Permalink
fix(config): ensure configs without queries are supported
Browse files Browse the repository at this point in the history
  • Loading branch information
uladkasach committed Jun 15, 2024
1 parent c4fa727 commit 2b5554f
Show file tree
Hide file tree
Showing 13 changed files with 211 additions and 25 deletions.
23 changes: 23 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@
"husky": "8.0.3",
"jest": "29.3.1",
"prettier": "2.8.1",
"test-fns": "1.4.0",
"ts-jest": "29.1.3",
"ts-node": "10.9.2",
"typescript": "5.4.5"
Expand Down
1 change: 1 addition & 0 deletions src/logic/__test_assets__/directory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const TEST_ASSETS_ROOT_DIR = __dirname;
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
language: postgres
dialect: 10.7
resources:
- 'schema/**/*.sql'
generates:
types: src/generated/fromSql/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
CREATE OR REPLACE FUNCTION get_answer_to_life()
RETURNS int
LANGUAGE plpgsql
AS $$
BEGIN
RETURN 42;
END;
$$
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
CREATE TABLE `ice_cream` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`uuid` char(36) COLLATE utf8mb4_bin NOT NULL,
`created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
`name` varchar(255) COLLATE utf8mb4_bin NOT NULL,
`ingredient_ids_hash` binary(32) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `tag_ux1` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
CREATE TABLE ingredient (
id bigserial PRIMARY KEY,
uuid uuid NOT NULL,
created_at timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
name varchar NOT NULL,
cost numeric(5, 2) NOT NULL,
CONSTRAINT ingredient_pk PRIMARY KEY (id),
CONSTRAINT ingredient_ux1 UNIQUE (name)
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
**/*
!.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -557,3 +557,33 @@ export const sqlQuerySelectUserWithMatchingIceCream = async ({
});
"
`;
exports[`generate postgres-noqueries should be able to read the example config provisioned in __test_assets__ 1`] = `
"// types for table 'ice_cream'
export interface SqlTableIceCream {
id: number;
uuid: string;
created_at: Date;
name: string;
ingredient_ids_hash: Buffer;
}
// types for table 'ingredient'
export interface SqlTableIngredient {
id: number | null;
uuid: string;
created_at: Date;
name: string;
cost: number;
}
// types for function 'get_answer_to_life'
export interface SqlFunctionGetAnswerToLifeInput {
}
export interface SqlFunctionGetAnswerToLifeInputByName {
}
export type SqlFunctionGetAnswerToLifeOutput = number;
"
`;
43 changes: 37 additions & 6 deletions src/logic/commands/generate/generate.integration.test.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import { getError } from 'test-fns';

import { TEST_ASSETS_ROOT_DIR } from '../../__test_assets__/directory';
import { readFile } from '../utils/fileIO';
import { generate } from './generate';

describe('generate', () => {
describe('mysql', () => {
const testAssetPaths = {
codegenYml: `${__dirname}/../../__test_assets__/exampleProject/mysql/codegen.sql.yml`,
generatedTypesCode: `${__dirname}/../../__test_assets__/exampleProject/mysql/src/generated/fromSql/types.ts`,
generatedQueryFunctionsCode: `${__dirname}/../../__test_assets__/exampleProject/mysql/src/generated/fromSql/queryFunctions.ts`,
codegenYml: `${TEST_ASSETS_ROOT_DIR}/exampleProject/mysql/codegen.sql.yml`,
generatedTypesCode: `${TEST_ASSETS_ROOT_DIR}/exampleProject/mysql/src/generated/fromSql/types.ts`,
generatedQueryFunctionsCode: `${TEST_ASSETS_ROOT_DIR}/exampleProject/mysql/src/generated/fromSql/queryFunctions.ts`,
};
it('should be able to read the example config provisioned in __test_assets__', async () => {
await generate({
Expand Down Expand Up @@ -37,9 +40,9 @@ describe('generate', () => {
});
describe('postgres', () => {
const testAssetPaths = {
codegenYml: `${__dirname}/../../__test_assets__/exampleProject/postgres/codegen.sql.yml`,
generatedTypesCode: `${__dirname}/../../__test_assets__/exampleProject/postgres/src/generated/fromSql/types.ts`,
generatedQueryFunctionsCode: `${__dirname}/../../__test_assets__/exampleProject/postgres/src/generated/fromSql/queryFunctions.ts`,
codegenYml: `${TEST_ASSETS_ROOT_DIR}/exampleProject/postgres/codegen.sql.yml`,
generatedTypesCode: `${TEST_ASSETS_ROOT_DIR}/exampleProject/postgres/src/generated/fromSql/types.ts`,
generatedQueryFunctionsCode: `${TEST_ASSETS_ROOT_DIR}/exampleProject/postgres/src/generated/fromSql/queryFunctions.ts`,
};
it('should be able to read the example config provisioned in __test_assets__', async () => {
await generate({
Expand Down Expand Up @@ -68,4 +71,32 @@ describe('generate', () => {
expect(queryFunctionsCode).toMatchSnapshot();
});
});
describe('postgres-noqueries', () => {
const testAssetPaths = {
codegenYml: `${TEST_ASSETS_ROOT_DIR}/exampleProject/postgres-noqueries/codegen.sql.yml`,
generatedTypesCode: `${TEST_ASSETS_ROOT_DIR}/exampleProject/postgres-noqueries/src/generated/fromSql/types.ts`,
generatedQueryFunctionsCode: `${TEST_ASSETS_ROOT_DIR}/exampleProject/postgres-noqueries/src/generated/fromSql/queryFunctions.ts`,
};
it('should be able to read the example config provisioned in __test_assets__', async () => {
await generate({
configPath: testAssetPaths.codegenYml,
});

// expect that the types code does not have compile errors
await import(testAssetPaths.generatedTypesCode);

// expect that the query functions code does not get produced
const error = await getError(
import(testAssetPaths.generatedQueryFunctionsCode),
);
expect(error.message).toContain('Cannot find module');
expect(error.message).toContain('queryFunctions.ts');

// expect the look right
const typesCode = (
await readFile(testAssetPaths.generatedTypesCode)
).toString();
expect(typesCode).toMatchSnapshot();
});
});
});
Original file line number Diff line number Diff line change
@@ -1,6 +1,58 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`readConfig should be able to read the example config provisioned in __test_assets__ 1`] = `
exports[`readConfig should be able to read a config without queries 1`] = `
{
"declarations": [
ResourceDeclaration {
"path": "schema/functions/get_answer_to_life.sql",
"sql": "CREATE OR REPLACE FUNCTION get_answer_to_life()
RETURNS int
LANGUAGE plpgsql
AS $$
BEGIN
RETURN 42;
END;
$$
",
},
ResourceDeclaration {
"path": "schema/tables/ice_cream.sql",
"sql": "CREATE TABLE \`ice_cream\` (
\`id\` bigint(20) NOT NULL AUTO_INCREMENT,
\`uuid\` char(36) COLLATE utf8mb4_bin NOT NULL,
\`created_at\` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
\`name\` varchar(255) COLLATE utf8mb4_bin NOT NULL,
\`ingredient_ids_hash\` binary(32) NOT NULL,
PRIMARY KEY (\`id\`),
UNIQUE KEY \`tag_ux1\` (\`name\`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin
",
},
ResourceDeclaration {
"path": "schema/tables/ingredient.sql",
"sql": "CREATE TABLE ingredient (
id bigserial PRIMARY KEY,
uuid uuid NOT NULL,
created_at timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
name varchar NOT NULL,
cost numeric(5, 2) NOT NULL,
CONSTRAINT ingredient_pk PRIMARY KEY (id),
CONSTRAINT ingredient_ux1 UNIQUE (name)
)
",
},
],
"dialect": "10.7",
"generates": {
"queryFunctions": undefined,
"types": "src/generated/fromSql/types.ts",
},
"language": "postgres",
"rootDir": "__DIR__",
}
`;
exports[`readConfig should be able to read a fully declared config 1`] = `
{
"declarations": [
ResourceDeclaration {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,26 @@
import { GeneratorConfig } from '../../../../domain';
import { TEST_ASSETS_ROOT_DIR } from '../../../__test_assets__/directory';
import { readConfig } from './readConfig';

describe('readConfig', () => {
it('should be able to read the example config provisioned in __test_assets__', async () => {
it('should be able to read a fully declared config', async () => {
const config = await readConfig({
filePath: `${__dirname}/../../../__test_assets__/exampleProject/mysql/codegen.sql.yml`,
filePath: `${TEST_ASSETS_ROOT_DIR}/exampleProject/mysql/codegen.sql.yml`,
});
expect(config).toBeInstanceOf(GeneratorConfig);
expect(config.language).toEqual('mysql');
expect(config.dialect).toEqual('5.7');
expect(config.declarations.length).toEqual(12);
expect({ ...config, rootDir: '__DIR__' }).toMatchSnapshot(); // to log an example of the output; note we mask dir to make it machine independent
});
it('should be able to read a config without queries', async () => {
const config = await readConfig({
filePath: `${TEST_ASSETS_ROOT_DIR}/exampleProject/postgres-noqueries/codegen.sql.yml`,
});
expect(config).toBeInstanceOf(GeneratorConfig);
expect(config.language).toEqual('postgres');
expect(config.dialect).toEqual('10.7');
expect(config.declarations.length).toEqual(3);
expect({ ...config, rootDir: '__DIR__' }).toMatchSnapshot(); // to log an example of the output; note we mask dir to make it machine independent
});
});
35 changes: 19 additions & 16 deletions src/logic/config/getConfig/readConfig/readConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,22 +49,25 @@ export const readConfig = async ({ filePath }: { filePath: string }) => {
);

// get the query declarations
const queryGlobs = contents.queries;
const queryPaths = await getAllPathsMatchingGlobs({
globs: queryGlobs,
root: configDir,
});
const queryDeclarations = await Promise.all(
queryPaths
.sort() // for determinism in order
.map((relativePath) =>
extractDeclarationFromGlobedFile({
rootDir: configDir,
relativePath,
type: DeclarationType.QUERY,
}),
),
);
const queryDeclarations = await (async () => {
const queryGlobs = contents.queries;
if (!queryGlobs) return [];
const queryPaths = await getAllPathsMatchingGlobs({
globs: queryGlobs,
root: configDir,
});
return await Promise.all(
queryPaths
.sort() // for determinism in order
.map((relativePath) =>
extractDeclarationFromGlobedFile({
rootDir: configDir,
relativePath,
type: DeclarationType.QUERY,
}),
),
);
})();

// return the results
return new GeneratorConfig({
Expand Down

0 comments on commit 2b5554f

Please sign in to comment.