Skip to main content

Generate Room/Bathroom STI model

Commit Message

Generate Room/Bathroom STI model

```console
pnpm psy g:sti-child --help
pnpm psy g:sti-child --model-name=Bathroom Room/Bathroom extends Room bath_or_shower_style:enum:bath_or_shower_styles:bath,shower,bath_and_shower,none
pnpm psy db:migrate
```

Changes

diff --git a/api/spec/factories/Room/BathroomFactory.ts b/api/spec/factories/Room/BathroomFactory.ts
new file mode 100644
index 0000000..749ce6b
--- /dev/null
+++ b/api/spec/factories/Room/BathroomFactory.ts
@@ -0,0 +1,9 @@
+import { UpdateableProperties } from '@rvoh/dream/types'
+import Bathroom from '@models/Room/Bathroom.js'
+
+export default async function createBathroom(attrs: UpdateableProperties<Bathroom> = {}) {
+ return await Bathroom.create({
+ bathOrShowerStyle: 'bath',
+ ...attrs,
+ })
+}
diff --git a/api/spec/unit/models/Room/Bathroom.spec.ts b/api/spec/unit/models/Room/Bathroom.spec.ts
new file mode 100644
index 0000000..25a6823
--- /dev/null
+++ b/api/spec/unit/models/Room/Bathroom.spec.ts
@@ -0,0 +1,3 @@
+describe('Room/Bathroom', () => {
+ it.todo('add a test here to get started building Room/Bathroom')
+})
diff --git a/api/src/app/models/Room/Bathroom.ts b/api/src/app/models/Room/Bathroom.ts
new file mode 100644
index 0000000..ae1bc18
--- /dev/null
+++ b/api/src/app/models/Room/Bathroom.ts
@@ -0,0 +1,19 @@
+import { STI } from '@rvoh/dream'
+import { DreamColumn, DreamSerializers } from '@rvoh/dream/types'
+import Room from '@models/Room.js'
+
+// Uncomment when adding decorators (@deco.BelongsTo, @deco.Validates, etc.):
+// import { Decorators } from '@rvoh/dream'
+// const deco = new Decorators<typeof Bathroom>()
+
+@STI(Room)
+export default class Bathroom extends Room {
+ public override get serializers(): DreamSerializers<Bathroom> {
+ return {
+ default: 'Room/BathroomSerializer',
+ summary: 'Room/BathroomSummarySerializer',
+ }
+ }
+
+ public bathOrShowerStyle: DreamColumn<Bathroom, 'bathOrShowerStyle'>
+}
diff --git a/api/src/app/serializers/Room/BathroomSerializer.ts b/api/src/app/serializers/Room/BathroomSerializer.ts
new file mode 100644
index 0000000..a80452b
--- /dev/null
+++ b/api/src/app/serializers/Room/BathroomSerializer.ts
@@ -0,0 +1,9 @@
+import { RoomSerializer, RoomSummarySerializer } from '@serializers/RoomSerializer.js'
+import Bathroom from '@models/Room/Bathroom.js'
+
+export const RoomBathroomSummarySerializer = (bathroom: Bathroom) =>
+ RoomSummarySerializer(Bathroom, bathroom)
+
+export const RoomBathroomSerializer = (bathroom: Bathroom) =>
+ RoomSerializer(Bathroom, bathroom)
+ .attribute('bathOrShowerStyle')
diff --git a/api/src/db/migrations/1779136762080-create-room-bathroom.ts b/api/src/db/migrations/1779136762080-create-room-bathroom.ts
new file mode 100644
index 0000000..3c845b2
--- /dev/null
+++ b/api/src/db/migrations/1779136762080-create-room-bathroom.ts
@@ -0,0 +1,37 @@
+import { Kysely, sql } from 'kysely'
+
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+export async function up(db: Kysely<any>): Promise<void> {
+ await db.schema
+ .createType('bath_or_shower_styles_enum')
+ .asEnum([
+ 'bath',
+ 'shower',
+ 'bath_and_shower',
+ 'none'
+ ])
+ .execute()
+
+ await db.schema
+ .alterTable('rooms')
+ .addColumn('bath_or_shower_style', sql`bath_or_shower_styles_enum`)
+ .execute()
+
+ await db.schema
+ .alterTable('rooms')
+ .addCheckConstraint(
+ 'rooms_not_null_bath_or_shower_style',
+ sql`type != 'Bathroom' OR bath_or_shower_style IS NOT NULL`,
+ )
+ .execute()
+}
+
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+export async function down(db: Kysely<any>): Promise<void> {
+ await db.schema
+ .alterTable('rooms')
+ .dropColumn('bath_or_shower_style')
+ .execute()
+
+ await db.schema.dropType('bath_or_shower_styles_enum').execute()
+}
\ No newline at end of file
diff --git a/api/src/openapi/mobile.openapi.json b/api/src/openapi/mobile.openapi.json
index 0aafbae..0b375d2 100644
--- a/api/src/openapi/mobile.openapi.json
+++ b/api/src/openapi/mobile.openapi.json
@@ -343,7 +343,7 @@
"results": {
"type": "array",
"items": {
- "$ref": "#/components/schemas/RoomSummary"
+ "$ref": "#/components/schemas/RoomBathroomSummary"
}
}
}
@@ -402,7 +402,7 @@
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/Room"
+ "$ref": "#/components/schemas/RoomBathroom"
}
}
},
@@ -461,7 +461,7 @@
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/Room"
+ "$ref": "#/components/schemas/RoomBathroom"
}
}
},
@@ -671,14 +671,22 @@
}
}
},
- "Room": {
+ "RoomBathroom": {
"type": "object",
"required": [
+ "bathOrShowerStyle",
"id",
"position",
"type"
],
"properties": {
+ "bathOrShowerStyle": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "description": "The following values will be allowed:\n bath,\n bath_and_shower,\n none,\n shower"
+ },
"id": {
"type": "string"
},
@@ -690,11 +698,11 @@
},
"type": {
"type": "string",
- "description": "The following values will be allowed:\n Room"
+ "description": "The following values will be allowed:\n Bathroom"
}
}
},
- "RoomSummary": {
+ "RoomBathroomSummary": {
"type": "object",
"required": [
"id"
diff --git a/api/src/openapi/openapi.json b/api/src/openapi/openapi.json
index 20583ce..b4e6930 100644
--- a/api/src/openapi/openapi.json
+++ b/api/src/openapi/openapi.json
@@ -343,7 +343,7 @@
"results": {
"type": "array",
"items": {
- "$ref": "#/components/schemas/RoomSummary"
+ "$ref": "#/components/schemas/RoomBathroomSummary"
}
}
}
@@ -402,7 +402,7 @@
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/Room"
+ "$ref": "#/components/schemas/RoomBathroom"
}
}
},
@@ -461,7 +461,7 @@
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/Room"
+ "$ref": "#/components/schemas/RoomBathroom"
}
}
},
@@ -679,14 +679,28 @@
}
}
},
- "Room": {
+ "RoomBathroom": {
"type": "object",
"required": [
+ "bathOrShowerStyle",
"id",
"position",
"type"
],
"properties": {
+ "bathOrShowerStyle": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "enum": [
+ "bath",
+ "bath_and_shower",
+ "none",
+ "shower",
+ null
+ ]
+ },
"id": {
"type": "string"
},
@@ -699,12 +713,12 @@
"type": {
"type": "string",
"enum": [
- "Room"
+ "Bathroom"
]
}
}
},
- "RoomSummary": {
+ "RoomBathroomSummary": {
"type": "object",
"required": [
"id"
diff --git a/api/src/openapi/tests.openapi.json b/api/src/openapi/tests.openapi.json
index 16ea710..6c559c8 100644
--- a/api/src/openapi/tests.openapi.json
+++ b/api/src/openapi/tests.openapi.json
@@ -343,7 +343,7 @@
"results": {
"type": "array",
"items": {
- "$ref": "#/components/schemas/RoomSummary"
+ "$ref": "#/components/schemas/RoomBathroomSummary"
}
}
}
@@ -402,7 +402,7 @@
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/Room"
+ "$ref": "#/components/schemas/RoomBathroom"
}
}
},
@@ -461,7 +461,7 @@
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/Room"
+ "$ref": "#/components/schemas/RoomBathroom"
}
}
},
@@ -679,14 +679,28 @@
}
}
},
- "Room": {
+ "RoomBathroom": {
"type": "object",
"required": [
+ "bathOrShowerStyle",
"id",
"position",
"type"
],
"properties": {
+ "bathOrShowerStyle": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "enum": [
+ "bath",
+ "bath_and_shower",
+ "none",
+ "shower",
+ null
+ ]
+ },
"id": {
"type": "string"
},
@@ -699,12 +713,12 @@
"type": {
"type": "string",
"enum": [
- "Room"
+ "Bathroom"
]
}
}
},
- "RoomSummary": {
+ "RoomBathroomSummary": {
"type": "object",
"required": [
"id"
diff --git a/api/src/types/db.ts b/api/src/types/db.ts
index 91dba44..e6aa3bd 100644
--- a/api/src/types/db.ts
+++ b/api/src/types/db.ts
@@ -68,6 +68,19 @@ import {
*/
import type { ColumnType } from 'kysely'

+export type BathOrShowerStylesEnum =
+ | 'bath'
+ | 'bath_and_shower'
+ | 'none'
+ | 'shower'
+
+export const BathOrShowerStylesEnumValues = [
+ 'bath',
+ 'bath_and_shower',
+ 'none',
+ 'shower',
+] as const
+
export type Generated<T> =
T extends ColumnType<infer S, infer I, infer U>
? ColumnType<S, I | undefined, U>
@@ -145,6 +158,7 @@ export interface Places {
}

export interface Rooms {
+ bathOrShowerStyle: BathOrShowerStylesEnum | null
createdAt: Timestamp
deletedAt: Timestamp | null
id: Generated<string>
diff --git a/api/src/types/dream.globals.ts b/api/src/types/dream.globals.ts
index a55e48c..b4348f8 100644
--- a/api/src/types/dream.globals.ts
+++ b/api/src/types/dream.globals.ts
@@ -64,6 +64,8 @@ export const globalTypeConfig = {
'HostSummarySerializer',
'PlaceSerializer',
'PlaceSummarySerializer',
+ 'Room/BathroomSerializer',
+ 'Room/BathroomSummarySerializer',
'RoomSerializer',
'RoomSummarySerializer',
],
diff --git a/api/src/types/dream.ts b/api/src/types/dream.ts
index 4332741..e450fe4 100644
--- a/api/src/types/dream.ts
+++ b/api/src/types/dream.ts
@@ -63,8 +63,10 @@ import {
type ClockTimeTz,
} from '@rvoh/dream'
import {
+ type BathOrShowerStylesEnum,
type PlaceStylesEnum,
type RoomTypesEnum,
+ BathOrShowerStylesEnumValues,
PlaceStylesEnumValues,
RoomTypesEnumValues,
} from './db.js'
@@ -419,10 +421,11 @@ export const schema = {
rooms: {
serializerKeys: ['default', 'summary'],
scopes: {
- default: ['dream:SoftDelete'],
+ default: ['dream:STI', 'dream:SoftDelete'],
named: [],
},
nonJsonColumnNames: [
+ 'bathOrShowerStyle',
'createdAt',
'deletedAt',
'id',
@@ -432,6 +435,15 @@ export const schema = {
'updatedAt',
],
columns: {
+ bathOrShowerStyle: {
+ coercedType: {} as BathOrShowerStylesEnum | null,
+ enumType: {} as BathOrShowerStylesEnum,
+ enumArrayType: [] as BathOrShowerStylesEnum[],
+ enumValues: BathOrShowerStylesEnumValues,
+ dbType: 'bath_or_shower_styles_enum',
+ allowNull: true,
+ isArray: false,
+ },
createdAt: {
coercedType: {} as DateTime,
enumType: null,
@@ -605,7 +617,7 @@ export const schema = {

export const connectionTypeConfig = {
passthroughColumns: [],
- allDefaultScopeNames: ['dream:SoftDelete'],
+ allDefaultScopeNames: ['dream:STI', 'dream:SoftDelete'],
globalNames: {
models: {
Guest: 'guests',
@@ -613,6 +625,7 @@ export const connectionTypeConfig = {
HostPlace: 'host_places',
Place: 'places',
Room: 'rooms',
+ 'Room/Bathroom': 'rooms',
User: 'users',
},
},
diff --git a/api/src/types/openapi/tests.openapi.d.ts b/api/src/types/openapi/tests.openapi.d.ts
index 0e726f4..8f52785 100644
--- a/api/src/types/openapi/tests.openapi.d.ts
+++ b/api/src/types/openapi/tests.openapi.d.ts
@@ -225,7 +225,7 @@ export interface paths {
content: {
"application/json": {
cursor: string | null;
- results: components["schemas"]["RoomSummary"][];
+ results: components["schemas"]["RoomBathroomSummary"][];
};
};
};
@@ -266,7 +266,7 @@ export interface paths {
[name: string]: unknown;
};
content: {
- "application/json": components["schemas"]["Room"];
+ "application/json": components["schemas"]["RoomBathroom"];
};
};
400: components["responses"]["BadRequest"];
@@ -313,7 +313,7 @@ export interface paths {
[name: string]: unknown;
};
content: {
- "application/json": components["schemas"]["Room"];
+ "application/json": components["schemas"]["RoomBathroom"];
};
};
400: components["responses"]["BadRequest"];
@@ -413,13 +413,15 @@ export interface components {
id: string;
name: string;
};
- Room: {
+ RoomBathroom: {
+ /** @enum {string|null} */
+ bathOrShowerStyle: "bath" | "bath_and_shower" | "none" | "shower" | null;
id: string;
position: number | null;
/** @enum {string} */
- type: "Room";
+ type: "Bathroom";
};
- RoomSummary: {
+ RoomBathroomSummary: {
id: string;
};
ValidationErrors: {