Create project schema and scaffold + some minor changes

This commit is contained in:
Ahmed Al-Taiar
2024-08-23 20:51:35 -04:00
parent bb4b5708e1
commit 3d031ca73c
38 changed files with 952 additions and 64 deletions

View File

@ -0,0 +1,49 @@
-- CreateTable
CREATE TABLE "Tag" (
"id" SERIAL NOT NULL,
"tag" TEXT NOT NULL,
"color" TEXT NOT NULL,
CONSTRAINT "Tag_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "ProjectImage" (
"id" SERIAL NOT NULL,
"fileId" TEXT NOT NULL,
"projectId" INTEGER,
CONSTRAINT "ProjectImage_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "Project" (
"id" SERIAL NOT NULL,
"title" TEXT NOT NULL,
"description" TEXT NOT NULL DEFAULT 'No description provided',
"date" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"links" TEXT[] DEFAULT ARRAY[]::TEXT[],
CONSTRAINT "Project_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "_ProjectToTag" (
"A" INTEGER NOT NULL,
"B" INTEGER NOT NULL
);
-- CreateIndex
CREATE UNIQUE INDEX "_ProjectToTag_AB_unique" ON "_ProjectToTag"("A", "B");
-- CreateIndex
CREATE INDEX "_ProjectToTag_B_index" ON "_ProjectToTag"("B");
-- AddForeignKey
ALTER TABLE "ProjectImage" ADD CONSTRAINT "ProjectImage_projectId_fkey" FOREIGN KEY ("projectId") REFERENCES "Project"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "_ProjectToTag" ADD CONSTRAINT "_ProjectToTag_A_fkey" FOREIGN KEY ("A") REFERENCES "Project"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "_ProjectToTag" ADD CONSTRAINT "_ProjectToTag_B_fkey" FOREIGN KEY ("B") REFERENCES "Tag"("id") ON DELETE CASCADE ON UPDATE CASCADE;

View File

@ -48,3 +48,28 @@ model Portrait {
id Int @id @default(autoincrement())
fileId String
}
model Tag {
id Int @id @default(autoincrement())
tag String
color String
projects Project[]
}
model ProjectImage {
id Int @id @default(autoincrement())
fileId String
Project Project? @relation(fields: [projectId], references: [id])
projectId Int?
}
model Project {
id Int @id @default(autoincrement())
title String
description String @default("No description provided")
images ProjectImage[]
date DateTime @default(now())
links String[] @default([])
tags Tag[]
}

View File

@ -11,6 +11,7 @@
"@redwoodjs/graphql-server": "7.7.4",
"@tus/file-store": "^1.4.0",
"@tus/server": "^1.7.0",
"graphql-scalars": "^1.23.0",
"nodemailer": "^6.9.14"
},
"devDependencies": {

View File

@ -2,6 +2,9 @@
"globals": {
"context": {
"writable": false
},
"gql": {
"writable": false
}
}
}

View File

@ -1,18 +0,0 @@
import { mockRedwoodDirective, getDirectiveName } from '@redwoodjs/testing/api'
import requireAuth from './requireAuth'
describe('requireAuth directive', () => {
it('declares the directive sdl as schema, with the correct name', () => {
expect(requireAuth.schema).toBeTruthy()
expect(getDirectiveName(requireAuth.schema)).toBe('requireAuth')
})
it('requireAuth has stub implementation. Should not throw when current user', () => {
// If you want to set values in context, pass it through e.g.
// mockRedwoodDirective(requireAuth, { context: { currentUser: { id: 1, name: 'Lebron McGretzky' } }})
const mockExecution = mockRedwoodDirective(requireAuth, { context: {} })
expect(mockExecution).not.toThrowError()
})
})

View File

@ -1,10 +0,0 @@
import { getDirectiveName } from '@redwoodjs/testing/api'
import skipAuth from './skipAuth'
describe('skipAuth directive', () => {
it('declares the directive sdl as schema, with the correct name', () => {
expect(skipAuth.schema).toBeTruthy()
expect(getDirectiveName(skipAuth.schema)).toBe('skipAuth')
})
})

View File

