diff --git a/app/api/[[...slug]].ts b/app/api/[[...slug]].ts index fc4b918..fea578a 100644 --- a/app/api/[[...slug]].ts +++ b/app/api/[[...slug]].ts @@ -26,10 +26,25 @@ const Handler: BlitzApiHandler = async (req: BlitzApiRequest, res: BlitzApiRespo if (!obj) { return res.status(404).end() } - const { id, contentType, statusCode, sleep, response, logs } = obj - if (sleep !== 0) { - await snooze(sleep * 1000) - } + const { + id, + contentType, + statusCode, + sleep, + response, + logs, + ntimesError, + ntimesErrorStatusCode, + ntimesErrorCounter, + } = obj + + const ntimesErrorCount = (() => { + if (ntimesError === 0 || ntimesError === ntimesErrorCounter) { + return 0 + } else { + return ntimesErrorCounter + 1 + } + })() const RECENT_LOGS = 3 const { slug: _, ...query } = req.query @@ -55,8 +70,25 @@ const Handler: BlitzApiHandler = async (req: BlitzApiRequest, res: BlitzApiRespo log, ...logArr.filter((_, i) => logArr.length < RECENT_LOGS || i !== logArr.length - 1), ].join("\t") - await db.stub.update({ where: { id }, data: { logs: updatedLogs } }) + const inNtimesErrorTerm = ntimesError > 0 && ntimesError >= ntimesErrorCounter + 1 + const sleepFunc = async () => { + if (!inNtimesErrorTerm && sleep !== 0) { + return snooze(sleep * 1000) + } + } + + await Promise.all([ + sleepFunc(), + db.stub.update({ + where: { id }, + data: { logs: updatedLogs, ntimesErrorCounter: ntimesErrorCount }, + }), + ]) + + if (inNtimesErrorTerm) { + return res.status(Number(ntimesErrorStatusCode)).end() + } return res.status(Number(statusCode)).setHeader("Content-Type", contentType).end(response) } diff --git a/app/core/components/LabeledTextField.tsx b/app/core/components/LabeledTextField.tsx index 67f6cb2..8498fdf 100644 --- a/app/core/components/LabeledTextField.tsx +++ b/app/core/components/LabeledTextField.tsx @@ -2,6 +2,13 @@ import { forwardRef, PropsWithoutRef, ComponentPropsWithoutRef } from "react" import { useField } from "react-final-form" import { Input } from "@chakra-ui/input" +import { + NumberInput, + NumberInputField, + NumberInputStepper, + NumberIncrementStepper, + NumberDecrementStepper, +} from "@chakra-ui/react" import { FormControl, FormLabel } from "@chakra-ui/form-control" export interface LabeledTextFieldProps extends ComponentPropsWithoutRef { @@ -29,7 +36,17 @@ export const LabeledTextField = forwardRef {label} - + {props.type === "number" ? ( + + + + + + + + ) : ( + + )} {touched && normalizedError && (
diff --git a/app/core/layouts/Card.tsx b/app/core/layouts/Card.tsx index 0d52bdb..0eb1cc7 100644 --- a/app/core/layouts/Card.tsx +++ b/app/core/layouts/Card.tsx @@ -1,9 +1,17 @@ import React from "react" import { Box, Heading } from "@chakra-ui/react" -const Card = ({ heading, children }: { heading: string; children: any }) => { +const Card = ({ + heading, + children, + bgColor = "#FFF", +}: { + heading: string + children: any + bgColor?: string +}) => { return ( - + {heading} diff --git a/app/pages/stubs/[stubId].tsx b/app/pages/stubs/[stubId].tsx index 1dcadbc..d75999e 100644 --- a/app/pages/stubs/[stubId].tsx +++ b/app/pages/stubs/[stubId].tsx @@ -111,29 +111,77 @@ export const Stub = () => { - path - createdBy - createdAt - updatedBy - updatedAt - method - contentType - statusCode - sleep + + path + + + createdBy + + + createdAt + + + updatedBy + + + updatedAt + + + method + + + contentType + + + statusCode + + + sleep + + + ntimesError + + + ntimesErrorStatusCode + response - {stub.path} - {stub.createdBy} - {stub.createdAt.toLocaleString()} - {stub.updatedBy} - {stub.updatedAt.toLocaleString()} - {stub.method} - {stub.contentType} - {stub.statusCode} - {stub.sleep} s + + {stub.path} + + + {stub.createdBy} + + + {stub.createdAt.toLocaleString()} + + + {stub.updatedBy} + + + {stub.updatedAt.toLocaleString()} + + + {stub.method} + + + {stub.contentType} + + + {stub.statusCode} + + + {stub.sleep} s + + + {stub.ntimesError} times + + + {stub.ntimesErrorStatusCode} +
                       {(() => {
diff --git a/app/pages/stubs/[stubId]/edit.tsx b/app/pages/stubs/[stubId]/edit.tsx
index f1c31c1..0f44951 100644
--- a/app/pages/stubs/[stubId]/edit.tsx
+++ b/app/pages/stubs/[stubId]/edit.tsx
@@ -70,7 +70,10 @@ export const EditStub = () => {
         
           
              {
       
         
           >(props: FormProps) {
   const query = useRouterQuery()
@@ -43,8 +45,29 @@ export function StubForm>(props: FormProps) {
         ]}
       />
       
-      
       
+      
+        
+        
+        
+      
     
   )
 }
diff --git a/app/stubs/mutations/createStub.ts b/app/stubs/mutations/createStub.ts
index ee4c917..98f96c0 100644
--- a/app/stubs/mutations/createStub.ts
+++ b/app/stubs/mutations/createStub.ts
@@ -25,6 +25,11 @@ const CreateStub = z.object({
     .regex(/^\d{3}$/, { message: "The status code must be a three-digit number." }),
   response: z.string().default(""),
   sleep: z.number().min(0).default(0),
+  ntimesError: z.number().min(0).default(0),
+  ntimesErrorStatusCode: z
+    .string()
+    .regex(/^\d{3}$/, { message: "The ntimes error status code must be a three-digit number." }),
+  ntimesErrorCounter: z.number().min(0).default(0),
   logs: z.string().default(""),
   projectId: z.number(),
 })
diff --git a/app/stubs/mutations/updateStub.ts b/app/stubs/mutations/updateStub.ts
index f0890bf..d3f4a40 100644
--- a/app/stubs/mutations/updateStub.ts
+++ b/app/stubs/mutations/updateStub.ts
@@ -25,6 +25,10 @@ const UpdateStub = z.object({
     .regex(/^\d{3}$/, { message: "The status code must be a three-digit number." }),
   response: z.string(),
   sleep: z.number().min(0),
+  ntimesError: z.number().min(0).default(0),
+  ntimesErrorStatusCode: z
+    .string()
+    .regex(/^\d{3}$/, { message: "The ntimes error status code must be a three-digit number." }),
 })
 
 export default resolver.pipe(
diff --git a/db/migrations/20211010133505_/migration.sql b/db/migrations/20211010133505_/migration.sql
new file mode 100644
index 0000000..6ae4f80
--- /dev/null
+++ b/db/migrations/20211010133505_/migration.sql
@@ -0,0 +1,32 @@
+/*
+  Warnings:
+
+  - Added the required column `ntimesErrorStatusCode` to the `Stub` table without a default value. This is not possible if the table is not empty.
+
+*/
+-- RedefineTables
+PRAGMA foreign_keys=OFF;
+CREATE TABLE "new_Stub" (
+    "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
+    "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    "updatedAt" DATETIME NOT NULL,
+    "createdBy" TEXT NOT NULL,
+    "updatedBy" TEXT NOT NULL,
+    "path" TEXT NOT NULL,
+    "method" TEXT NOT NULL,
+    "contentType" TEXT NOT NULL,
+    "statusCode" TEXT NOT NULL,
+    "response" TEXT NOT NULL,
+    "sleep" INTEGER NOT NULL DEFAULT 0,
+    "logs" TEXT NOT NULL,
+    "ntimesError" INTEGER NOT NULL DEFAULT 0,
+    "ntimesErrorStatusCode" TEXT NOT NULL,
+    "ntimesErrorCounter" INTEGER NOT NULL DEFAULT 0,
+    "projectId" INTEGER NOT NULL,
+    CONSTRAINT "Stub_projectId_fkey" FOREIGN KEY ("projectId") REFERENCES "Project" ("id") ON DELETE CASCADE ON UPDATE CASCADE
+);
+INSERT INTO "new_Stub" ("contentType", "createdAt", "createdBy", "id", "logs", "method", "path", "projectId", "response", "sleep", "statusCode", "updatedAt", "updatedBy") SELECT "contentType", "createdAt", "createdBy", "id", "logs", "method", "path", "projectId", "response", "sleep", "statusCode", "updatedAt", "updatedBy" FROM "Stub";
+DROP TABLE "Stub";
+ALTER TABLE "new_Stub" RENAME TO "Stub";
+PRAGMA foreign_key_check;
+PRAGMA foreign_keys=ON;
diff --git a/db/schema.prisma b/db/schema.prisma
index f9c6c75..ebae33a 100644
--- a/db/schema.prisma
+++ b/db/schema.prisma
@@ -76,18 +76,21 @@ model Project {
 }
 
 model Stub {
-  id          Int      @id @default(autoincrement())
-  createdAt   DateTime @default(now())
-  updatedAt   DateTime @updatedAt
-  createdBy   String
-  updatedBy   String
-  path        String
-  method      String
-  contentType String
-  statusCode  String
-  response    String
-  sleep       Int
-  logs        String
-  project     Project  @relation(fields: [projectId], references: [id], onDelete: Cascade)
-  projectId   Int
+  id                    Int      @id @default(autoincrement())
+  createdAt             DateTime @default(now())
+  updatedAt             DateTime @updatedAt
+  createdBy             String
+  updatedBy             String
+  path                  String
+  method                String
+  contentType           String
+  statusCode            String
+  response              String
+  sleep                 Int      @default(0)
+  logs                  String
+  ntimesError           Int      @default(0)
+  ntimesErrorStatusCode String
+  ntimesErrorCounter    Int      @default(0)
+  project               Project  @relation(fields: [projectId], references: [id], onDelete: Cascade)
+  projectId             Int
 }