Skip to main content

Generate Room/Bedroom STI model

Commit Message

Generate Room/Bedroom STI model

```console
pnpm psy g:sti-child --model-name=Bedroom Room/Bedroom extends Room bed_types:enum\[\]:bed_types:twin,bunk,queen,king,cot,sofabed
pnpm psy db:migrate
```

Changes

diff --git a/api/spec/factories/Room/BedroomFactory.ts b/api/spec/factories/Room/BedroomFactory.ts
new file mode 100644
index 0000000..354c9e9
--- /dev/null
+++ b/api/spec/factories/Room/BedroomFactory.ts
@@ -0,0 +1,9 @@
+import { UpdateableProperties } from '@rvoh/dream/types'
+import Bedroom from '@models/Room/Bedroom.js'
+
+export default async function createBedroom(attrs: UpdateableProperties<Bedroom> = {}) {
+ return await Bedroom.create({
+ bedTypes: ['twin'],
+ ...attrs,
+ })
+}
diff --git a/api/spec/unit/models/Room/Bedroom.spec.ts b/api/spec/unit/models/Room/Bedroom.spec.ts
new file mode 100644
index 0000000..89fb496
--- /dev/null
+++ b/api/spec/unit/models/Room/Bedroom.spec.ts
@@ -0,0 +1,3 @@
+describe('Room/Bedroom', () => {
+ it.todo('add a test here to get started building Room/Bedroom')
+})
diff --git a/api/src/app/models/Room/Bedroom.ts b/api/src/app/models/Room/Bedroom.ts
new file mode 100644
index 0000000..b7eabca
--- /dev/null
+++ b/api/src/app/models/Room/Bedroom.ts
@@ -0,0 +1,17 @@
+import { Decorators, STI } from '@rvoh/dream'
+import { DreamColumn, DreamSerializers } from '@rvoh/dream/types'
+import Room from '@models/Room.js'
+
+const deco = new Decorators<typeof Bedroom>()
+
+@STI(Room)
+export default class Bedroom extends Room {
+ public override get serializers(): DreamSerializers<Bedroom> {
+ return {
+ default: 'Room/BedroomSerializer',
+ summary: 'Room/BedroomSummarySerializer',
+ }
+ }
+
+ public bedTypes: DreamColumn<Bedroom, 'bedTypes'>
+}
diff --git a/api/src/app/serializers/Room/BedroomSerializer.ts b/api/src/app/serializers/Room/BedroomSerializer.ts
new file mode 100644
index 0000000..f34b5b4
--- /dev/null
+++ b/api/src/app/serializers/Room/BedroomSerializer.ts
@@ -0,0 +1,9 @@
+import { RoomSerializer, RoomSummarySerializer } from '@serializers/RoomSerializer.js'
+import Bedroom from '@models/Room/Bedroom.js'
+
+export const RoomBedroomSummarySerializer = (bedroom: Bedroom) =>
+ RoomSummarySerializer(Bedroom, bedroom)
+
+export const RoomBedroomSerializer = (bedroom: Bedroom) =>
+ RoomSerializer(Bedroom, bedroom)
+ .attribute('bedTypes')
diff --git a/api/src/db/migrations/1773153070336-create-room-bedroom.ts b/api/src/db/migrations/1773153070336-create-room-bedroom.ts
new file mode 100644
index 0000000..55d5d4c
--- /dev/null
+++ b/api/src/db/migrations/1773153070336-create-room-bedroom.ts
@@ -0,0 +1,31 @@
+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('bed_types_enum')
+ .asEnum([
+ 'twin',
+ 'bunk',
+ 'queen',
+ 'king',
+ 'cot',
+ 'sofabed'
+ ])
+ .execute()
+
+ await db.schema
+ .alterTable('rooms')
+ .addColumn('bed_types', sql`bed_types_enum[]`, col => col.notNull().defaultTo('{}'))
+ .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('bed_types')
+ .execute()
+
+ await db.schema.dropType('bed_types_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 011167c..8cf4e91 100644
--- a/api/src/openapi/mobile.openapi.json
+++ b/api/src/openapi/mobile.openapi.json
@@ -343,7 +343,14 @@
"results": {
"type": "array",
"items": {
- "$ref": "#/components/schemas/RoomBathroomSummary"
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/RoomBathroomSummary"
+ },
+ {
+ "$ref": "#/components/schemas/RoomBedroomSummary"
+ }
+ ]
}
}
}
@@ -399,6 +406,20 @@
null
]
},
+ "bedTypes": {
+ "type": "array",
+ "items": {
+ "type": "string",
+ "enum": [
+ "bunk",
+ "cot",
+ "king",
+ "queen",
+ "sofabed",
+ "twin"
+ ]
+ }
+ },
"position": {
"type": [
"integer",
@@ -415,7 +436,14 @@
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/RoomBathroom"
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/RoomBathroom"
+ },
+ {
+ "$ref": "#/components/schemas/RoomBedroom"
+ }
+ ]
}
}
},
@@ -474,7 +502,14 @@
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/RoomBathroom"
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/RoomBathroom"
+ },
+ {
+ "$ref": "#/components/schemas/RoomBedroom"
+ }
+ ]
}
}
},
@@ -527,6 +562,20 @@
null
]
},
+ "bedTypes": {
+ "type": "array",
+ "items": {
+ "type": "string",
+ "enum": [
+ "bunk",
+ "cot",
+ "king",
+ "queen",
+ "sofabed",
+ "twin"
+ ]
+ }
+ },
"position": {
"type": [
"integer",
@@ -759,6 +808,58 @@
}
}
},
+ "RoomBedroom": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "bedTypes",
+ "deletedAt",
+ "id",
+ "position",
+ "type"
+ ],
+ "properties": {
+ "bedTypes": {
+ "type": "array",
+ "items": {
+ "type": "string",
+ "description": "The following values will be allowed:\n bunk,\n cot,\n king,\n queen,\n sofabed,\n twin"
+ }
+ },
+ "deletedAt": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ },
+ "id": {
+ "type": "string"
+ },
+ "position": {
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "type": {
+ "type": "string",
+ "description": "The following values will be allowed:\n Bedroom"
+ }
+ }
+ },
+ "RoomBedroomSummary": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "id"
+ ],
+ "properties": {
+ "id": {
+ "type": "string"
+ }
+ }
+ },
"ValidationErrors": {
"type": "object",
"required": [
diff --git a/api/src/openapi/openapi.json b/api/src/openapi/openapi.json
index c741d69..e5027c7 100644
--- a/api/src/openapi/openapi.json
+++ b/api/src/openapi/openapi.json
@@ -343,7 +343,14 @@
"results": {
"type": "array",
"items": {
- "$ref": "#/components/schemas/RoomBathroomSummary"
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/RoomBathroomSummary"
+ },
+ {
+ "$ref": "#/components/schemas/RoomBedroomSummary"
+ }
+ ]
}
}
}
@@ -399,6 +406,20 @@
null
]
},
+ "bedTypes": {
+ "type": "array",
+ "items": {
+ "type": "string",
+ "enum": [
+ "bunk",
+ "cot",
+ "king",
+ "queen",
+ "sofabed",
+ "twin"
+ ]
+ }
+ },
"position": {
"type": [
"integer",
@@ -415,7 +436,14 @@
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/RoomBathroom"
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/RoomBathroom"
+ },
+ {
+ "$ref": "#/components/schemas/RoomBedroom"
+ }
+ ]
}
}
},
@@ -474,7 +502,14 @@
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/RoomBathroom"
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/RoomBathroom"
+ },
+ {
+ "$ref": "#/components/schemas/RoomBedroom"
+ }
+ ]
}
}
},
@@ -527,6 +562,20 @@
null
]
},
+ "bedTypes": {
+ "type": "array",
+ "items": {
+ "type": "string",
+ "enum": [
+ "bunk",
+ "cot",
+ "king",
+ "queen",
+ "sofabed",
+ "twin"
+ ]
+ }
+ },
"position": {
"type": [
"integer",
@@ -775,6 +824,67 @@
}
}
},
+ "RoomBedroom": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "bedTypes",
+ "deletedAt",
+ "id",
+ "position",
+ "type"
+ ],
+ "properties": {
+ "bedTypes": {
+ "type": "array",
+ "items": {
+ "type": "string",
+ "enum": [
+ "bunk",
+ "cot",
+ "king",
+ "queen",
+ "sofabed",
+ "twin"
+ ]
+ }
+ },
+ "deletedAt": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ },
+ "id": {
+ "type": "string"
+ },
+ "position": {
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "type": {
+ "type": "string",
+ "enum": [
+ "Bedroom"
+ ]
+ }
+ }
+ },
+ "RoomBedroomSummary": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "id"
+ ],
+ "properties": {
+ "id": {
+ "type": "string"
+ }
+ }
+ },
"ValidationErrors": {
"type": "object",
"required": [
diff --git a/api/src/openapi/tests.openapi.json b/api/src/openapi/tests.openapi.json
index ca33fa0..73bcb9c 100644
--- a/api/src/openapi/tests.openapi.json
+++ b/api/src/openapi/tests.openapi.json
@@ -343,7 +343,14 @@
"results": {
"type": "array",
"items": {
- "$ref": "#/components/schemas/RoomBathroomSummary"
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/RoomBathroomSummary"
+ },
+ {
+ "$ref": "#/components/schemas/RoomBedroomSummary"
+ }
+ ]
}
}
}
@@ -399,6 +406,20 @@
null
]
},
+ "bedTypes": {
+ "type": "array",
+ "items": {
+ "type": "string",
+ "enum": [
+ "bunk",
+ "cot",
+ "king",
+ "queen",
+ "sofabed",
+ "twin"
+ ]
+ }
+ },
"position": {
"type": [
"integer",
@@ -415,7 +436,14 @@
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/RoomBathroom"
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/RoomBathroom"
+ },
+ {
+ "$ref": "#/components/schemas/RoomBedroom"
+ }
+ ]
}
}
},
@@ -474,7 +502,14 @@
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/RoomBathroom"
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/RoomBathroom"
+ },
+ {
+ "$ref": "#/components/schemas/RoomBedroom"
+ }
+ ]
}
}
},
@@ -527,6 +562,20 @@
null
]
},
+ "bedTypes": {
+ "type": "array",
+ "items": {
+ "type": "string",
+ "enum": [
+ "bunk",
+ "cot",
+ "king",
+ "queen",
+ "sofabed",
+ "twin"
+ ]
+ }
+ },
"position": {
"type": [
"integer",
@@ -775,6 +824,67 @@
}
}
},
+ "RoomBedroom": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "bedTypes",
+ "deletedAt",
+ "id",
+ "position",
+ "type"
+ ],
+ "properties": {
+ "bedTypes": {
+ "type": "array",
+ "items": {
+ "type": "string",
+ "enum": [
+ "bunk",
+ "cot",
+ "king",
+ "queen",
+ "sofabed",
+ "twin"
+ ]
+ }
+ },
+ "deletedAt": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ },
+ "id": {
+ "type": "string"
+ },
+ "position": {
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "type": {
+ "type": "string",
+ "enum": [
+ "Bedroom"
+ ]
+ }
+ }
+ },
+ "RoomBedroomSummary": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "id"
+ ],
+ "properties": {
+ "id": {
+ "type": "string"
+ }
+ }
+ },
"ValidationErrors": {
"type": "object",
"required": [
diff --git a/api/src/types/db.ts b/api/src/types/db.ts
index f2fd5c5..822576f 100644
--- a/api/src/types/db.ts
+++ b/api/src/types/db.ts
@@ -68,6 +68,14 @@ import {
*/
import type { ColumnType } from 'kysely'

+export type ArrayType<T> =
+ ArrayTypeImpl<T> extends (infer U)[] ? U[] : ArrayTypeImpl<T>
+
+export type ArrayTypeImpl<T> =
+ T extends ColumnType<infer S, infer I, infer U>
+ ? ColumnType<S[], I[], U[]>
+ : T[]
+
export type BathOrShowerStylesEnum =
| 'bath'
| 'bath_and_shower'
@@ -81,6 +89,23 @@ export const BathOrShowerStylesEnumValues = [
'shower',
] as const

+export type BedTypesEnum =
+ | 'bunk'
+ | 'cot'
+ | 'king'
+ | 'queen'
+ | 'sofabed'
+ | 'twin'
+
+export const BedTypesEnumValues = [
+ 'bunk',
+ 'cot',
+ 'king',
+ 'queen',
+ 'sofabed',
+ 'twin',
+] as const
+
export type Generated<T> =
T extends ColumnType<infer S, infer I, infer U>
? ColumnType<S, I | undefined, U>
@@ -157,6 +182,7 @@ export interface Places {

export interface Rooms {
bathOrShowerStyle: BathOrShowerStylesEnum | null
+ bedTypes: Generated<ArrayType<BedTypesEnum>>
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 b4348f8..6109591 100644
--- a/api/src/types/dream.globals.ts
+++ b/api/src/types/dream.globals.ts
@@ -66,6 +66,8 @@ export const globalTypeConfig = {
'PlaceSummarySerializer',
'Room/BathroomSerializer',
'Room/BathroomSummarySerializer',
+ 'Room/BedroomSerializer',
+ 'Room/BedroomSummarySerializer',
'RoomSerializer',
'RoomSummarySerializer',
],
diff --git a/api/src/types/dream.ts b/api/src/types/dream.ts
index 3121dfe..8203e1f 100644
--- a/api/src/types/dream.ts
+++ b/api/src/types/dream.ts
@@ -64,9 +64,11 @@ import {
} from '@rvoh/dream'
import {
type BathOrShowerStylesEnum,
+ type BedTypesEnum,
type PlaceStylesEnum,
type RoomTypesEnum,
BathOrShowerStylesEnumValues,
+ BedTypesEnumValues,
PlaceStylesEnumValues,
RoomTypesEnumValues,
} from './db.js'
@@ -408,6 +410,7 @@ export const schema = {
},
nonJsonColumnNames: [
'bathOrShowerStyle',
+ 'bedTypes',
'createdAt',
'deletedAt',
'id',
@@ -426,6 +429,15 @@ export const schema = {
allowNull: true,
isArray: false,
},
+ bedTypes: {
+ coercedType: {} as BedTypesEnum[],
+ enumType: {} as BedTypesEnum,
+ enumArrayType: [] as BedTypesEnum[],
+ enumValues: BedTypesEnumValues,
+ dbType: 'bed_types_enum[]',
+ allowNull: false,
+ isArray: true,
+ },
createdAt: {
coercedType: {} as DateTime,
enumType: null,
@@ -598,6 +610,7 @@ export const connectionTypeConfig = {
Place: 'places',
Room: 'rooms',
'Room/Bathroom': 'rooms',
+ 'Room/Bedroom': 'rooms',
User: 'users',
},
},
diff --git a/api/src/types/openapi/tests.openapi.d.ts b/api/src/types/openapi/tests.openapi.d.ts
index 603ae3d..60e921d 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"]["RoomBathroomSummary"][];
+ results: (components["schemas"]["RoomBathroomSummary"] | components["schemas"]["RoomBedroomSummary"])[];
};
};
};
@@ -257,6 +257,7 @@ export interface paths {
"application/json": {
/** @enum {string|null} */
bathOrShowerStyle?: "bath" | "bath_and_shower" | "none" | "shower" | null;
+ bedTypes?: ("bunk" | "cot" | "king" | "queen" | "sofabed" | "twin")[];
position?: number | null;
};
};
@@ -268,7 +269,7 @@ export interface paths {
[name: string]: unknown;
};
content: {
- "application/json": components["schemas"]["RoomBathroom"];
+ "application/json": components["schemas"]["RoomBathroom"] | components["schemas"]["RoomBedroom"];
};
};
400: components["responses"]["BadRequest"];
@@ -315,7 +316,7 @@ export interface paths {
[name: string]: unknown;
};
content: {
- "application/json": components["schemas"]["RoomBathroom"];
+ "application/json": components["schemas"]["RoomBathroom"] | components["schemas"]["RoomBedroom"];
};
};
400: components["responses"]["BadRequest"];
@@ -371,6 +372,7 @@ export interface paths {
"application/json": {
/** @enum {string|null} */
bathOrShowerStyle?: "bath" | "bath_and_shower" | "none" | "shower" | null;
+ bedTypes?: ("bunk" | "cot" | "king" | "queen" | "sofabed" | "twin")[];
position?: number | null;
};
};
@@ -432,6 +434,18 @@ export interface components {
RoomBathroomSummary: {
id: string;
};
+ RoomBedroom: {
+ bedTypes: ("bunk" | "cot" | "king" | "queen" | "sofabed" | "twin")[];
+ /** Format: date-time */
+ deletedAt: string | null;
+ id: string;
+ position: number | null;
+ /** @enum {string} */
+ type: "Bedroom";
+ };
+ RoomBedroomSummary: {
+ id: string;
+ };
ValidationErrors: {
/** @enum {string} */
type: "validation";