diff --git a/api/routes/_.context.ts b/api/routes/_.context.ts index 7ae9a0c..2bbd799 100644 --- a/api/routes/_.context.ts +++ b/api/routes/_.context.ts @@ -8,4 +8,179 @@ * * See https://counterfact.dev/docs/usage.html#working-with-state-the-codecontextcode-object-and-codecontexttscode */ -export class Context {} + +import type { Pet } from "../types/components/schemas/Pet.js"; +import type { User } from "../types/components/schemas/User.js"; +import type { Order } from "../types/components/schemas/Order.js"; + +export class Context { + pets: Pet[] = [ + { + id: 1, + name: "Buddy", + category: { id: 1, name: "Dogs" }, + photoUrls: ["https://example.com/buddy.jpg"], + tags: [{ id: 1, name: "friendly" }], + status: "available", + }, + { + id: 2, + name: "Whiskers", + category: { id: 2, name: "Cats" }, + photoUrls: ["https://example.com/whiskers.jpg"], + tags: [{ id: 2, name: "indoor" }], + status: "available", + }, + { + id: 3, + name: "Goldie", + category: { id: 3, name: "Fish" }, + photoUrls: ["https://example.com/goldie.jpg"], + tags: [], + status: "pending", + }, + { + id: 4, + name: "Max", + category: { id: 1, name: "Dogs" }, + photoUrls: ["https://example.com/max.jpg"], + tags: [{ id: 3, name: "trained" }], + status: "sold", + }, + ]; + + users: User[] = [ + { + id: 1, + username: "user1", + firstName: "John", + lastName: "Doe", + email: "john@example.com", + password: "password123", + phone: "555-1234", + userStatus: 1, + }, + { + id: 2, + username: "jane_smith", + firstName: "Jane", + lastName: "Smith", + email: "jane@example.com", + password: "secret456", + phone: "555-5678", + userStatus: 1, + }, + ]; + + orders: Order[] = [ + { + id: 1, + petId: 1, + quantity: 1, + shipDate: "2024-01-15T10:00:00Z", + status: "placed", + complete: false, + }, + { + id: 2, + petId: 4, + quantity: 1, + shipDate: "2024-01-10T08:00:00Z", + status: "delivered", + complete: true, + }, + ]; + + private nextPetId = 5; + private nextUserId = 3; + private nextOrderId = 3; + + addPet(pet: Pet): Pet { + const newPet = { ...pet, id: this.nextPetId++ }; + this.pets.push(newPet); + return newPet; + } + + updatePet(pet: Pet): Pet | undefined { + const index = this.pets.findIndex((p) => p.id === pet.id); + if (index === -1) return undefined; + this.pets[index] = pet; + return pet; + } + + getPetById(id: number): Pet | undefined { + return this.pets.find((p) => p.id === id); + } + + deletePet(id: number): boolean { + const index = this.pets.findIndex((p) => p.id === id); + if (index === -1) return false; + this.pets.splice(index, 1); + return true; + } + + findPetsByStatus(status: "available" | "pending" | "sold"): Pet[] { + return this.pets.filter((p) => p.status === status); + } + + findPetsByTags(tags: string[]): Pet[] { + return this.pets.filter((p) => + p.tags?.some((t) => t.name !== undefined && tags.includes(t.name)), + ); + } + + addUser(user: User): User { + const newUser = { ...user, id: this.nextUserId++ }; + this.users.push(newUser); + return newUser; + } + + getUserByUsername(username: string): User | undefined { + return this.users.find((u) => u.username === username); + } + + updateUser(username: string, user: User): User | undefined { + const index = this.users.findIndex((u) => u.username === username); + if (index === -1) return undefined; + this.users[index] = { ...user, username }; + return this.users[index]; + } + + deleteUser(username: string): boolean { + const index = this.users.findIndex((u) => u.username === username); + if (index === -1) return false; + this.users.splice(index, 1); + return true; + } + + placeOrder(order: Order): Order { + const newOrder = { ...order, id: this.nextOrderId++ }; + this.orders.push(newOrder); + return newOrder; + } + + getOrderById(id: number): Order | undefined { + return this.orders.find((o) => o.id === id); + } + + deleteOrder(id: number): boolean { + const index = this.orders.findIndex((o) => o.id === id); + if (index === -1) return false; + this.orders.splice(index, 1); + return true; + } + + getInventory(): { [key: string]: number } { + const inventory: { [key: string]: number } = { + available: 0, + pending: 0, + sold: 0, + }; + for (const pet of this.pets) { + if (pet.status) { + inventory[pet.status] = (inventory[pet.status] ?? 0) + 1; + } + } + return inventory; + } +} diff --git a/api/routes/pet.ts b/api/routes/pet.ts index a7e23a5..8da9bd8 100644 --- a/api/routes/pet.ts +++ b/api/routes/pet.ts @@ -1,10 +1,18 @@ import type { updatePet } from "../types/paths/pet.types.js"; import type { addPet } from "../types/paths/pet.types.js"; -export const PUT: updatePet = async ($) => { - return $.response[200].random(); +export const PUT: updatePet = ($) => { + if (!$.body.id) { + return $.response[400]; + } + const updated = $.context.updatePet($.body); + if (!updated) { + return $.response[404]; + } + return $.response[200].json(updated); }; -export const POST: addPet = async ($) => { - return $.response[200].random(); +export const POST: addPet = ($) => { + const pet = $.context.addPet($.body); + return $.response[200].json(pet); }; diff --git a/api/routes/pet/findByStatus.ts b/api/routes/pet/findByStatus.ts index 8b9195f..2a4567e 100644 --- a/api/routes/pet/findByStatus.ts +++ b/api/routes/pet/findByStatus.ts @@ -1,5 +1,6 @@ import type { findPetsByStatus } from "../../types/paths/pet/findByStatus.types.js"; -export const GET: findPetsByStatus = async ($) => { - return $.response[200].random(); +export const GET: findPetsByStatus = ($) => { + const pets = $.context.findPetsByStatus($.query.status); + return $.response[200].json(pets); }; diff --git a/api/routes/pet/findByTags.ts b/api/routes/pet/findByTags.ts index 378b273..9db1b5c 100644 --- a/api/routes/pet/findByTags.ts +++ b/api/routes/pet/findByTags.ts @@ -1,5 +1,6 @@ import type { findPetsByTags } from "../../types/paths/pet/findByTags.types.js"; -export const GET: findPetsByTags = async ($) => { - return $.response[200].random(); +export const GET: findPetsByTags = ($) => { + const pets = $.context.findPetsByTags($.query.tags); + return $.response[200].json(pets); }; diff --git a/api/routes/pet/{petId}.ts b/api/routes/pet/{petId}.ts index 4f53319..1fc4187 100644 --- a/api/routes/pet/{petId}.ts +++ b/api/routes/pet/{petId}.ts @@ -2,14 +2,36 @@ import type { getPetById } from "../../types/paths/pet/{petId}.types.js"; import type { updatePetWithForm } from "../../types/paths/pet/{petId}.types.js"; import type { deletePet } from "../../types/paths/pet/{petId}.types.js"; -export const GET: getPetById = async ($) => { - return $.response[200].random(); +export const GET: getPetById = ($) => { + const pet = $.context.getPetById($.path.petId); + if (!pet) { + return $.response[404]; + } + return $.response[200].json(pet); }; -export const POST: updatePetWithForm = async ($) => { - return $.response[200].random(); +export const POST: updatePetWithForm = ($) => { + const pet = $.context.getPetById($.path.petId); + if (!pet) { + return $.response[400]; + } + const updated = $.context.updatePet({ + ...pet, + ...($.query.name !== undefined ? { name: $.query.name } : {}), + ...($.query.status !== undefined + ? { status: $.query.status as "available" | "pending" | "sold" } + : {}), + }); + if (!updated) { + return $.response[400]; + } + return $.response[200].json(updated); }; -export const DELETE: deletePet = async ($) => { +export const DELETE: deletePet = ($) => { + const deleted = $.context.deletePet($.path.petId); + if (!deleted) { + return $.response[400]; + } return $.response[200]; }; diff --git a/api/routes/pet/{petId}/uploadImage.ts b/api/routes/pet/{petId}/uploadImage.ts index f3262ba..fd5de1c 100644 --- a/api/routes/pet/{petId}/uploadImage.ts +++ b/api/routes/pet/{petId}/uploadImage.ts @@ -1,5 +1,13 @@ import type { uploadFile } from "../../../types/paths/pet/{petId}/uploadImage.types.js"; -export const POST: uploadFile = async ($) => { - return $.response[200].random(); +export const POST: uploadFile = ($) => { + const pet = $.context.getPetById($.path.petId); + if (!pet) { + return $.response[404]; + } + return $.response[200].json({ + code: 200, + type: "unknown", + message: `File uploaded for pet ${$.path.petId}`, + }); }; diff --git a/api/routes/store/inventory.ts b/api/routes/store/inventory.ts index 5f57b3a..24e74bb 100644 --- a/api/routes/store/inventory.ts +++ b/api/routes/store/inventory.ts @@ -1,5 +1,6 @@ import type { getInventory } from "../../types/paths/store/inventory.types.js"; -export const GET: getInventory = async ($) => { - return $.response[200].random(); +export const GET: getInventory = ($) => { + const inventory = $.context.getInventory(); + return $.response[200].json(inventory); }; diff --git a/api/routes/store/order.ts b/api/routes/store/order.ts index 49e7097..2388046 100644 --- a/api/routes/store/order.ts +++ b/api/routes/store/order.ts @@ -1,5 +1,6 @@ import type { placeOrder } from "../../types/paths/store/order.types.js"; -export const POST: placeOrder = async ($) => { - return $.response[200].random(); +export const POST: placeOrder = ($) => { + const order = $.context.placeOrder($.body); + return $.response[200].json(order); }; diff --git a/api/routes/store/order/{orderId}.ts b/api/routes/store/order/{orderId}.ts index 1604417..660f051 100644 --- a/api/routes/store/order/{orderId}.ts +++ b/api/routes/store/order/{orderId}.ts @@ -1,10 +1,18 @@ import type { getOrderById } from "../../../types/paths/store/order/{orderId}.types.js"; import type { deleteOrder } from "../../../types/paths/store/order/{orderId}.types.js"; -export const GET: getOrderById = async ($) => { - return $.response[200].random(); +export const GET: getOrderById = ($) => { + const order = $.context.getOrderById($.path.orderId); + if (!order) { + return $.response[404]; + } + return $.response[200].json(order); }; -export const DELETE: deleteOrder = async ($) => { +export const DELETE: deleteOrder = ($) => { + const deleted = $.context.deleteOrder($.path.orderId); + if (!deleted) { + return $.response[404]; + } return $.response[200]; }; diff --git a/api/routes/user.ts b/api/routes/user.ts index da9a0a4..c1efc68 100644 --- a/api/routes/user.ts +++ b/api/routes/user.ts @@ -1,5 +1,6 @@ import type { createUser } from "../types/paths/user.types.js"; -export const POST: createUser = async ($) => { - return $.response[200].random(); +export const POST: createUser = ($) => { + const user = $.context.addUser($.body); + return $.response[200].json(user); }; diff --git a/api/routes/user/createWithList.ts b/api/routes/user/createWithList.ts index 72c66a0..0c30fdc 100644 --- a/api/routes/user/createWithList.ts +++ b/api/routes/user/createWithList.ts @@ -1,5 +1,7 @@ import type { createUsersWithListInput } from "../../types/paths/user/createWithList.types.js"; -export const POST: createUsersWithListInput = async ($) => { - return $.response[200].random(); +export const POST: createUsersWithListInput = ($) => { + const users = $.body.map((user) => $.context.addUser(user)); + // The OpenAPI spec returns a single User on 200; return the first created user + return $.response[200].json(users[0]); }; diff --git a/api/routes/user/login.ts b/api/routes/user/login.ts index 27ab891..fc86e7a 100644 --- a/api/routes/user/login.ts +++ b/api/routes/user/login.ts @@ -1,5 +1,11 @@ import type { loginUser } from "../../types/paths/user/login.types.js"; -export const GET: loginUser = async ($) => { - return $.response[200].random(); +export const GET: loginUser = ($) => { + const user = $.context.users.find( + (u) => u.username === $.query.username && u.password === $.query.password, + ); + if (!user) { + return $.response[400]; + } + return $.response[200].json(`Logged in as ${user.username}`); }; diff --git a/api/routes/user/logout.ts b/api/routes/user/logout.ts index 1ce507e..5ad2b80 100644 --- a/api/routes/user/logout.ts +++ b/api/routes/user/logout.ts @@ -1,5 +1,5 @@ import type { logoutUser } from "../../types/paths/user/logout.types.js"; -export const GET: logoutUser = async ($) => { +export const GET: logoutUser = ($) => { return $.response[200]; }; diff --git a/api/routes/user/{username}.ts b/api/routes/user/{username}.ts index 9064fa5..5e7f655 100644 --- a/api/routes/user/{username}.ts +++ b/api/routes/user/{username}.ts @@ -2,14 +2,26 @@ import type { getUserByName } from "../../types/paths/user/{username}.types.js"; import type { updateUser } from "../../types/paths/user/{username}.types.js"; import type { deleteUser } from "../../types/paths/user/{username}.types.js"; -export const GET: getUserByName = async ($) => { - return $.response[200].random(); +export const GET: getUserByName = ($) => { + const user = $.context.getUserByUsername($.path.username); + if (!user) { + return $.response[404]; + } + return $.response[200].json(user); }; -export const PUT: updateUser = async ($) => { +export const PUT: updateUser = ($) => { + const updated = $.context.updateUser($.path.username, $.body); + if (!updated) { + return $.response[404]; + } return $.response[200]; }; -export const DELETE: deleteUser = async ($) => { +export const DELETE: deleteUser = ($) => { + const deleted = $.context.deleteUser($.path.username); + if (!deleted) { + return $.response[404]; + } return $.response[200]; };