From adcc19564fdf4cb84cf804356f8f41d0415f09fa Mon Sep 17 00:00:00 2001 From: ntr Date: Thu, 28 Jul 2022 00:18:11 +1000 Subject: [PATCH] prisma pretty noice --- docker/docker-entrypoint-initdb.d/pg.init.sh | 8 -- index.ts | 11 +- migrations/20220725073815_users.js | 17 --- migrations/20220725080947_wants.js | 27 ---- migrations/20220725083125_listings.js | 23 ---- .../20220727084210_users/migration.sql | 13 -- prisma/migrations/migration_lock.toml | 3 - prisma/schema.prisma | 48 +++++-- src/discogs.ts | 11 +- src/mcrates.d.ts | 34 ++--- src/pg.ts | 123 +++++++++--------- 11 files changed, 133 insertions(+), 185 deletions(-) delete mode 100644 docker/docker-entrypoint-initdb.d/pg.init.sh delete mode 100644 migrations/20220725073815_users.js delete mode 100644 migrations/20220725080947_wants.js delete mode 100644 migrations/20220725083125_listings.js delete mode 100644 prisma/migrations/20220727084210_users/migration.sql delete mode 100644 prisma/migrations/migration_lock.toml diff --git a/docker/docker-entrypoint-initdb.d/pg.init.sh b/docker/docker-entrypoint-initdb.d/pg.init.sh deleted file mode 100644 index 961cbd3..0000000 --- a/docker/docker-entrypoint-initdb.d/pg.init.sh +++ /dev/null @@ -1,8 +0,0 @@ -# #!/bin/bash -# set -e - -# psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL -# CREATE USER mcrates; -# CREATE DATABASE mcrates; -# GRANT ALL PRIVILEGES ON DATABASE mcrates TO mcrates; -# EOSQL \ No newline at end of file diff --git a/index.ts b/index.ts index 794c4ce..3952834 100644 --- a/index.ts +++ b/index.ts @@ -1,13 +1,15 @@ import fs from 'fs/promises'; import _ from 'lodash'; import Promise from 'bluebird'; +import { PrismaClient } from '@prisma/client' import discogsService from './src/discogs'; import pgService from './src/pg'; import { logAxiosError } from './src/utils'; async function start() { - const pg = pgService(); + const prisma = new PrismaClient(); + const pg = pgService(prisma); const discogs = discogsService(pg); const usernames = [ @@ -47,9 +49,10 @@ async function start() { // return listings; } - return Promise.resolve(usernames) - .map(u => discogs.user(u)) - .map(populateUserListings); + for (const u of usernames) { + const user = await discogs.user(u) + await populateUserListings(user); + } // return Promise.map(releases, async w => { // try { diff --git a/migrations/20220725073815_users.js b/migrations/20220725073815_users.js deleted file mode 100644 index 11d54dd..0000000 --- a/migrations/20220725073815_users.js +++ /dev/null @@ -1,17 +0,0 @@ -exports.up = async knex => { - await knex.schema.createTable('users', table => { - table.uuid('id') - .primary() - .defaultTo(knex.raw('gen_random_uuid()')); - - table.string('discogs') - .unique(); - - table.jsonb('data') - .notNullable(); - - table.timestamps(true, true); - }); -}; - -exports.down = async () => true; diff --git a/migrations/20220725080947_wants.js b/migrations/20220725080947_wants.js deleted file mode 100644 index 73adae2..0000000 --- a/migrations/20220725080947_wants.js +++ /dev/null @@ -1,27 +0,0 @@ -exports.up = async knex => { - await knex.schema.createTable('wants', table => { - table.uuid('id') - .primary() - .defaultTo(knex.raw('gen_random_uuid()')); - - table.integer('release') - .notNullable() - .index(); - - table.uuid('user') - table.foreign('user') - .references('id') - .inTable('users') - .onDelete('CASCADE'); - - table.jsonb('data') - .notNullable(); - - // user can only want a release once - table.unique(['user', 'release']); - - table.timestamps(true, true); - }); -}; - -exports.down = async () => true; diff --git a/migrations/20220725083125_listings.js b/migrations/20220725083125_listings.js deleted file mode 100644 index 20ff3c6..0000000 --- a/migrations/20220725083125_listings.js +++ /dev/null @@ -1,23 +0,0 @@ -exports.up = async knex => { - await knex.schema.createTable('listings', table => { - table.integer('id') - .primary(); - - table.uuid('want') - table.foreign('want') - .references('id') - .inTable('wants') - .onDelete('CASCADE'); - - table.integer('seller') - .notNullable() - .index(); - - table.jsonb('data') - .notNullable(); - - table.timestamps(true, true); - }); -}; - -exports.down = async () => true; diff --git a/prisma/migrations/20220727084210_users/migration.sql b/prisma/migrations/20220727084210_users/migration.sql deleted file mode 100644 index 62f37d7..0000000 --- a/prisma/migrations/20220727084210_users/migration.sql +++ /dev/null @@ -1,13 +0,0 @@ --- CreateTable -CREATE TABLE "User" ( - "id" TEXT NOT NULL, - "discogs" TEXT, - "data" JSONB NOT NULL, - "creaedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" TIMESTAMP(3) NOT NULL, - - CONSTRAINT "User_pkey" PRIMARY KEY ("id") -); - --- CreateIndex -CREATE UNIQUE INDEX "User_discogs_key" ON "User"("discogs"); diff --git a/prisma/migrations/migration_lock.toml b/prisma/migrations/migration_lock.toml deleted file mode 100644 index fbffa92..0000000 --- a/prisma/migrations/migration_lock.toml +++ /dev/null @@ -1,3 +0,0 @@ -# Please do not edit this file manually -# It should be added in your version-control system (i.e. Git) -provider = "postgresql" \ No newline at end of file diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 173db4f..ee6bb61 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -1,6 +1,3 @@ -// This is your Prisma schema file, -// learn more about it in the docs: https://pris.ly/d/prisma-schema - generator client { provider = "prisma-client-js" } @@ -10,11 +7,44 @@ datasource db { url = env("DATABASE_URL") } -model User { - id String @id @default(uuid()) - discogs String? @unique - data Json +model Listing { + id Int @id + seller Int + data Json - creaedAt DateTime @default(now()) - updatedAt DateTime @updatedAt + wantId String @db.Uuid + want Want @relation(fields: [wantId], references: [id], onDelete: Cascade, onUpdate: Cascade) + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + @@index([seller]) +} + +model User { + id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid + discogs String? @unique + data Json + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + wants Want[] +} + +model Want { + id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid + release Int? + data Json + + userId String @db.Uuid + user User @relation(fields: [userId], references: [id], onDelete: Cascade, onUpdate: Cascade) + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + listings Listing[] + + @@unique([userId, release]) + @@index([release]) } diff --git a/src/discogs.ts b/src/discogs.ts index e25fc6e..9619351 100644 --- a/src/discogs.ts +++ b/src/discogs.ts @@ -2,6 +2,7 @@ import fs from 'fs/promises'; import axios from 'axios'; import cheerio from 'cheerio'; +import type { PrismaClient, Want, User, Listing } from '@prisma/client'; import { logAxiosError } from './utils'; import { Pg } from './pg'; @@ -25,17 +26,19 @@ export default function discogs(pg: Pg) { } async function user(discogs: string) { - const user = await pg.userGetName(discogs); + const user = await pg.userFindName(discogs); if (user) return user; console.log('fetching user', discogs); const res = await api.get(`${apiBase}/users/${discogs}`); return pg.userUpsert(res.data); + } async function wantlist(user: User): Promise { const stored = await pg.userWants(user); if (stored.length) return stored; - const { data: { wants, pagination }} = await api.get(`${apiBase}/users/${user.data.username}/wants`); + const { username } = user.data as { username: string }; + const { data: { wants, pagination }} = await api.get(`${apiBase}/users/${username}/wants`); return Promise.all(wants.map(r => pg.wantUpsert(r, user))) } @@ -44,7 +47,7 @@ export default function discogs(pg: Pg) { return res.data.listings; } - async function listings(want: Want): Promise { + async function listings(want: Want) { const res = await api.get(`https://www.discogs.com/sell/release/${want.release}`); const $ = cheerio.load(res.data); const ids = $('.item_description_title[href]') @@ -57,7 +60,7 @@ export default function discogs(pg: Pg) { } async function listing(id, want) { - const listing = await pg.listingGet(id); + const listing = await pg.listingFind(id); if (listing) return listing; try { diff --git a/src/mcrates.d.ts b/src/mcrates.d.ts index 245b455..9f24b2c 100644 --- a/src/mcrates.d.ts +++ b/src/mcrates.d.ts @@ -1,21 +1,21 @@ type UUID = string; -type User = { - id: UUID, - discogs: string, - data: any, -} +// type User = { +// id: UUID, +// discogs: string, +// data: any, +// } -type Listing = { - id: number, - want: UUID, - seller: number, - data: any, -} +// type Listing = { +// id: number, +// want: UUID, +// seller: number, +// data: any, +// } -type Want = { - id: UUID, - release: number, - user: UUID, - data: any, -} +// type Want = { +// id: UUID, +// release: number, +// user: UUID, +// data: any, +// } diff --git a/src/pg.ts b/src/pg.ts index 8b2b41d..13abaa6 100644 --- a/src/pg.ts +++ b/src/pg.ts @@ -1,98 +1,101 @@ import Knex from 'knex'; import config from '../knexfile'; +import type { PrismaClient, Want, User, Listing, Prisma } from '@prisma/client'; export interface Pg { - userGetId: (id: UUID) => Promise, - userGetName: (name: string) => Promise, + userFindId: (id: UUID) => Promise, + userFindName: (name: string) => Promise, userUpsert: (user: User) => Promise, userWants: (user: User) => Promise, wantUpsert: (want: Want, user: User) => Promise, listingUpsert: (listing: Listing, want: Want) => Promise, - listingGet: (id: number) => Promise, + listingFind: (id: number) => Promise, } -export default function createPg(): Pg { - const pg = Knex(config); +export default function createPg(prisma: PrismaClient): Pg { + // const pg = Knex(config); - async function userGetId(id): Promise { - return pg('users') - .where({ id }) - .first(); + async function userFindId(id) { + return prisma.user.findUnique({ where: { id }}); } - async function userGetName(name): Promise { - return pg('users') - .where({ discogs: name }) - .first(); + async function userFindName(name) { + return prisma.user.findUnique({ where: { discogs: name }}); } - async function userWants(user: User): Promise { - return pg('wants') - .where({ user: user.id }); + async function userWants(user: User) { + return prisma.want.findMany({ where: { userId: user.id }}); } - async function userUpsert(user): Promise { - const [row] = await pg('users') - .insert({ - discogs: user.username, - data: user, - }) - .onConflict('id') - .merge() - .onConflict('discogs') - .merge() - .returning('*'); + async function userUpsert(user) { + const data = { + discogs: user.username, + data: user as Prisma.JsonObject, + }; - return row; + return prisma.user.upsert({ + where: { + discogs: user.username, + }, + update: data, + create: data, + }); } - async function wantUpsert(want, user): Promise { - const [row] = await pg('wants') - .insert({ - data: want, - release: want.id, - user: user.id, - }) - .onConflict('id') - .merge() - .onConflict(['user', 'release']) - .merge() - .returning('*'); + async function wantUpsert(dcWant, user) { + const values: { userId: string, release: number, data: any } = { + data: dcWant, + release: dcWant.id, + userId: user.id, + }; - return row; + return prisma.want.upsert({ + // get these from the generated types + // ./node_modules/.prisma/client/index.d.ts + where: { userId_release: { + userId: values.userId, + release: values.release || null, + }}, + update: values, + create: values, + }); } - async function listingUpsert(listing, want): Promise { - const [row] = await pg('listings') - .insert({ - id: listing.id, - want: want.id, - seller: listing.seller.id, - data: listing, - }) - .onConflict('id') - .merge() - .returning('*'); + async function listingUpsert(listing, want) { + const values = { + id: listing.id, + wantId: want.id, + seller: listing.seller.id, + data: listing, + }; - return row; + return prisma.listing.upsert({ + where: { + id: listing.id + }, + update: values, + create: values, + }); } - async function listingGet(id): Promise { - return pg('listings') - .where({ id }) - .first(); + async function listingFind(id) { + return prisma.listing.findUnique({ + where: { + id, + } + }); } return { - userGetId, - userGetName, + userFindId, + userFindName, userUpsert, userWants, wantUpsert, listingUpsert, - listingGet, + listingFind, }; } \ No newline at end of file