commit 6b69da29adc6d62cc2220467e6ce064599292e8e
parent 154c3289366c2762fb80039d81dd840fdf2b2176
Author: Yuukin256 <Yuukin256@gmail.com>
Date: Fri, 4 Nov 2022 00:14:15 +0900
classes api と classes/[classId]/standard api を実装
Diffstat:
7 files changed, 449 insertions(+), 0 deletions(-)
diff --git a/src/api/$api.ts b/src/api/$api.ts
@@ -0,0 +1,41 @@
+import type { AspidaClient } from 'aspida'
+import type { Methods as Methods0 } from './classes'
+import type { Methods as Methods1 } from './classes/_classId@number/standard'
+
+const api = <T>({ baseURL, fetch }: AspidaClient<T>) => {
+ const prefix = (baseURL === undefined ? '' : baseURL).replace(/\/$/, '')
+ const PATH0 = '/classes'
+ const PATH1 = '/standard'
+ const GET = 'GET'
+ const POST = 'POST'
+
+ return {
+ classes: {
+ _classId: (val1: number) => {
+ const prefix1 = `${PATH0}/${val1}`
+
+ return {
+ standard: {
+ get: (option?: { config?: T | undefined } | undefined) =>
+ fetch<Methods1['get']['resBody']>(prefix, `${prefix1}${PATH1}`, GET, option).json(),
+ $get: (option?: { config?: T | undefined } | undefined) =>
+ fetch<Methods1['get']['resBody']>(prefix, `${prefix1}${PATH1}`, GET, option).json().then(r => r.body),
+ post: (option: { body: Methods1['post']['reqBody'], config?: T | undefined }) =>
+ fetch<Methods1['post']['resBody']>(prefix, `${prefix1}${PATH1}`, POST, option).json(),
+ $post: (option: { body: Methods1['post']['reqBody'], config?: T | undefined }) =>
+ fetch<Methods1['post']['resBody']>(prefix, `${prefix1}${PATH1}`, POST, option).json().then(r => r.body),
+ $path: () => `${prefix}${prefix1}${PATH1}`
+ }
+ }
+ },
+ get: (option?: { config?: T | undefined } | undefined) =>
+ fetch<Methods0['get']['resBody']>(prefix, PATH0, GET, option).json(),
+ $get: (option?: { config?: T | undefined } | undefined) =>
+ fetch<Methods0['get']['resBody']>(prefix, PATH0, GET, option).json().then(r => r.body),
+ $path: () => `${prefix}${PATH0}`
+ }
+ }
+}
+
+export type ApiInstance = ReturnType<typeof api>
+export default api
diff --git a/src/api/classes/_classId@number/standard.ts b/src/api/classes/_classId@number/standard.ts
@@ -0,0 +1,11 @@
+import type { DefineMethods } from 'aspida';
+import type { PostApiRequestBody } from 'pages/api/rest/v1/classes/[classId]/standard/api-types';
+import type { StandardEn } from 'types';
+
+export type Methods = DefineMethods<{
+ get: { resBody: StandardEn };
+ post: {
+ reqBody: PostApiRequestBody;
+ resBody: StandardEn;
+ };
+}>;
diff --git a/src/api/classes/index.ts b/src/api/classes/index.ts
@@ -0,0 +1,8 @@
+import type { SchoolClass } from '@prisma/client';
+import type { DefineMethods } from 'aspida';
+
+export type Methods = DefineMethods<{
+ get: {
+ resBody: SchoolClass[];
+ };
+}>;
diff --git a/src/pages/api/rest/v1/classes/[classId]/standard/api-types.ts b/src/pages/api/rest/v1/classes/[classId]/standard/api-types.ts
@@ -0,0 +1,3 @@
+import type { StandardEn } from 'types';
+
+export type PostApiRequestBody = StandardEn;
diff --git a/src/pages/api/rest/v1/classes/[classId]/standard/api-types.validator.ts b/src/pages/api/rest/v1/classes/[classId]/standard/api-types.validator.ts
@@ -0,0 +1,282 @@
+// @ts-nocheck
+// eslint-disable
+// This file is generated by create-validator-ts
+import Ajv from 'ajv';
+import * as apiTypes from './api-types';
+
+export const SCHEMA = {
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "$ref": "#/definitions/PostApiRequestBody",
+ "definitions": {
+ "PostApiRequestBody": {
+ "$ref": "#/definitions/StandardEn"
+ },
+ "StandardEn": {
+ "type": "object",
+ "properties": {
+ "Mon1": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "Mon2": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "Mon3": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "Mon4": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "Mon5": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "Mon6": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "Mon7": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "Tue1": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "Tue2": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "Tue3": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "Tue4": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "Tue5": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "Tue6": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "Tue7": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "Wed1": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "Wed2": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "Wed3": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "Wed4": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "Wed5": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "Wed6": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "Wed7": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "Thu1": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "Thu2": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "Thu3": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "Thu4": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "Thu5": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "Thu6": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "Fri1": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "Fri2": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "Fri3": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "Fri4": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "Fri5": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "Fri6": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "Fri7": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ },
+ "required": [
+ "Mon1",
+ "Mon2",
+ "Mon3",
+ "Mon4",
+ "Mon5",
+ "Mon6",
+ "Mon7",
+ "Tue1",
+ "Tue2",
+ "Tue3",
+ "Tue4",
+ "Tue5",
+ "Tue6",
+ "Tue7",
+ "Wed1",
+ "Wed2",
+ "Wed3",
+ "Wed4",
+ "Wed5",
+ "Wed6",
+ "Wed7",
+ "Thu1",
+ "Thu2",
+ "Thu3",
+ "Thu4",
+ "Thu5",
+ "Thu6",
+ "Fri1",
+ "Fri2",
+ "Fri3",
+ "Fri4",
+ "Fri5",
+ "Fri6",
+ "Fri7"
+ ],
+ "additionalProperties": false
+ }
+ }
+};
+const ajv = new Ajv({ removeAdditional: true }).addSchema(SCHEMA, "SCHEMA");
+export function validatePostApiRequestBody(payload: unknown): apiTypes.PostApiRequestBody {
+ /** Schema is defined in {@link SCHEMA.definitions.PostApiRequestBody } **/
+ const validator = ajv.getSchema("SCHEMA#/definitions/PostApiRequestBody");
+ const valid = validator(payload);
+ if (!valid) {
+ const error = new Error('Invalid PostApiRequestBody: ' + ajv.errorsText(validator.errors, {dataVar: "PostApiRequestBody"}));
+ error.name = "ValidationError";
+ throw error;
+ }
+ return payload;
+}
+
+export function isPostApiRequestBody(payload: unknown): payload is apiTypes.PostApiRequestBody {
+ try {
+ validatePostApiRequestBody(payload);
+ return true;
+ } catch (error) {
+ return false;
+ }
+}
diff --git a/src/pages/api/rest/v1/classes/[classId]/standard/index.ts b/src/pages/api/rest/v1/classes/[classId]/standard/index.ts
@@ -0,0 +1,83 @@
+import { List } from 'immutable';
+
+import { ABSTRACT_LESSON_NAMES } from 'constant';
+import { prisma } from 'lib/prisma';
+
+import { isPostApiRequestBody } from './api-types.validator';
+
+import type { ClassUnit } from '@prisma/client';
+import type { NextApiHandler } from 'next';
+import type { StandardEn } from 'types';
+
+/** 完全 or 不完全 な ClassUnit 配列 から Standard データを返す */
+const classUnitsToStandard = (classUnits: ClassUnit[]): StandardEn => {
+ const find = (to: string) => classUnits.find((v) => v.abstractLessonName === to)?.subject ?? [];
+ return Object.fromEntries(ABSTRACT_LESSON_NAMES.map((v) => [v, find(v)])) as StandardEn;
+};
+
+const handler: NextApiHandler = async (req, res) => {
+ if (typeof req.query['classId'] !== 'string' || isNaN(parseInt(req.query['classId']))) {
+ return res.status(400).send('Bad Request');
+ }
+
+ const classId = parseInt(req.query['classId']);
+
+ switch (req.method) {
+ case 'HEAD': {
+ return res.status(200).send('OK');
+ }
+
+ case 'GET': {
+ const classUnits = await prisma.classUnit.findMany({
+ where: { schoolClassId: { equals: classId } },
+ });
+
+ const standard = classUnitsToStandard(classUnits);
+
+ return res.status(200).json(standard);
+ }
+
+ case 'POST': {
+ if (isPostApiRequestBody(req.body)) {
+ const newStandard = req.body;
+ const oldClassUnits = await prisma.classUnit.findMany({ where: { schoolClassId: classId } });
+
+ // 各コマについてループ
+ // DBに変更を投げた Promise の配列が返る
+ const promises = ABSTRACT_LESSON_NAMES.flatMap((abstractLessonName) => {
+ const oldClassUnit = oldClassUnits.find((cu) => cu.abstractLessonName === abstractLessonName);
+ const newSubject = newStandard[abstractLessonName];
+
+ // 変化があった項目のみ更新(or作成)
+ if (oldClassUnit && !List(oldClassUnit.subject).equals(List(newSubject))) {
+ return prisma.classUnit.upsert({
+ where: { schoolClassId_abstractLessonName: { schoolClassId: classId, abstractLessonName } },
+ create: { abstractLessonName, subject: newSubject, SchoolClass: { connect: { id: classId } } },
+ update: { subject: { set: newSubject } },
+ });
+ }
+
+ return [];
+ });
+
+ await Promise.all(promises);
+
+ // 更新後の Standard を取得して返す
+ const standard = classUnitsToStandard(
+ await prisma.classUnit.findMany({
+ where: { schoolClassId: { equals: classId } },
+ })
+ );
+ return res.status(200).json(standard);
+ } else {
+ return res.status(400).send('Bad Request');
+ }
+ }
+
+ default: {
+ return res.status(405).send('Method Not Allowed');
+ }
+ }
+};
+
+export default handler;
diff --git a/src/pages/api/rest/v1/classes/index.ts b/src/pages/api/rest/v1/classes/index.ts
@@ -0,0 +1,21 @@
+import { prisma } from 'lib/prisma';
+
+import type { NextApiHandler } from 'next';
+
+const handler: NextApiHandler = async (req, res) => {
+ switch (req.method) {
+ case 'HEAD': {
+ return res.status(200).send('OK');
+ }
+
+ case 'GET': {
+ return res.status(200).json(await prisma.schoolClass.findMany({ orderBy: [{ grade: 'asc' }, { class: 'asc' }] }));
+ }
+
+ default: {
+ return res.status(405).send('Method Not Allowed');
+ }
+ }
+};
+
+export default handler;