@ -1,3 +1,5 @@
import { URLTypeDefinition, URLResolver } from 'graphql-scalars'
import { createAuthDecoder } from '@redwoodjs/auth-dbauth-api'
import { createGraphQLHandler } from '@redwoodjs/graphql-server'
@ -18,6 +20,12 @@ export const handler = createGraphQLHandler({
directives,
sdls,
services,
schemaOptions: {
typeDefs: [URLTypeDefinition],
resolvers: {
URL: URLResolver,
},
},
onException: () => {
// Disconnect from your database with an unhandled exception.
db.$disconnect()

View File

@ -1,7 +1,7 @@
export const schema = gql`
type Portrait {
id: Int!
fileId: String!
fileId: URL!
}
type Query {
@ -9,7 +9,7 @@ export const schema = gql`
}
input CreatePortraitInput {
fileId: String!
fileId: URL!
}
type Mutation {

View File

@ -0,0 +1,33 @@
export const schema = gql`
type ProjectImage {
id: Int!
fileId: URL!
Project: Project
projectId: Int
}
type Query {
projectImages: [ProjectImage!]! @requireAuth
projectImage(id: Int!): ProjectImage @requireAuth
}
input CreateProjectImageInput {
fileId: URL!
projectId: Int
}
input UpdateProjectImageInput {
fileId: URL
projectId: Int
}
type Mutation {
createProjectImage(input: CreateProjectImageInput!): ProjectImage!
@requireAuth
updateProjectImage(
id: Int!
input: UpdateProjectImageInput!
): ProjectImage! @requireAuth
deleteProjectImage(id: Int!): ProjectImage! @requireAuth
}
`

View File

@ -0,0 +1,36 @@
export const schema = gql`
type Project {
id: Int!
title: String!
description: String!
images: [ProjectImage]!
date: DateTime!
links: [String]!
tags: [Tag]!
}
type Query {
projects: [Project!]! @requireAuth
project(id: Int!): Project @requireAuth
}
input CreateProjectInput {
title: String!
description: String!
date: DateTime!
links: [String]!
}
input UpdateProjectInput {
title: String
description: String
date: DateTime
links: [String]!
}
type Mutation {
createProject(input: CreateProjectInput!): Project! @requireAuth
updateProject(id: Int!, input: UpdateProjectInput!): Project! @requireAuth
deleteProject(id: Int!): Project! @requireAuth
}
`

View File

@ -0,0 +1,3 @@
export const schema = gql`
scalar URL
`

View File

@ -0,0 +1,29 @@
export const schema = gql`
type Tag {
id: Int!
tag: String!
color: String!
projects: [Project]!
}
type Query {
tags: [Tag!]! @requireAuth
tag(id: Int!): Tag @requireAuth
}
input CreateTagInput {
tag: String!
color: String!
}
input UpdateTagInput {
tag: String
color: String
}
type Mutation {
createTag(input: CreateTagInput!): Tag! @requireAuth
updateTag(id: Int!, input: UpdateTagInput!): Tag! @requireAuth
deleteTag(id: Int!): Tag! @requireAuth
}
`

View File

@ -6,7 +6,7 @@ import {
ValidationError,
} from '@redwoodjs/graphql-server'
import { db } from './db'
import { db } from 'src/lib/db'
/**
* The name of the cookie that dbAuth sets

View File

@ -5,7 +5,7 @@ import { PrismaClient } from '@prisma/client'
import { emitLogLevels, handlePrismaLogging } from '@redwoodjs/api/logger'
import { logger } from './logger'
import { logger } from 'src/lib/logger'
/*
* Instance of the Prisma Client

View File

@ -1,9 +1,14 @@
import type { QueryResolvers, MutationResolvers } from 'types/graphql'
import { isProduction } from '@redwoodjs/api/dist/logger'
import { ValidationError } from '@redwoodjs/graphql-server'
import { db } from 'src/lib/db'
const address = isProduction
? process.env.ADDRESS_PROD
: process.env.ADDRESS_DEV
export const portrait: QueryResolvers['portrait'] = async () => {
const portrait = await db.portrait.findFirst()
@ -11,7 +16,7 @@ export const portrait: QueryResolvers['portrait'] = async () => {
else
return {
id: -1,
fileId: '/no_portrait.webp',
fileId: `${address}/no_portrait.webp`,
}
}

View File

@ -0,0 +1,49 @@
import type {
QueryResolvers,
MutationResolvers,
ProjectImageRelationResolvers,
} from 'types/graphql'
import { db } from 'src/lib/db'
export const projectImages: QueryResolvers['projectImages'] = () => {
return db.projectImage.findMany()
}
export const projectImage: QueryResolvers['projectImage'] = ({ id }) => {
return db.projectImage.findUnique({
where: { id },
})
}
export const createProjectImage: MutationResolvers['createProjectImage'] = ({
input,
}) => {
return db.projectImage.create({
data: input,
})
}
export const updateProjectImage: MutationResolvers['updateProjectImage'] = ({
id,
input,
}) => {
return db.projectImage.update({
data: input,
where: { id },
})
}
export const deleteProjectImage: MutationResolvers['deleteProjectImage'] = ({
id,
}) => {
return db.projectImage.delete({
where: { id },
})
}
export const ProjectImage: ProjectImageRelationResolvers = {
Project: (_obj, { root }) => {
return db.projectImage.findUnique({ where: { id: root?.id } }).Project()
},
}

View File

@ -0,0 +1,50 @@
import type {
QueryResolvers,
MutationResolvers,
ProjectRelationResolvers,
} from 'types/graphql'
import { db } from 'src/lib/db'
export const projects: QueryResolvers['projects'] = () => {
return db.project.findMany()
}
export const project: QueryResolvers['project'] = ({ id }) => {
return db.project.findUnique({
where: { id },
})
}
export const createProject: MutationResolvers['createProject'] = ({
input,
}) => {
return db.project.create({
data: input,
})
}
export const updateProject: MutationResolvers['updateProject'] = ({
id,
input,
}) => {
return db.project.update({
data: input,
where: { id },
})
}
export const deleteProject: MutationResolvers['deleteProject'] = ({ id }) => {
return db.project.delete({
where: { id },
})
}
export const Project: ProjectRelationResolvers = {
images: (_obj, { root }) => {
return db.project.findUnique({ where: { id: root?.id } }).images()
},
tags: (_obj, { root }) => {
return db.project.findUnique({ where: { id: root?.id } }).tags()
},
}

View File

@ -0,0 +1,42 @@
import type {
QueryResolvers,
MutationResolvers,
TagRelationResolvers,
} from 'types/graphql'
import { db } from 'src/lib/db'
export const tags: QueryResolvers['tags'] = () => {
return db.tag.findMany()
}
export const tag: QueryResolvers['tag'] = ({ id }) => {
return db.tag.findUnique({
where: { id },
})
}
export const createTag: MutationResolvers['createTag'] = ({ input }) => {
return db.tag.create({
data: input,
})
}
export const updateTag: MutationResolvers['updateTag'] = ({ id, input }) => {
return db.tag.update({
data: input,
where: { id },
})
}
export const deleteTag: MutationResolvers['deleteTag'] = ({ id }) => {
return db.tag.delete({
where: { id },
})
}
export const Tag: TagRelationResolvers = {
projects: (_obj, { root }) => {
return db.tag.findUnique({ where: { id: root?.id } }).projects()
},
}