From 07d71c96fabcabea25071835817755b610460074 Mon Sep 17 00:00:00 2001 From: Matt Thompson Date: Tue, 19 Oct 2021 13:27:49 -0400 Subject: [PATCH 1/5] init seeds: - Updates types for Profile relation - Adds initial structure for dev/prod seeds --- packages/create-bison-app/.tool-versions | 1 + .../template/graphql/modules/profile.ts | 19 ++++++- .../template/graphql/modules/user.ts.ejs | 3 ++ .../template/package.json.ejs | 4 ++ .../template/prisma/seeds/index.ts | 1 + .../template/prisma/seeds/users/data.ts | 51 +++++++++++++++++++ .../template/prisma/seeds/users/index.ts | 2 + .../prisma/seeds/users/prismaRunner.ts | 32 ++++++++++++ 8 files changed, 112 insertions(+), 1 deletion(-) create mode 100644 packages/create-bison-app/.tool-versions create mode 100644 packages/create-bison-app/template/prisma/seeds/index.ts create mode 100644 packages/create-bison-app/template/prisma/seeds/users/data.ts create mode 100644 packages/create-bison-app/template/prisma/seeds/users/index.ts create mode 100644 packages/create-bison-app/template/prisma/seeds/users/prismaRunner.ts diff --git a/packages/create-bison-app/.tool-versions b/packages/create-bison-app/.tool-versions new file mode 100644 index 00000000..7a49a1a4 --- /dev/null +++ b/packages/create-bison-app/.tool-versions @@ -0,0 +1 @@ +nodejs 16.5.0 diff --git a/packages/create-bison-app/template/graphql/modules/profile.ts b/packages/create-bison-app/template/graphql/modules/profile.ts index 8c0b8f70..9204bbf7 100644 --- a/packages/create-bison-app/template/graphql/modules/profile.ts +++ b/packages/create-bison-app/template/graphql/modules/profile.ts @@ -1,4 +1,4 @@ -import { objectType } from 'nexus'; +import { objectType, inputObjectType } from 'nexus'; // Profile Type export const Profile = objectType({ @@ -29,3 +29,20 @@ export const Profile = objectType({ }); }, }); + +export const ProfileCreateInput = inputObjectType({ + name: 'ProfileCreateInput', + description: 'Profile Input for relational Create', + definition(t) { + t.nonNull.string('firstName'); + t.nonNull.string('lastName'); + }, +}); + +export const ProfileRelationalCreateInput = inputObjectType({ + name: 'ProfileRelationalCreateInput', + description: 'Input to Add a new user', + definition(t) { + t.nonNull.field('create', { type: 'ProfileCreateInput' }); + }, +}); diff --git a/packages/create-bison-app/template/graphql/modules/user.ts.ejs b/packages/create-bison-app/template/graphql/modules/user.ts.ejs index df732bfe..01e9e7bd 100644 --- a/packages/create-bison-app/template/graphql/modules/user.ts.ejs +++ b/packages/create-bison-app/template/graphql/modules/user.ts.ejs @@ -256,5 +256,8 @@ export const UserCreateInput = inputObjectType({ t.field('roles', { type: list('Role'), }); + t.field('profile', { + type: 'ProfileRelationalCreateInput' + }) }, }); diff --git a/packages/create-bison-app/template/package.json.ejs b/packages/create-bison-app/template/package.json.ejs index 5ee56f9b..f91faaa1 100644 --- a/packages/create-bison-app/template/package.json.ejs +++ b/packages/create-bison-app/template/package.json.ejs @@ -19,6 +19,7 @@ "db:deploy": "yarn prisma migrate deploy", "db:reset": "yarn prisma migrate reset", "db:seed": "yarn prisma db seed", + "db:seed:prod": "APP_ENV=production prisma db seed", "db:setup": "yarn db:reset", "dev": "concurrently -n \"WATCHERS,NEXT\" -c \"black.bgYellow.dim,black.bgCyan.dim\" \"yarn watch:all\" \"next dev\"", "dev:typecheck": "tsc --noEmit", @@ -46,6 +47,9 @@ "watch:nexus": "yarn ts-node --transpile-only --respawn --watch graphql/schema.ts,prisma/schema.prisma graphql/schema.ts", "watch:ts": "yarn dev:typecheck --watch" }, + "prisma": { + "seed": "yarn ts-node prisma/seed.ts" + }, "dependencies": { "@apollo/client": "^3.3.21", "@chakra-ui/react": "^1.6.4", diff --git a/packages/create-bison-app/template/prisma/seeds/index.ts b/packages/create-bison-app/template/prisma/seeds/index.ts new file mode 100644 index 00000000..1c07d17e --- /dev/null +++ b/packages/create-bison-app/template/prisma/seeds/index.ts @@ -0,0 +1 @@ +export const thing = 2; diff --git a/packages/create-bison-app/template/prisma/seeds/users/data.ts b/packages/create-bison-app/template/prisma/seeds/users/data.ts new file mode 100644 index 00000000..c917fd8d --- /dev/null +++ b/packages/create-bison-app/template/prisma/seeds/users/data.ts @@ -0,0 +1,51 @@ +import { hashPassword } from '../../../services/auth'; +import { Role, UserCreateInput } from '../../../types'; + +// ********************************************* +// ** DEVELOPMENT DATA SET +// ********************************************* + +const INITIAL_PASSWORD = 'test1234'; + +const initialDevUsers: UserCreateInput[] = [ + { + email: 'ballen@speedforce.net', + password: hashPassword(INITIAL_PASSWORD), + roles: [Role.ADMIN], + profile: { + create: { + firstName: 'Barry', + lastName: 'Allen', + }, + }, + }, +]; + +// ********************************************* +// ** PRODUCTION DATA SET +// ********************************************* + +const INITIAL_PROD_PASSWORD = 'strong@password'; + +const initialProdUsers: UserCreateInput[] = [ + { + email: 'apps@echobind.com', + password: hashPassword(INITIAL_PROD_PASSWORD), + roles: [Role.ADMIN], + profile: { + create: { + firstName: 'EB', + lastName: 'Admin', + }, + }, + }, +]; + +// ********************************************* +// ** MAIN DATA EXPORT +// ********************************************* + +const appEnv = process.env.APP_ENV || 'development'; + +export const userCreateData: UserCreateInput[] = + appEnv === 'production' ? initialProdUsers : initialDevUsers; diff --git a/packages/create-bison-app/template/prisma/seeds/users/index.ts b/packages/create-bison-app/template/prisma/seeds/users/index.ts new file mode 100644 index 00000000..f53a5288 --- /dev/null +++ b/packages/create-bison-app/template/prisma/seeds/users/index.ts @@ -0,0 +1,2 @@ +export * from './data'; +export * from './prismaRunner'; diff --git a/packages/create-bison-app/template/prisma/seeds/users/prismaRunner.ts b/packages/create-bison-app/template/prisma/seeds/users/prismaRunner.ts new file mode 100644 index 00000000..7eb4989f --- /dev/null +++ b/packages/create-bison-app/template/prisma/seeds/users/prismaRunner.ts @@ -0,0 +1,32 @@ +import { PrismaClient } from '@prisma/client'; + +import { User, UserCreateInput } from '../../../types'; + +type SeedUserResult = Pick; + +export const seedUsers = async ( + prisma: PrismaClient, + users: UserCreateInput[] +): Promise => { + const userPromiseArray = users.map( + async (user): Promise => + prisma.user.upsert({ + where: { + email: user.email, + }, + create: { + email: user.email, + password: user.password, + roles: user.roles, + profile: user.profile, + }, + update: {}, + select: { + id: true, + email: true, + }, + }) + ); + + return Promise.all(userPromiseArray); +}; From 6e1f627c37fea1d843507130981dfc004445c2cd Mon Sep 17 00:00:00 2001 From: Matt Thompson Date: Thu, 21 Oct 2021 12:17:57 -0400 Subject: [PATCH 2/5] seeds and scripts: - Adds example for users - Adds Readme - Adds Example Script - Adds Script Runner - updates pacakage json - adds scripts for seed:prod and run:script --- .../create-bison-app/template/_.env.local.ejs | 3 +- .../template/package.json.ejs | 2 + .../template/prisma/SeedsAndScripts.md | 66 +++++++++++++++++++ .../prisma/scripts/exampleUserScript.ts | 54 +++++++++++++++ .../template/prisma/scripts/run.ts | 23 +++++++ .../create-bison-app/template/prisma/seed.ts | 18 ++--- .../template/prisma/seeds/index.ts | 1 - .../template/prisma/seeds/users/data.ts | 4 +- .../prisma/seeds/users/prismaRunner.ts | 8 +-- 9 files changed, 160 insertions(+), 19 deletions(-) create mode 100644 packages/create-bison-app/template/prisma/SeedsAndScripts.md create mode 100644 packages/create-bison-app/template/prisma/scripts/exampleUserScript.ts create mode 100644 packages/create-bison-app/template/prisma/scripts/run.ts delete mode 100644 packages/create-bison-app/template/prisma/seeds/index.ts diff --git a/packages/create-bison-app/template/_.env.local.ejs b/packages/create-bison-app/template/_.env.local.ejs index 06929156..e158dbc4 100644 --- a/packages/create-bison-app/template/_.env.local.ejs +++ b/packages/create-bison-app/template/_.env.local.ejs @@ -1,3 +1,4 @@ # ENV vars here override .env when running locally # DATABASE_URL="postgresql://postgres@localhost:5432/<%= name -%>_dev?schema=public" -APP_SECRET=foo \ No newline at end of file +APP_SECRET=foo +APP_ENV=development \ No newline at end of file diff --git a/packages/create-bison-app/template/package.json.ejs b/packages/create-bison-app/template/package.json.ejs index f91faaa1..c9d76e18 100644 --- a/packages/create-bison-app/template/package.json.ejs +++ b/packages/create-bison-app/template/package.json.ejs @@ -33,6 +33,7 @@ "g:test:request": "hygen test request --name", "g:test:util": "hygen test util --name", "lint": "yarn eslint . --ext .ts,.tsx --fix --ignore-pattern tmp", + "run:script": "yarn ts-node prisma/scripts/run.ts -f" "setup:dev": "yarn db:setup && yarn build:types", "start": "next start -p $PORT", "test": "yarn withEnv:test jest --runInBand --watch", @@ -60,6 +61,7 @@ "@sendgrid/mail": "^7.4.4", "apollo-server-micro": "^3.1.1", "bcryptjs": "^2.4.3", + "commander": "^8.1.0", "cross-fetch": "3.0.5", "framer-motion": "^4", "graphql": "^15.5.0", diff --git a/packages/create-bison-app/template/prisma/SeedsAndScripts.md b/packages/create-bison-app/template/prisma/SeedsAndScripts.md new file mode 100644 index 00000000..bc4ab805 --- /dev/null +++ b/packages/create-bison-app/template/prisma/SeedsAndScripts.md @@ -0,0 +1,66 @@ +# Seeds and Scripts + +## Seeds + +### Dev VS Prod + +- `yarn db:seed` (DEV) +- `yarn db:seed:prod` (PROD) + +We use `APP_ENV` in the scripts to determine the dataset returned for seeds. +This ENV is set in your .env.local file as well, and can be manually set as an ENV in your deploy environment if needed for other scripts. + +### File Breakdown (Seeds) + +```sh +/seeds +--/model +----/data.ts +----/prismaRunner.ts +----/index.ts +``` + +**data.ts** contains the exported function `seedModelData: ModelCreateInput[]` this function leverages APP_ENV to return the dataset expected for Dev vs Prod. In the case of `users` this returns `initialDevUsers: UserCreateInput[]` or `initalProdUsers: UserCreateInput[]`. + +**prismaRunner.ts** this file contains the Prisma `UPSERT` call for the model. We leverage upsert here so that seeds can potentially be ran more than once as your models and data expand over time. + +**index.ts** export of functions for main seed file + +### Relationships + +In the event your model has a relationship dependency ie. Accounts, Organizations, etc. The prismaRunners are set to return a `Pick` result for you to leverage in future seeds. The dependent runner would expand to take these parameters. + +**User/Organization example:** + +```ts + import {orgSeedData, seedOrganizations } from './seeds/organizations'; + import {userSeedData, seedUsers } from './seeds/users'; + + const [{ id: orgId }] = await seedOrganizations(orgSeedData); + await seedUsers(userSeedData(orgId)); +``` + +```ts + const initialDevUsers = (orgId: string): UserCreateInput[] => [ + { + email: 'john.doe@echobind.com', + organization: { connect: { id: orgId } }, + // ...other create data + } + ] +``` + +## Scripts + +### File Breakdown (Scripts) + +```sh +/scripts +--/run.ts +--/exampleUserScript.ts +``` + +**run.ts** This is the main run file leveraged by `yarn run:script {filename}`. +This script takes a file name to be run via `commander`. These scripts can be ANYTHING. However, in our example, we've leveraged the same `seedUsers` runner to add new employees. + +**exampleUserScript.ts** This is an example script file that can be ran with `yarn run:script exampleUserScript.ts`. This script leverages the same `seedUsers` prisma runner to add a few new employees to the team. diff --git a/packages/create-bison-app/template/prisma/scripts/exampleUserScript.ts b/packages/create-bison-app/template/prisma/scripts/exampleUserScript.ts new file mode 100644 index 00000000..ea02808c --- /dev/null +++ b/packages/create-bison-app/template/prisma/scripts/exampleUserScript.ts @@ -0,0 +1,54 @@ +import { hashPassword } from '../../services/auth'; +import { Role, UserCreateInput } from '../../types'; + +import { seedUsers } from '../seeds/users'; + +// HR: Hey, we've had a few more employees join -- can you create an account for them?! + +const INITIAL_PASSWORD = 'test1234'; + +const main = async () => { + const newEmployees: UserCreateInput[] = [ + { + profile: { + create: { + firstName: 'Cisco', + lastName: 'Ramon', + }, + }, + email: 'cisco.ramon@speedforce.net', + password: hashPassword(INITIAL_PASSWORD), + roles: [Role.ADMIN], + }, + { + profile: { + create: { + firstName: 'Caitlin', + lastName: 'Snow', + }, + }, + email: 'caitlin.snow@speedforce.net', + password: hashPassword(INITIAL_PASSWORD), + roles: [Role.ADMIN], + }, + { + profile: { + create: { + firstName: 'Harrison', + lastName: 'Wells', + }, + }, + email: 'harrison.wells@speedforce.net', + password: hashPassword(INITIAL_PASSWORD), + roles: [Role.ADMIN], + }, + ]; + + await seedUsers(newEmployees); +}; + +main() + .catch((e) => console.error(e)) + .finally(async () => { + await prisma.$disconnect(); + }); diff --git a/packages/create-bison-app/template/prisma/scripts/run.ts b/packages/create-bison-app/template/prisma/scripts/run.ts new file mode 100644 index 00000000..d8d5186a --- /dev/null +++ b/packages/create-bison-app/template/prisma/scripts/run.ts @@ -0,0 +1,23 @@ +import { Command } from 'commander'; +import { spawn } from 'child_process'; + +const program = new Command(); +const run = () => { + program.option('-f, --file ', 'filename of script to run'); + + program.parse(process.argv); + + const { file } = program.opts(); + console.log({ file }); + + const child = spawn(`yarn ts-node ${__dirname}/${file}`, { + shell: true, + stdio: 'inherit', + }); + + child.on('exit', function (code) { + process.exit(code); + }); +}; + +run(); diff --git a/packages/create-bison-app/template/prisma/seed.ts b/packages/create-bison-app/template/prisma/seed.ts index 72f99a67..837da0f4 100644 --- a/packages/create-bison-app/template/prisma/seed.ts +++ b/packages/create-bison-app/template/prisma/seed.ts @@ -1,10 +1,10 @@ -// import { UserFactory } from '../tests/factories'; -// import { Role } from '../types'; -// import { prisma } from '../lib/prisma'; +import { userSeedData, seedUsers } from './seeds/users'; -export async function seed() { - // Add seeds here. You can use factories or raw prisma.create/upsert calls. - console.log('no seeds yet!'); - // await UserFactory.create({ email: 'hello@wee.net' }); - // await UserFactory.create({ roles: [Role.ADMIN] }); -} +const seed = async () => { + console.log('seeding Users...'); + await seedUsers(userSeedData); +}; + +seed() + .catch((e) => console.error(e)) + .finally(() => console.log('Seeding Complete')); diff --git a/packages/create-bison-app/template/prisma/seeds/index.ts b/packages/create-bison-app/template/prisma/seeds/index.ts deleted file mode 100644 index 1c07d17e..00000000 --- a/packages/create-bison-app/template/prisma/seeds/index.ts +++ /dev/null @@ -1 +0,0 @@ -export const thing = 2; diff --git a/packages/create-bison-app/template/prisma/seeds/users/data.ts b/packages/create-bison-app/template/prisma/seeds/users/data.ts index c917fd8d..76dfcab7 100644 --- a/packages/create-bison-app/template/prisma/seeds/users/data.ts +++ b/packages/create-bison-app/template/prisma/seeds/users/data.ts @@ -9,7 +9,7 @@ const INITIAL_PASSWORD = 'test1234'; const initialDevUsers: UserCreateInput[] = [ { - email: 'ballen@speedforce.net', + email: 'barry.allen@speedforce.net', password: hashPassword(INITIAL_PASSWORD), roles: [Role.ADMIN], profile: { @@ -47,5 +47,5 @@ const initialProdUsers: UserCreateInput[] = [ const appEnv = process.env.APP_ENV || 'development'; -export const userCreateData: UserCreateInput[] = +export const userSeedData: UserCreateInput[] = appEnv === 'production' ? initialProdUsers : initialDevUsers; diff --git a/packages/create-bison-app/template/prisma/seeds/users/prismaRunner.ts b/packages/create-bison-app/template/prisma/seeds/users/prismaRunner.ts index 7eb4989f..6187ecd9 100644 --- a/packages/create-bison-app/template/prisma/seeds/users/prismaRunner.ts +++ b/packages/create-bison-app/template/prisma/seeds/users/prismaRunner.ts @@ -1,13 +1,9 @@ -import { PrismaClient } from '@prisma/client'; - import { User, UserCreateInput } from '../../../types'; +import { prisma } from '../../../lib/prisma'; type SeedUserResult = Pick; -export const seedUsers = async ( - prisma: PrismaClient, - users: UserCreateInput[] -): Promise => { +export const seedUsers = async (users: UserCreateInput[]): Promise => { const userPromiseArray = users.map( async (user): Promise => prisma.user.upsert({ From c65de04a5d545efac8f61c4456cf8fef4d152ab3 Mon Sep 17 00:00:00 2001 From: Matt Thompson Date: Thu, 21 Oct 2021 12:25:22 -0400 Subject: [PATCH 3/5] lint --- packages/create-bison-app/template/_.env.local.ejs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/create-bison-app/template/_.env.local.ejs b/packages/create-bison-app/template/_.env.local.ejs index e158dbc4..537e1ee0 100644 --- a/packages/create-bison-app/template/_.env.local.ejs +++ b/packages/create-bison-app/template/_.env.local.ejs @@ -1,4 +1,4 @@ # ENV vars here override .env when running locally # DATABASE_URL="postgresql://postgres@localhost:5432/<%= name -%>_dev?schema=public" APP_SECRET=foo -APP_ENV=development \ No newline at end of file +APP_ENV=development From 093056c10ba41f14bec4ceea03c2994313742ddd Mon Sep 17 00:00:00 2001 From: Matt Thompson Date: Thu, 21 Oct 2021 12:29:42 -0400 Subject: [PATCH 4/5] missing , --- packages/create-bison-app/template/package.json.ejs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/create-bison-app/template/package.json.ejs b/packages/create-bison-app/template/package.json.ejs index c9d76e18..8a5e017e 100644 --- a/packages/create-bison-app/template/package.json.ejs +++ b/packages/create-bison-app/template/package.json.ejs @@ -33,7 +33,7 @@ "g:test:request": "hygen test request --name", "g:test:util": "hygen test util --name", "lint": "yarn eslint . --ext .ts,.tsx --fix --ignore-pattern tmp", - "run:script": "yarn ts-node prisma/scripts/run.ts -f" + "run:script": "yarn ts-node prisma/scripts/run.ts -f", "setup:dev": "yarn db:setup && yarn build:types", "start": "next start -p $PORT", "test": "yarn withEnv:test jest --runInBand --watch", From 44d1f63f81c6fa1050a0a88052aea628cc36b9ae Mon Sep 17 00:00:00 2001 From: Matt Thompson Date: Fri, 22 Oct 2021 13:37:03 -0400 Subject: [PATCH 5/5] pr feedback --- packages/create-bison-app/template/package.json.ejs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/create-bison-app/template/package.json.ejs b/packages/create-bison-app/template/package.json.ejs index 8a5e017e..f4c8091e 100644 --- a/packages/create-bison-app/template/package.json.ejs +++ b/packages/create-bison-app/template/package.json.ejs @@ -19,7 +19,7 @@ "db:deploy": "yarn prisma migrate deploy", "db:reset": "yarn prisma migrate reset", "db:seed": "yarn prisma db seed", - "db:seed:prod": "APP_ENV=production prisma db seed", + "db:seed:prod": "cross-env APP_ENV=production prisma db seed", "db:setup": "yarn db:reset", "dev": "concurrently -n \"WATCHERS,NEXT\" -c \"black.bgYellow.dim,black.bgCyan.dim\" \"yarn watch:all\" \"next dev\"", "dev:typecheck": "tsc --noEmit", @@ -48,9 +48,6 @@ "watch:nexus": "yarn ts-node --transpile-only --respawn --watch graphql/schema.ts,prisma/schema.prisma graphql/schema.ts", "watch:ts": "yarn dev:typecheck --watch" }, - "prisma": { - "seed": "yarn ts-node prisma/seed.ts" - }, "dependencies": { "@apollo/client": "^3.3.21", "@chakra-ui/react": "^1.6.4",