Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add projects submit solution api [#118] #125

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
2 changes: 2 additions & 0 deletions src/server/api/routers/edge/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { exampleEdgeRouter } from '~/server/api/routers/edge/example';
import { submitSolutionEdgeRouter } from '~/server/api/routers/edge/submitSolution';

export const edgeRouters = {
example: exampleEdgeRouter,
solution: submitSolutionEdgeRouter,
};
85 changes: 85 additions & 0 deletions src/server/api/routers/edge/submitSolution.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { z } from 'zod';
import { db } from '~/server/database';
import { createTRPCRouter, publicProcedure } from '~/server/utils/trpc';

export const submitSolutionEdgeRouter = createTRPCRouter({
submitSolution: publicProcedure
.input(
z.object({
solutionMaskId: z.string(),
title: z.string(),
repositoryUrl: z.string(),
liveUrl: z.string(),
questions: z.string(),
tags: z.array(
z.object({
id: z.number().optional(),
tagMaskId: z.string(),
title: z.string(),
}),
),
}),
)
.mutation(async ({ input }) => {
await db.query.insert(db.models.solution).values({
solutionMaskId: input.solutionMaskId,
title: input.title,
repositoryUrl: input.repositoryUrl,
liveUrl: input.liveUrl,
questions: input.questions,
});
const solutionIdResponse = await db.query
.select({
id: db.models.solution.id,
})
.from(db.models.solution)
.where(
db.exp.eq(db.models.solution.solutionMaskId, input.solutionMaskId),
);

const solutionId = solutionIdResponse[0]?.id;

const { tags } = input;
console.log('tags: ', tags);

try {
await Promise.all(
tags.map(async (tag): Promise<any> => {
if (tag.id !== undefined && solutionId !== undefined) {
await db.query.insert(db.models.solutionTag).values({
solutionId: solutionId,
tagId: tag.id,
});
} else {
await db.query.insert(db.models.tag).values({
tagMaskId: tag.tagMaskId,
title: tag.title,
});

const tagIdResponse = await db.query
.select({
id: db.models.tag.id,
})
.from(db.models.tag)
.where(db.exp.eq(db.models.tag.tagMaskId, tag.tagMaskId));

const tagId = tagIdResponse[0]?.id;

if (tagId !== undefined && solutionId !== undefined) {
await db.query.insert(db.models.solutionTag).values({
solutionId: solutionId,
tagId: tagId,
});
}
}
}),
);
} catch (error) {
console.error('Error inserting tags: ', error);
}
}),

getAllTags: publicProcedure.query(async () => {
return await db.query.select().from(db.models.tag);
}),
});
5 changes: 4 additions & 1 deletion src/server/database/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { connect } from '@planetscale/database';
import * as expressions from 'drizzle-orm/expressions';
import { drizzle } from 'drizzle-orm/planetscale-serverless';

import { schemaMigration } from './schema';
import { schemaMigration, solution, solutionTag, tag } from './schema';

// create the connection
const connection = connect({
Expand All @@ -17,6 +17,9 @@ const db = {
query: drizzleClient,
models: {
schemaMigration,
tag,
solution,
solutionTag,
},
exp: expressions,
};
Expand Down
14 changes: 14 additions & 0 deletions src/server/database/migrations/20230419065727_add_tag_table.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
-- migrate:up
CREATE TABLE tag (
id SERIAL PRIMARY KEY,
tagMaskId VARCHAR(30) NOT NULL,
title VARCHAR(255) NOT NULL,
createdAt DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
updatedAt DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

CREATE UNIQUE INDEX idx_uni_tagMaskId ON tag (tagMaskId);

-- migrate:down
DROP INDEX idx_uni_tagMaskId ON tag;
DROP TABLE IF EXISTS tag;
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
-- migrate:up
CREATE TABLE solution (
id SERIAL PRIMARY KEY,
solutionMaskId VARCHAR(30) NOT NULL,
title VARCHAR(255) NOT NULL,
repositoryUrl VARCHAR(255) NOT NULL,
liveUrl VARCHAR(255) NOT NULL,
questions TEXT NOT NULL,
createdAt DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
updatedAt DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

CREATE UNIQUE INDEX idx_uni_solutionMaskId ON solution (solutionMaskId);

-- migrate:down
DROP INDEX idx_uni_solutionMaskId ON solution;
DROP TABLE IF EXISTS solution;


Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
-- migrate:up
CREATE TABLE solutionTag (
id SERIAL PRIMARY KEY,
solutionId INT NOT NULL,
tagId INT NOT NULL,
createdAt DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
updatedAt DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

CREATE INDEX idx_solutionId ON solutionTag (solutionId);
CREATE INDEX idx_tagId ON solutionTag (tagId);

-- migrate:down
DROP INDEX IF EXISTS idx_solutionId ON solutionTag;
DROP INDEX IF EXISTS idx_tagId ON solutionTag;
DROP TABLE IF EXISTS solutionTag;
178 changes: 177 additions & 1 deletion src/server/database/schema.generated.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,185 @@
import { datetime, mysqlTable, varchar } from 'drizzle-orm/mysql-core';
import {
datetime,
index,
int,
mysqlTable,
serial,
text,
uniqueIndex,
varchar,
} from 'drizzle-orm/mysql-core';
import { sql } from 'drizzle-orm/sql';

export const account = mysqlTable(
'account',
{
id: serial('id').primaryKey().notNull(),
userId: varchar('userId', { length: 255 }).notNull(),
type: varchar('type', { length: 255 }).notNull(),
provider: varchar('provider', { length: 255 }).notNull(),
providerAccountId: varchar('providerAccountId', { length: 255 }).notNull(),
accessToken: text('accessToken'),
expiresIn: int('expiresIn'),
idToken: text('idToken'),
refreshToken: text('refreshToken'),
refreshTokenExpiresIn: int('refreshTokenExpiresIn'),
scope: varchar('scope', { length: 255 }),
tokenType: varchar('tokenType', { length: 255 }),
createdAt: datetime('createdAt', { mode: 'string' })
.default(sql`(CURRENT_TIMESTAMP)`)
.notNull(),
updatedAt: datetime('updatedAt', { mode: 'string' })
.default('CURRENT_TIMESTAMP')
.notNull(),
},
(table) => {
return {
idxUniProviderProviderAccountId: uniqueIndex(
'idx_uni_provider_providerAccountId',
).on(table.provider, table.providerAccountId),
idxUserId: index('idx_userId').on(table.userId),
};
},
);

export const profileUser = mysqlTable(
'profileUser',
{
id: serial('id').primaryKey().notNull(),
userId: varchar('userId', { length: 255 }).notNull(),
name: varchar('name', { length: 255 }),
email: varchar('email', { length: 255 }).notNull(),
emailVerified: datetime('emailVerified', { mode: 'string' }),
image: varchar('image', { length: 255 }),
createdAt: datetime('createdAt', { mode: 'string' })
.default(sql`(CURRENT_TIMESTAMP)`)
.notNull(),
updatedUt: datetime('updatedUt', { mode: 'string' })
.default('CURRENT_TIMESTAMP')
.notNull(),
},
(table) => {
return {
idxUniUserId: uniqueIndex('idx_uni_userId').on(table.userId),
idxUniEmail: uniqueIndex('idx_uni_email').on(table.email),
};
},
);

export const schemaMigration = mysqlTable('schemaMigration', {
version: varchar('version', { length: 128 }).primaryKey().notNull(),
createdAt: datetime('createdAt', { mode: 'string' })
.default(sql`(CURRENT_TIMESTAMP)`)
.notNull(),
});

export const session = mysqlTable(
'session',
{
id: serial('id').primaryKey().notNull(),
sessionToken: varchar('sessionToken', { length: 255 }).notNull(),
userId: varchar('userId', { length: 255 }).notNull(),
expires: datetime('expires', { mode: 'string' }).notNull(),
createdAt: datetime('createdAt', { mode: 'string' })
.default(sql`(CURRENT_TIMESTAMP)`)
.notNull(),
updatedAt: datetime('updatedAt', { mode: 'string' })
.default('CURRENT_TIMESTAMP')
.notNull(),
},
(table) => {
return {
idxUniSessionToken: uniqueIndex('idx_uni_sessionToken').on(
table.sessionToken,
),
idxUserId: index('idx_userId').on(table.userId),
};
},
);

export const solution = mysqlTable(
'solution',
{
id: serial('id').primaryKey().notNull(),
solutionMaskId: varchar('solutionMaskId', { length: 30 }).notNull(),
title: varchar('title', { length: 255 }).notNull(),
repositoryUrl: varchar('repositoryUrl', { length: 255 }).notNull(),
liveUrl: varchar('liveUrl', { length: 255 }).notNull(),
questions: text('questions').notNull(),
createdAt: datetime('createdAt', { mode: 'string' })
.default(sql`(CURRENT_TIMESTAMP)`)
.notNull(),
updatedAt: datetime('updatedAt', { mode: 'string' })
.default('CURRENT_TIMESTAMP')
.notNull(),
},
(table) => {
return {
idxUniSolutionMaskId: uniqueIndex('idx_uni_solutionMaskId').on(
table.solutionMaskId,
),
};
},
);

export const solutionTag = mysqlTable(
'solutionTag',
{
id: serial('id').primaryKey().notNull(),
solutionId: int('solutionId').notNull(),
tagId: int('tagId').notNull(),
createdAt: datetime('createdAt', { mode: 'string' })
.default(sql`(CURRENT_TIMESTAMP)`)
.notNull(),
updatedAt: datetime('updatedAt', { mode: 'string' })
.default('CURRENT_TIMESTAMP')
.notNull(),
},
(table) => {
return {
idxSolutionId: index('idx_solutionId').on(table.solutionId),
idxTagId: index('idx_tagId').on(table.tagId),
};
},
);

export const tag = mysqlTable(
'tag',
{
id: serial('id').primaryKey().notNull(),
tagMaskId: varchar('tagMaskId', { length: 30 }).notNull(),
title: varchar('title', { length: 255 }).notNull(),
createdAt: datetime('createdAt', { mode: 'string' })
.default(sql`(CURRENT_TIMESTAMP)`)
.notNull(),
updatedAt: datetime('updatedAt', { mode: 'string' })
.default('CURRENT_TIMESTAMP')
.notNull(),
},
(table) => {
return {
idxUniTagMaskId: uniqueIndex('idx_uni_tagMaskId').on(table.tagMaskId),
};
},
);

export const verificationToken = mysqlTable(
'verificationToken',
{
id: serial('id').primaryKey().notNull(),
identifier: varchar('identifier', { length: 255 }).notNull(),
token: varchar('token', { length: 255 }).notNull(),
expires: datetime('expires', { mode: 'string' }).notNull(),
createdAt: datetime('createdAt', { mode: 'string' })
.default('CURRENT_TIMESTAMP')
.notNull(),
updatedAt: datetime('updatedAt', { mode: 'string' })
.default('CURRENT_TIMESTAMP')
.notNull(),
},
(table) => {
return {
idxUniToken: uniqueIndex('idx_uni_token').on(table.token),
};
},
);
Loading