From e0ec4c2b49c2b5f89592b26922e162b2880f82ac Mon Sep 17 00:00:00 2001 From: Thanu Poptiphueng Date: Fri, 19 Apr 2024 15:05:01 +0700 Subject: [PATCH] added remaining user api --- Taskfile.yml | 9 +- drizzle/0000_hot_gressill.sql | 56 ----- drizzle/meta/0000_snapshot.json | 359 -------------------------------- drizzle/meta/_journal.json | 14 +- src/app.ts | 1 - src/schema.ts | 18 +- src/userRoute.ts | 69 +++++- 7 files changed, 83 insertions(+), 443 deletions(-) delete mode 100644 drizzle/0000_hot_gressill.sql delete mode 100644 drizzle/meta/0000_snapshot.json diff --git a/Taskfile.yml b/Taskfile.yml index cf74257..b20b644 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -1,10 +1,15 @@ version: "3" tasks: - generate: + db:generate: cmds: - pnpm drizzle-kit generate:sqlite - + db:push: + cmds: + - pnpm drizzle-kit push:sqlite + db:drop: + cmds: + - pnpm drizzle-kit drop studio: cmds: - pnpm drizzle-kit studio diff --git a/drizzle/0000_hot_gressill.sql b/drizzle/0000_hot_gressill.sql deleted file mode 100644 index a8b9460..0000000 --- a/drizzle/0000_hot_gressill.sql +++ /dev/null @@ -1,56 +0,0 @@ -CREATE TABLE `groups` ( - `id` integer PRIMARY KEY AUTOINCREMENT NOT NULL, - `name` text -); ---> statement-breakpoint -CREATE TABLE `opinions` ( - `id` integer PRIMARY KEY AUTOINCREMENT NOT NULL, - `name` text, - `type` text -); ---> statement-breakpoint -CREATE TABLE `phone_tokens` ( - `phone` text PRIMARY KEY NOT NULL, - `token` text NOT NULL, - `created_on` integer DEFAULT CURRENT_TIMESTAMP -); ---> statement-breakpoint -CREATE TABLE `provinces` ( - `id` integer PRIMARY KEY AUTOINCREMENT NOT NULL, - `name` text NOT NULL -); ---> statement-breakpoint -CREATE TABLE `users` ( - `id` integer PRIMARY KEY AUTOINCREMENT NOT NULL, - `firstName` text NOT NULL, - `lastName` text NOT NULL, - `title` text NOT NULL, - `phone` text NOT NULL, - `email` text, - `job` text NOT NULL, - `education` text NOT NULL, - `vision` text, - `reason` text, - `group_id` integer NOT NULL, - `zone_id` integer NOT NULL, - FOREIGN KEY (`group_id`) REFERENCES `groups`(`id`) ON UPDATE no action ON DELETE no action, - FOREIGN KEY (`zone_id`) REFERENCES `zones`(`id`) ON UPDATE no action ON DELETE no action -); ---> statement-breakpoint -CREATE TABLE `user_opinions` ( - `user_id` integer, - `opinion_id` integer, - `choice` text DEFAULT 'ignore', - PRIMARY KEY(`opinion_id`, `user_id`), - FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action, - FOREIGN KEY (`opinion_id`) REFERENCES `opinions`(`id`) ON UPDATE no action ON DELETE no action -); ---> statement-breakpoint -CREATE TABLE `zones` ( - `id` integer PRIMARY KEY AUTOINCREMENT NOT NULL, - `name` text NOT NULL, - `province_id` integer NOT NULL, - FOREIGN KEY (`province_id`) REFERENCES `provinces`(`id`) ON UPDATE no action ON DELETE no action -); ---> statement-breakpoint -CREATE UNIQUE INDEX `users_phone_unique` ON `users` (`phone`); \ No newline at end of file diff --git a/drizzle/meta/0000_snapshot.json b/drizzle/meta/0000_snapshot.json deleted file mode 100644 index 9293ca3..0000000 --- a/drizzle/meta/0000_snapshot.json +++ /dev/null @@ -1,359 +0,0 @@ -{ - "version": "5", - "dialect": "sqlite", - "id": "86633c77-c16c-46e0-92a5-f0e696d3276c", - "prevId": "00000000-0000-0000-0000-000000000000", - "tables": { - "groups": { - "name": "groups", - "columns": { - "id": { - "name": "id", - "type": "integer", - "primaryKey": true, - "notNull": true, - "autoincrement": true - }, - "name": { - "name": "name", - "type": "text", - "primaryKey": false, - "notNull": false, - "autoincrement": false - } - }, - "indexes": {}, - "foreignKeys": {}, - "compositePrimaryKeys": {}, - "uniqueConstraints": {} - }, - "opinions": { - "name": "opinions", - "columns": { - "id": { - "name": "id", - "type": "integer", - "primaryKey": true, - "notNull": true, - "autoincrement": true - }, - "name": { - "name": "name", - "type": "text", - "primaryKey": false, - "notNull": false, - "autoincrement": false - }, - "type": { - "name": "type", - "type": "text", - "primaryKey": false, - "notNull": false, - "autoincrement": false - } - }, - "indexes": {}, - "foreignKeys": {}, - "compositePrimaryKeys": {}, - "uniqueConstraints": {} - }, - "phone_tokens": { - "name": "phone_tokens", - "columns": { - "phone": { - "name": "phone", - "type": "text", - "primaryKey": true, - "notNull": true, - "autoincrement": false - }, - "token": { - "name": "token", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "created_on": { - "name": "created_on", - "type": "integer", - "primaryKey": false, - "notNull": false, - "autoincrement": false, - "default": "CURRENT_TIMESTAMP" - } - }, - "indexes": {}, - "foreignKeys": {}, - "compositePrimaryKeys": {}, - "uniqueConstraints": {} - }, - "provinces": { - "name": "provinces", - "columns": { - "id": { - "name": "id", - "type": "integer", - "primaryKey": true, - "notNull": true, - "autoincrement": true - }, - "name": { - "name": "name", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false - } - }, - "indexes": {}, - "foreignKeys": {}, - "compositePrimaryKeys": {}, - "uniqueConstraints": {} - }, - "users": { - "name": "users", - "columns": { - "id": { - "name": "id", - "type": "integer", - "primaryKey": true, - "notNull": true, - "autoincrement": true - }, - "firstName": { - "name": "firstName", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "lastName": { - "name": "lastName", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "title": { - "name": "title", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "phone": { - "name": "phone", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "email": { - "name": "email", - "type": "text", - "primaryKey": false, - "notNull": false, - "autoincrement": false - }, - "job": { - "name": "job", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "education": { - "name": "education", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "vision": { - "name": "vision", - "type": "text", - "primaryKey": false, - "notNull": false, - "autoincrement": false - }, - "reason": { - "name": "reason", - "type": "text", - "primaryKey": false, - "notNull": false, - "autoincrement": false - }, - "group_id": { - "name": "group_id", - "type": "integer", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "zone_id": { - "name": "zone_id", - "type": "integer", - "primaryKey": false, - "notNull": true, - "autoincrement": false - } - }, - "indexes": { - "users_phone_unique": { - "name": "users_phone_unique", - "columns": [ - "phone" - ], - "isUnique": true - } - }, - "foreignKeys": { - "users_group_id_groups_id_fk": { - "name": "users_group_id_groups_id_fk", - "tableFrom": "users", - "tableTo": "groups", - "columnsFrom": [ - "group_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "no action", - "onUpdate": "no action" - }, - "users_zone_id_zones_id_fk": { - "name": "users_zone_id_zones_id_fk", - "tableFrom": "users", - "tableTo": "zones", - "columnsFrom": [ - "zone_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "no action", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {} - }, - "user_opinions": { - "name": "user_opinions", - "columns": { - "user_id": { - "name": "user_id", - "type": "integer", - "primaryKey": false, - "notNull": false, - "autoincrement": false - }, - "opinion_id": { - "name": "opinion_id", - "type": "integer", - "primaryKey": false, - "notNull": false, - "autoincrement": false - }, - "choice": { - "name": "choice", - "type": "text", - "primaryKey": false, - "notNull": false, - "autoincrement": false, - "default": "'ignore'" - } - }, - "indexes": {}, - "foreignKeys": { - "user_opinions_user_id_users_id_fk": { - "name": "user_opinions_user_id_users_id_fk", - "tableFrom": "user_opinions", - "tableTo": "users", - "columnsFrom": [ - "user_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "no action", - "onUpdate": "no action" - }, - "user_opinions_opinion_id_opinions_id_fk": { - "name": "user_opinions_opinion_id_opinions_id_fk", - "tableFrom": "user_opinions", - "tableTo": "opinions", - "columnsFrom": [ - "opinion_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "no action", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": { - "user_opinions_user_id_opinion_id_pk": { - "columns": [ - "opinion_id", - "user_id" - ], - "name": "user_opinions_user_id_opinion_id_pk" - } - }, - "uniqueConstraints": {} - }, - "zones": { - "name": "zones", - "columns": { - "id": { - "name": "id", - "type": "integer", - "primaryKey": true, - "notNull": true, - "autoincrement": true - }, - "name": { - "name": "name", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "province_id": { - "name": "province_id", - "type": "integer", - "primaryKey": false, - "notNull": true, - "autoincrement": false - } - }, - "indexes": {}, - "foreignKeys": { - "zones_province_id_provinces_id_fk": { - "name": "zones_province_id_provinces_id_fk", - "tableFrom": "zones", - "tableTo": "provinces", - "columnsFrom": [ - "province_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "no action", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {} - } - }, - "enums": {}, - "_meta": { - "schemas": {}, - "tables": {}, - "columns": {} - } -} \ No newline at end of file diff --git a/drizzle/meta/_journal.json b/drizzle/meta/_journal.json index dc83c81..d0f23be 100644 --- a/drizzle/meta/_journal.json +++ b/drizzle/meta/_journal.json @@ -1,13 +1 @@ -{ - "version": "5", - "dialect": "sqlite", - "entries": [ - { - "idx": 0, - "version": "5", - "when": 1713506374595, - "tag": "0000_hot_gressill", - "breakpoints": true - } - ] -} \ No newline at end of file +{"version":"5","dialect":"sqlite","entries":[]} \ No newline at end of file diff --git a/src/app.ts b/src/app.ts index 021506a..0d0b19f 100644 --- a/src/app.ts +++ b/src/app.ts @@ -1,6 +1,5 @@ import { createContext, router } from "./trpc"; import { createHTTPServer } from "@trpc/server/adapters/standalone"; -import { db } from "./db"; import { userRoute } from "./userRoute"; import { runPlayground } from "./playgroud"; import cors from "cors"; diff --git a/src/schema.ts b/src/schema.ts index 33b0bec..293e2f3 100644 --- a/src/schema.ts +++ b/src/schema.ts @@ -41,7 +41,7 @@ export const userRelation = relations(user, ({ many, one }) => ({ //----------------Group export const group = sqliteTable("groups", { id: integer("id", { mode: "number" }).primaryKey({ autoIncrement: true }), - name: text("name"), + name: text("name").notNull(), }); export const groupRelation = relations(group, ({ many }) => ({ @@ -51,19 +51,25 @@ export const groupRelation = relations(group, ({ many }) => ({ //----------------Opinion export const opinion = sqliteTable("opinions", { id: integer("id", { mode: "number" }).primaryKey({ autoIncrement: true }), - name: text("name"), - type: text("type", { enum: ["3Choice", "4Choice"] }), + name: text("name").notNull(), + type: text("type", { enum: ["3Choice", "4Choice"] }) + .default("3Choice") + .notNull(), }); //----------------UserOpinion export const userOpinion = sqliteTable( "user_opinions", { - userId: integer("user_id").references(() => user.id), - opinionId: integer("opinion_id").references(() => opinion.id), + userId: integer("user_id") + .notNull() + .references(() => user.id), + opinionId: integer("opinion_id") + .notNull() + .references(() => opinion.id), choice: text("choice", { enum: ["agree", "disagree", "deciding", "ignore"], - }).default("ignore"), + }).default("deciding"), }, (t) => ({ pk: primaryKey({ columns: [t.userId, t.opinionId] }), diff --git a/src/userRoute.ts b/src/userRoute.ts index fbd08e3..16b4a1b 100644 --- a/src/userRoute.ts +++ b/src/userRoute.ts @@ -5,7 +5,7 @@ import { protectedProcedure, } from "./trpc"; import { db } from "./db"; -import { phoneToken, user } from "./schema"; +import { phoneToken, user, userOpinion } from "./schema"; import { createInsertSchema } from "drizzle-zod"; import { z } from "zod"; import { SQL, eq } from "drizzle-orm"; @@ -13,6 +13,19 @@ import { Config } from "./config"; import { TRPCError } from "@trpc/server"; const userInsertSchema = createInsertSchema(user); +const opinionInsertSchema = createInsertSchema(userOpinion) + .omit({ + userId: true, + }) + .array() + .default([]); + +const opinionUpdateSchema = createInsertSchema(userOpinion) + .omit({ + userId: true, + }) + .required({ opinionId: true }); +type OpinionInsertSchema = z.infer; type UserInsertSchema = z.infer; export const userRoute = router({ @@ -30,9 +43,14 @@ export const userRoute = router({ await getAllUser(input.offset, input.limit, input.group, input.zone) ), createUser: verifiedPhone - .input(userInsertSchema.omit({ id: true, phone: true })) + .input( + userInsertSchema.omit({ id: true, phone: true }).extend({ + opinions: opinionInsertSchema, + }) + ) .mutation( - async ({ input, ctx }) => await createUser({ ...input, phone: ctx.phone }) + async ({ input, ctx }) => + await createUser({ ...input, phone: ctx.phone }, input.opinions) ), requestOtp: publicProcedure .input(z.object({ phone: z.string().trim().min(5) })) @@ -45,6 +63,12 @@ export const userRoute = router({ }) ) .mutation(async ({ input }) => await verifyOtp(input.token, input.pin)), + changeOpinion: protectedProcedure + .input(opinionUpdateSchema) + .mutation( + async ({ input, ctx }) => + await changeOpinion(input.opinionId, ctx.user.id, input.choice) + ), }); async function getAllUser( @@ -76,10 +100,18 @@ async function getAllUser( }); return users; } -async function createUser(newUser: UserInsertSchema) { +async function createUser( + newUser: UserInsertSchema, + opinions: OpinionInsertSchema +) { try { - let result = await db.insert(user).values(newUser); - return result.lastInsertRowid; + let result = ( + await db.insert(user).values(newUser).returning({ id: user.id }) + )[0]; + for (let op of opinions) { + await db.insert(userOpinion).values({ ...op, userId: result.id }); + } + return { status: "OK" }; } catch (e) { console.error(e); throw new Error(`Unable to create new user:\n${e}`); @@ -168,3 +200,28 @@ async function verifyOtp(token: string, pin: string) { }); } } + +async function changeOpinion( + opinionId: number, + userId: number, + opinion: OpinionInsertSchema[0]["choice"] +) { + try { + db.insert(userOpinion) + .values({ + opinionId, + userId, + choice: opinion, + }) + .onConflictDoUpdate({ + target: [userOpinion.userId, userOpinion.opinionId], + set: { choice: opinion }, + }); + } catch (e) { + console.error(e); + throw new TRPCError({ + message: "Error updating schema", + code: "INTERNAL_SERVER_ERROR", + }); + } +}