Skip to content

Commit

Permalink
Merge pull request #175 from alan-francis/main
Browse files Browse the repository at this point in the history
Add new textDefaultNullable schema object as domain to typical & governance
  • Loading branch information
shah authored Dec 6, 2023
2 parents 4f89c6f + af90927 commit f102416
Show file tree
Hide file tree
Showing 7 changed files with 152 additions and 18 deletions.
50 changes: 40 additions & 10 deletions pattern/governed/governed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,20 +60,40 @@ export class GovernedDomains<
return z.string().optional();
}

varChar(maxLength: number) {
textDefaultNullable() {
return z.string(
SQLa.zodSqlDomainRawCreateParams(
SQLa.sqlDomainZodStringDescr({ isVarChar: true }),
SQLa.sqlDomainZodStringDescr({ isDefaultNullableText: true }),
),
).max(maxLength);
).optional();
}

varCharNullable(maxLength: number) {
return z.string(
SQLa.zodSqlDomainRawCreateParams(
SQLa.sqlDomainZodStringDescr({ isVarChar: true }),
),
).max(maxLength).optional();
varChar(maxLength?: number) {
return maxLength
? z.string(
SQLa.zodSqlDomainRawCreateParams(
SQLa.sqlDomainZodStringDescr({ isVarChar: true }),
),
).max(maxLength)
: z.string(
SQLa.zodSqlDomainRawCreateParams(
SQLa.sqlDomainZodStringDescr({ isVarChar: true }),
),
);
}

varCharNullable(maxLength?: number) {
return maxLength
? z.string(
SQLa.zodSqlDomainRawCreateParams(
SQLa.sqlDomainZodStringDescr({ isVarChar: true }),
),
).max(maxLength).optional()
: z.string(
SQLa.zodSqlDomainRawCreateParams(
SQLa.sqlDomainZodStringDescr({ isVarChar: true }),
),
).optional();
}

integer() {
Expand Down Expand Up @@ -231,7 +251,9 @@ export class GovernedDomains<
}

uuid() {
return z.string().uuid();
return z.string(SQLa.zodSqlDomainRawCreateParams(
SQLa.sqlDomainZodStringDescr({ isUuid: true }),
)).uuid();
}

uuidNullable() {
Expand Down Expand Up @@ -435,6 +457,14 @@ export class GovernedKeys<
autoIncPrimaryKey() {
return this.pkcf.autoIncPrimaryKey();
}

varCharPrimaryKey() {
return this.pkcf.primaryKey(this.domains.varChar());
}

uuidPrimaryKey() {
return this.pkcf.primaryKey(this.domains.uuid());
}
}

export class GovernedTemplateState<
Expand Down
51 changes: 50 additions & 1 deletion pattern/governed/governed_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,16 @@ export function syntheticSchema() {
...housekeeping.columns,
});

const publAnotherHost = gm.textPkTable("publ_another_host", {
publ_another_host_id: keys.varCharPrimaryKey(),
host: tcf.unique(sd.text()),
host_identity: sd.jsonTextNullable(),
host_type_code: hostType.references.code(),
mutation_count: sd.integer(),
host_description: sd.textDefaultNullable(),
...housekeepingAuditable.columns,
});

return {
governedModel: gm,
publHost,
Expand All @@ -117,6 +127,7 @@ export function syntheticSchema() {
publHostView,
hostType,
buildEventType,
publAnotherHost,
};
}

