Skip to content

Commit

Permalink
Merge pull request #431 from VK-RED/feat/openapi-for-shares
Browse files Browse the repository at this point in the history
feat: openapi for shares endpoint
  • Loading branch information
dahal authored Jul 21, 2024
2 parents 17db694 + 9e2c6df commit db00a12
Show file tree
Hide file tree
Showing 23 changed files with 987 additions and 31 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "Share" ALTER COLUMN "companyLegends" SET DEFAULT ARRAY[]::"ShareLegendsEnum"[];
2 changes: 1 addition & 1 deletion prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -701,7 +701,7 @@ model Share {
vestingYears Int @default(0) // 0 means immediate vesting, 1 means vesting over 1 year
vestingSchedule VestingScheduleEnum
companyLegends ShareLegendsEnum[]
companyLegends ShareLegendsEnum[] @default([])
issueDate DateTime
rule144Date DateTime?
Expand Down
4 changes: 3 additions & 1 deletion src/components/ui/uploader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,9 @@ export function Uploader({
}
} catch (error) {
console.error("Error uploading file:", error);
toast.error("Uh oh! Something went wrong, please try again.");
toast.error(
"Uh oh! Something went wrong, please try again or contact support.",
);
} finally {
setUploading(false);
}
Expand Down
2 changes: 2 additions & 0 deletions src/server/api/error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ const ErrorCode = z.enum([
"METHOD_NOT_ALLOWED",
]);

export type ErrorCodeType = z.infer<typeof ErrorCode>;

function errorSchemaFactory(code: z.ZodEnum<[z.infer<typeof ErrorCode>]>) {
return z.object({
error: z.object({
Expand Down
8 changes: 4 additions & 4 deletions src/server/api/index.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import { PublicAPI } from "./hono";
import { initMiddleware } from "./middlewares/init";
import { registerCompanyRoutes } from "./routes/company";
import registerStakeholderRoutes from "./routes/company/stakeholder";
import { registerShareRoutes } from "./routes/company/share";
import { registerStakeholderRoutes } from "./routes/company/stakeholder";

const api = PublicAPI();

api.use("*", initMiddleware());

// RESTful routes for company
// Register RESTful routes
registerCompanyRoutes(api);

// RESTful routes for a stakeholder in a company
registerShareRoutes(api);
registerStakeholderRoutes(api);

export default api;
1 change: 0 additions & 1 deletion src/server/api/pagination/find-many.proxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ export function makeFindManyPaginated(model: ProxyFunctions) {
return {
data: docs,
count: docs.length,
// Send totalCount only for first request, let client side manages it for subsequent requests.
total: totalCount,
cursor: nextCursor,
};
Expand Down
107 changes: 107 additions & 0 deletions src/server/api/routes/company/share/create.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import { withCompanyAuth } from "@/server/api/auth";
import { ApiError, ErrorResponses } from "@/server/api/error";
import type { PublicAPI } from "@/server/api/hono";
import { CreateShareSchema } from "@/server/api/schema/shares";
import { getHonoUserAgent, getIp } from "@/server/api/utils";
import { addShare } from "@/server/services/shares/add-share";
import { createRoute, z } from "@hono/zod-openapi";
import type { Context } from "hono";

const ParamsSchema = z.object({
id: z
.string()
.cuid()
.openapi({
description: "Company ID",
param: {
name: "id",
in: "path",
},

example: "clycjihpy0002c5fzcyf4gjjc",
}),
});

const ResponseSchema = z.object({
message: z.string(),
data: CreateShareSchema,
});

const route = createRoute({
method: "post",
path: "/v1/companies/{id}/shares",
summary: "Issue shares",
description: "Issue shares to a stakeholder in a company.",
tags: ["Shares"],
request: {
params: ParamsSchema,
body: {
content: {
"application/json": {
schema: CreateShareSchema,
},
},
},
},
responses: {
200: {
content: {
"application/json": {
schema: ResponseSchema,
},
},
description: "Issue shares",
},
...ErrorResponses,
},
});

const create = (app: PublicAPI) => {
app.openapi(route, async (c: Context) => {
const { company, member, user } = await withCompanyAuth(c);
const body = await c.req.json();

const { success, message, data } = await addShare({
...body,
companyId: company.id,
memberId: member.id,
requestIP: getIp(c.req),
userAgent: getHonoUserAgent(c.req),
user: {
id: user.id,
name: user.name,
},
});

if (!success || !data) {
throw new ApiError({
code: "INTERNAL_SERVER_ERROR",
message: "Something went wrong, please try again or contact support.",
});
}

// Ensure data matches ResponseSchema
const responseData = {
status: data.status as string, // Cast to string if necessary
certificateId: data.certificateId,
quantity: data.quantity,
pricePerShare: data.pricePerShare ?? 0,
capitalContribution: data.capitalContribution ?? 0,
ipContribution: data.ipContribution ?? 0,
debtCancelled: data.debtCancelled ?? 0,
otherContributions: data.otherContributions ?? 0,
vestingSchedule: data.vestingSchedule ?? "",
companyLegends: data.companyLegends ?? "", // Add missing fields
issueDate: data.issueDate ?? new Date().toISOString(), // Add missing fields
rule144Date: data.rule144Date ?? new Date().toISOString(), // Add missing fields
vestingStartDate: data.vestingStartDate ?? new Date().toISOString(), // Add missing fields
boardApprovalDate: data.boardApprovalDate ?? new Date().toISOString(), // Add boardApprovalDate
stakeholderId: data.stakeholderId ?? "", // Add stakeholderId
shareClassId: data.shareClassId,
};

return c.json({ message, data: responseData }, 200);
});
};

export default create;
73 changes: 73 additions & 0 deletions src/server/api/routes/company/share/delete.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { withCompanyAuth } from "@/server/api/auth";
import {
ApiError,
type ErrorCodeType,
ErrorResponses,
} from "@/server/api/error";
import type { PublicAPI } from "@/server/api/hono";
import { getHonoUserAgent, getIp } from "@/server/api/utils";
import { deleteShare } from "@/server/services/shares/delete-share";
import { createRoute, z } from "@hono/zod-openapi";
import type { Context } from "hono";
import { RequestParamsSchema } from "./update";

const ResponseSchema = z
.object({
message: z.string(),
})
.openapi({
description: "Delete a Share by ID",
});

const route = createRoute({
method: "delete",
path: "/v1/companies/{id}/shares/{shareId}",
summary: "Delete issued shares",
description: "Delete a Share by ID",
tags: ["Shares"],
request: {
params: RequestParamsSchema,
},
responses: {
200: {
content: {
"application/json": {
schema: ResponseSchema,
},
},
description: "Delete a Share by ID",
},
...ErrorResponses,
},
});

const deleteOne = (app: PublicAPI) => {
app.openapi(route, async (c: Context) => {
const { company, user } = await withCompanyAuth(c);
const { shareId: id } = c.req.param();

const { success, code, message } = await deleteShare({
companyId: company.id,
requestIp: getIp(c.req),
userAgent: getHonoUserAgent(c.req),
shareId: id as string,
user: { id: user.id, name: user.name || "" },
});

if (!success) {
throw new ApiError({
code: code as ErrorCodeType,
message,
});
}

return c.json(
{
message: message,
},
200,
);
});
};

export default deleteOne;
84 changes: 84 additions & 0 deletions src/server/api/routes/company/share/getMany.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { withCompanyAuth } from "@/server/api/auth";
import { ErrorResponses } from "@/server/api/error";
import type { PublicAPI } from "@/server/api/hono";
import {
DEFAULT_PAGINATION_LIMIT,
PaginationQuerySchema,
PaginationResponseSchema,
} from "@/server/api/schema/pagination";
import { ShareSchema } from "@/server/api/schema/shares";
import { getPaginatedShares } from "@/server/services/shares/get-shares";
import { createRoute, z } from "@hono/zod-openapi";
import type { Context } from "hono";

const ParamsSchema = z.object({
id: z
.string()
.cuid()
.openapi({
description: "Company ID",
param: {
name: "id",
in: "path",
},

example: "clxwbok580000i7nge8nm1ry0",
}),
});

const ResponseSchema = z
.object({
data: z.array(ShareSchema),
meta: PaginationResponseSchema,
})
.openapi({
description: "Get Shares by Company ID",
});

const route = createRoute({
method: "get",
path: "/v1/companies/{id}/shares",
summary: "Get list of issued shares",
description: "Get list of issued shares for a company",
tags: ["Shares"],
request: {
params: ParamsSchema,
query: PaginationQuerySchema,
},
responses: {
200: {
content: {
"application/json": {
schema: ResponseSchema,
},
},
description: "Retrieve the shares for the company",
},
...ErrorResponses,
},
});

const getMany = (app: PublicAPI) => {
app.openapi(route, async (c: Context) => {
const { company } = await withCompanyAuth(c);

const { take, cursor, total } = c.req.query();

const { data, meta } = await getPaginatedShares({
companyId: company.id,
take: Number(take || DEFAULT_PAGINATION_LIMIT),
cursor,
total: Number(total),
});

return c.json(
{
data,
meta,
},
200,
);
});
};

export default getMany;
Loading

0 comments on commit db00a12

Please sign in to comment.