Expand Down Expand Up @@ -157,6 +168,8 @@ Deno.test("SQL Aide (SQLa) emit template", () => {
${ss.publServerErrorLog}
${ss.publAnotherHost}
${ss.publHost.insertDML({ publ_host_id: "test", host: "test", host_identity: "testHI", mutation_count: 0, host_type_code: ss.hostType.seedEnum.linux })}
${ss.publHost.select({ host_identity: "testHI"})}
Expand All @@ -179,7 +192,7 @@ Deno.test("SQL Aide (SQLa) emit template", () => {
0,
DDL.stsOptions.sqlQualitySystemState?.lintedSqlText.lintIssues?.length,
);
ta.assertEquals(gts.tablesDeclared.size, 7);
ta.assertEquals(gts.tablesDeclared.size, 8);
ta.assertEquals(gts.viewsDeclared.size, 1);
ta.assertEquals(fixturePUML, gts.pumlERD(ctx).content);
});
Expand Down Expand Up @@ -295,6 +308,24 @@ const fixtureSQL = ws.unindentWhitespace(`
FOREIGN KEY("publ_server_service_id") REFERENCES "publ_server_service"("publ_server_service_id")
);
CREATE TABLE IF NOT EXISTS "publ_another_host" (
"publ_another_host_id" VARCHAR(4096) PRIMARY KEY NOT NULL,
"host" TEXT /* UNIQUE COLUMN */ NOT NULL,
"host_identity" TEXT,
"host_type_code" INTEGER NOT NULL,
"mutation_count" INTEGER NOT NULL,
"host_description" TEXT DEFAULT NULL,
"created_at" TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
"created_by" TEXT DEFAULT 'UNKNOWN',
"updated_at" TIMESTAMP,
"updated_by" TEXT,
"deleted_at" TIMESTAMP,
"deleted_by" TEXT,
"activity_log" TEXT,
FOREIGN KEY("host_type_code") REFERENCES "host_type"("code"),
UNIQUE("host")
);
INSERT INTO "publ_host" ("publ_host_id", "host", "host_identity", "host_type_code", "mutation_count", "created_by") VALUES ('test', 'test', 'testHI', 0, 0, NULL);
SELECT "publ_host_id" FROM "publ_host" WHERE "host_identity" = 'testHI';
Expand Down Expand Up @@ -412,10 +443,28 @@ const fixturePUML = `@startuml IE
created_by: TEXT
}
entity "publ_another_host" as publ_another_host {
* **publ_another_host_id**: VARCHAR(4096)
--
* host: TEXT
host_identity: TEXT
* host_type_code: INTEGER
* mutation_count: INTEGER
host_description: TEXT
created_at: TIMESTAMP
created_by: TEXT
updated_at: TIMESTAMP
updated_by: TEXT
deleted_at: TIMESTAMP
deleted_by: TEXT
activity_log: TEXT
}
publ_host |o..o{ publ_build_event
build_event_type |o..o{ publ_build_event
publ_build_event |o..o{ publ_server_service
publ_server_service |o..o{ publ_server_static_access_log
publ_server_error_log |o..o{ publ_server_error_log
publ_server_service |o..o{ publ_server_error_log
host_type |o..o{ publ_another_host
@enduml`;
4 changes: 2 additions & 2 deletions pattern/postgres/migrate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,7 @@ export class PgMigrate<
CASE task
WHEN 'migrate' THEN
FOR r IN (
SELECT sp_migration,sp_migration_undo,fn_migration_status FROM ${this.infoSchemaLifecycle.sqlNamespace}.${islmGovernance.tableName} WHERE from_state = '${TransitionStatus.NONE}' AND to_state = '${TransitionStatus.SQLLOADED}' AND (target_version_number NOT IN (SELECT state_sort_index FROM ${this.infoSchemaLifecycle.sqlNamespace}.${islmGovernance.tableName} WHERE from_state = '${TransitionStatus.SQLLOADED}' AND to_state = '${TransitionStatus.MIGRATED}')) AND
SELECT sp_migration,sp_migration_undo,fn_migration_status FROM ${this.infoSchemaLifecycle.sqlNamespace}.${islmGovernance.tableName} WHERE from_state = '${TransitionStatus.NONE}' AND to_state = '${TransitionStatus.SQLLOADED}' AND (state_sort_index NOT IN (SELECT state_sort_index FROM ${this.infoSchemaLifecycle.sqlNamespace}.${islmGovernance.tableName} WHERE from_state = '${TransitionStatus.SQLLOADED}' AND to_state = '${TransitionStatus.MIGRATED}')) AND
(target_version_number IS NULL OR state_sort_index<=target_version_number)
ORDER BY state_sort_index
) LOOP
Expand Down Expand Up @@ -323,7 +323,7 @@ export class PgMigrate<
migrate_rb_insertion_sql TEXT;
sp_migration_undo_sql TEXT;
BEGIN
SELECT sp_migration_undo FROM ${this.infoSchemaLifecycle.sqlNamespace}.${islmGovernance.tableName} WHERE from_state = '${TransitionStatus.SQLLOADED}' AND to_state = '${TransitionStatus.MIGRATED}' AND (target_version_number NOT IN (SELECT state_sort_index FROM ${this.infoSchemaLifecycle.sqlNamespace}.${islmGovernance.tableName} WHERE from_state = '${TransitionStatus.MIGRATED}' AND to_state = '${TransitionStatus.ROLLBACK}')) ORDER BY state_sort_index INTO sp_migration_undo_sql;
SELECT sp_migration_undo FROM ${this.infoSchemaLifecycle.sqlNamespace}.${islmGovernance.tableName} WHERE from_state = '${TransitionStatus.SQLLOADED}' AND to_state = '${TransitionStatus.MIGRATED}' AND state_sort_index=target_version_number AND (target_version_number IN (SELECT state_sort_index FROM ${this.infoSchemaLifecycle.sqlNamespace}.${islmGovernance.tableName} WHERE from_state = '${TransitionStatus.SQLLOADED}' AND to_state = '${TransitionStatus.MIGRATED}' ORDER BY state_sort_index DESC LIMIT 1)) INTO sp_migration_undo_sql;
IF sp_migration_undo_sql IS NOT NULL THEN
EXECUTE format('CALL ${this.infoSchemaLifecycle.sqlNamespace}."%s"()', sp_migration_undo_sql);
Expand Down
4 changes: 2 additions & 2 deletions pattern/postgres/migrate_test.fixture.sql
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ BEGIN
CASE task
WHEN 'migrate' THEN
FOR r IN (
SELECT sp_migration,sp_migration_undo,fn_migration_status FROM info_schema_lifecycle.islm_governance WHERE from_state = 'None' AND to_state = 'SQL Loaded' AND (target_version_number NOT IN (SELECT state_sort_index FROM info_schema_lifecycle.islm_governance WHERE from_state = 'SQL Loaded' AND to_state = 'Migrated')) AND
SELECT sp_migration,sp_migration_undo,fn_migration_status FROM info_schema_lifecycle.islm_governance WHERE from_state = 'None' AND to_state = 'SQL Loaded' AND (state_sort_index NOT IN (SELECT state_sort_index FROM info_schema_lifecycle.islm_governance WHERE from_state = 'SQL Loaded' AND to_state = 'Migrated')) AND
(target_version_number IS NULL OR state_sort_index<=target_version_number)
ORDER BY state_sort_index
) LOOP
Expand Down Expand Up @@ -71,7 +71,7 @@ BEGIN
migrate_rb_insertion_sql TEXT;
sp_migration_undo_sql TEXT;
BEGIN
SELECT sp_migration_undo FROM info_schema_lifecycle.islm_governance WHERE from_state = 'SQL Loaded' AND to_state = 'Migrated' AND (target_version_number NOT IN (SELECT state_sort_index FROM info_schema_lifecycle.islm_governance WHERE from_state = 'Migrated' AND to_state = 'Rollback')) ORDER BY state_sort_index INTO sp_migration_undo_sql;
SELECT sp_migration_undo FROM info_schema_lifecycle.islm_governance WHERE from_state = 'SQL Loaded' AND to_state = 'Migrated' AND state_sort_index=target_version_number AND (target_version_number IN (SELECT state_sort_index FROM info_schema_lifecycle.islm_governance WHERE from_state = 'SQL Loaded' AND to_state = 'Migrated' ORDER BY state_sort_index DESC LIMIT 1)) INTO sp_migration_undo_sql;
IF sp_migration_undo_sql IS NOT NULL THEN
EXECUTE format('CALL info_schema_lifecycle."%s"()', sp_migration_undo_sql);

Expand Down
6 changes: 6 additions & 0 deletions pattern/typical/typical.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,12 @@ export function governedDomains<
textArray: z.array(z.string()),
text: z.string,
textNullable: () => z.string().optional(),
textDefaultNullable: () =>
z.string(
SQLa.zodSqlDomainRawCreateParams(
SQLa.sqlDomainZodStringDescr({ isDefaultNullableText: true }),
),
).optional(),

varChar: (maxLength?: number) =>
maxLength
Expand Down
3 changes: 3 additions & 0 deletions pattern/typical/typical_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export function syntheticSchema<Context extends SQLa.SqlEmitContext>(
host_identity: sd.jsonTextNullable(),
host_type_code: hostType.references.code(),
mutation_count: sd.integer(),
host_description: sd.textDefaultNullable(),
...housekeeping.columns,
});

Expand Down Expand Up @@ -352,6 +353,7 @@ const fixtureSQL = ws.unindentWhitespace(`
"host_identity" TEXT,
"host_type_code" INTEGER NOT NULL,
"mutation_count" INTEGER NOT NULL,
"host_description" TEXT DEFAULT NULL,
"created_at" TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
"created_by" TEXT DEFAULT 'UNKNOWN',
"updated_at" TIMESTAMP,
Expand Down Expand Up @@ -510,6 +512,7 @@ const fixturePUML = `@startuml IE
host_identity: TEXT
* host_type_code: INTEGER
* mutation_count: INTEGER
host_description: TEXT
created_at: TIMESTAMP
created_by: TEXT
updated_at: TIMESTAMP
Expand Down
52 changes: 49 additions & 3 deletions render/domain/domain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,12 +192,20 @@ export type SqlDomainZodStringDescr = SqlDomainZodDescrMeta & {
readonly isUlid?: boolean;
} & {
readonly isBlobText?: boolean;
} & {
readonly isDefaultNullableText?: boolean;
};

export function sqlDomainZodStringDescr(
options: Pick<
SqlDomainZodStringDescr,
"isJsonText" | "isVarChar" | "isSemver" | "isUuid" | "isUlid" | "isBlobText"
| "isJsonText"
| "isVarChar"
| "isSemver"
| "isUuid"
| "isUlid"
| "isBlobText"
| "isDefaultNullableText"
>,
): SqlDomainZodStringDescr {
return {
Expand All @@ -212,7 +220,8 @@ export function isSqlDomainZodStringDescr<
const isSDZSD = safety.typeGuard<SDZND>("isSqlDomainZodDescrMeta");
return isSDZSD(o) &&
("isJsonText" in o || "isVarChar" in o || "isSemver" in o ||
"isUuid" in o || "isUlid" in o || "isBlobText" in o);
"isUuid" in o || "isUlid" in o || "isBlobText" in o ||
"isDefaultNullableText" in o);
}

export function zodStringSqlDomainFactory<
Expand Down Expand Up @@ -484,6 +493,41 @@ export function zodStringSqlDomainFactory<
parents: init?.parents,
};
},
textDefaultNullable: <
ZodType extends z.ZodType<string, z.ZodStringDef>,
Identity extends string,
>(
zodType: ZodType,
init?: {
readonly identity?: Identity;
readonly isOptional?: boolean;
readonly parents?: z.ZodTypeAny[];
},
) => {
const defaultValue = init?.parents?.[0]?._def.defaultValue?.();
return {
...ztaSDF.defaults<Identity>(zodType, init),
sqlDataType: () => ({
SQL: (ctx: Context) => {
if (tmpl.isMsSqlServerDialect(ctx.sqlDialect)) {
return `NVARCHAR(MAX)`;
}
return `TEXT`;
},
}),
sqlDefaultValue: () => ({
SQL: (_ctx: Context) => {
const defaultValueTransformed = defaultValue != undefined
? _ctx.sqlTextEmitOptions.quotedLiteral(defaultValue)
: `NULL`;
return defaultValueTransformed != `NULL`
? defaultValueTransformed[1]
: `NULL`;
},
}),
parents: init?.parents,
};
},
};
}

Expand Down Expand Up @@ -1510,7 +1554,9 @@ export function zodTypeSqlDomainFactory<
? stringSDF.ulid(zodType, init)
: (zodDefHook.descrMeta.isBlobText
? stringSDF.blobString(zodType, init)
: stringSDF.string(zodType, init))))));
: (zodDefHook.descrMeta.isDefaultNullableText
? stringSDF.textDefaultNullable(zodType, init)
: stringSDF.string(zodType, init)))))));
} else {
throw new Error(
`Unable to map Zod type ${zodDef.typeName} to SQL domain, description meta is not for ZodString ${
Expand Down

0 comments on commit f102416

Please sign in to comment.