Skip to content

Commit

Permalink
refactor: add more proper types to events service
Browse files Browse the repository at this point in the history
  • Loading branch information
raczu committed Jun 22, 2024
1 parent 8cac908 commit aa2abf5
Show file tree
Hide file tree
Showing 19 changed files with 168 additions and 47 deletions.
2 changes: 1 addition & 1 deletion Client/reasn-client/.husky/pre-commit
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

(cd ./Client/reasn-client && yarn lint-staged)
(cd ./Server/ReasnAPI && dotnet format)
if ! git diff --quiet; then
if git diff --name-only --quiet | grep '\.cs$'; then
echo "🚫 dotnet format made changes, commit aborted."
exit 1
fi
72 changes: 44 additions & 28 deletions Client/reasn-client/apps/web/services/event.ts
Original file line number Diff line number Diff line change
@@ -1,60 +1,76 @@
import { sendRequest, HttpMethod } from "@/lib/request";
import { EventRequest } from "@reasn/common/src/schemas/EventRequest";
import {
EventResponse,
EventResponseMapper,
} from "@reasn/common/src/schemas/EventResponse";
import { ParameterDto } from "@reasn/common/src/schemas/ParameterDto";
import { TagDto } from "@reasn/common/src/schemas/TagDto";

const baseUrl = `${process.env.REASN_API_URL}/api/v1/events`;

export const getEvents = async (
params: Record<string, string> = {},
): Promise<any> => {
): Promise<EventResponse[]> => {
const url = new URL(baseUrl);
url.search = new URLSearchParams(params).toString();

const response = await sendRequest<any>(url, { method: HttpMethod.GET });
const response = await sendRequest<EventResponse[]>(url, {
method: HttpMethod.GET,
});
return response;
};

export const createEvent = async (event: any): Promise<any> => {
export const createEvent = async (
eventRequest: EventRequest,
): Promise<EventResponse> => {
const url = new URL(baseUrl);

const response = await sendRequest<any>(url, {
const response = await sendRequest<EventResponse>(url, {
method: HttpMethod.POST,
body: event,
body: eventRequest,
authRequired: true,
});
return response;
return EventResponseMapper.fromObject(response);
};

export const getEventBySlug = async (slug: string): Promise<any> => {
export const getEventBySlug = async (slug: string): Promise<EventResponse> => {
const url = new URL(`${baseUrl}/${slug}`);

const response = await sendRequest<any>(url, { method: HttpMethod.GET });
return response;
const response = await sendRequest<EventResponse>(url, {
method: HttpMethod.GET,
});
return EventResponseMapper.fromObject(response);
};

export const updateEvent = async (slug: string, event: any): Promise<any> => {
export const updateEvent = async (
slug: string,
event: any,
): Promise<EventResponse> => {
const url = new URL(`${baseUrl}/${slug}`);

const response = await sendRequest<any>(url, {
const response = await sendRequest<EventResponse>(url, {
method: HttpMethod.PUT,
body: event,
authRequired: true,
});
return response;
return EventResponseMapper.fromObject(response);
};

export const getEventsRequests = async (): Promise<any> => {
export const getEventsRequests = async (): Promise<EventResponse[]> => {
const url = new URL(`${baseUrl}/requests`);

const response = await sendRequest<any>(url, {
const response = await sendRequest<EventResponse[]>(url, {
method: HttpMethod.GET,
authRequired: true,
});
return response;
};

export const approveEventRequest = async (slug: string): Promise<any> => {
export const approveEventRequest = async (slug: string): Promise<Object> => {
const url = new URL(`${baseUrl}/requests/${slug}`);

const response = await sendRequest<any>(url, {
const response = await sendRequest<Object>(url, {
method: HttpMethod.POST,
authRequired: true,
});
Expand All @@ -64,15 +80,15 @@ export const approveEventRequest = async (slug: string): Promise<any> => {
export const addEventImage = async (
slug: string,
images: Blob[],
): Promise<any> => {
): Promise<Object> => {
const url = new URL(`${baseUrl}/${slug}/images`);

const formData = new FormData();
images.forEach((image) => {
formData.append("images", image);
});

const response = await sendRequest<any>(url, {
const response = await sendRequest<Object>(url, {
method: HttpMethod.POST,
body: formData,
authRequired: true,
Expand All @@ -83,26 +99,26 @@ export const addEventImage = async (
export const updateEventImage = async (
slug: string,
images: Blob[],
): Promise<any> => {
): Promise<Object> => {
const url = new URL(`${baseUrl}/${slug}/images`);

const formData = new FormData();
images.forEach((image) => {
formData.append("images", image);
});

const response = await sendRequest<any>(url, {
const response = await sendRequest<Object>(url, {
method: HttpMethod.PUT,
body: formData,
authRequired: true,
});
return response;
};

export const getEventImages = async (slug: string): Promise<any> => {
export const getEventImages = async (slug: string): Promise<string[]> => {
const url = new URL(`${baseUrl}/${slug}/images`);

const response = await sendRequest<any>(url, { method: HttpMethod.GET });
const response = await sendRequest<string[]>(url, { method: HttpMethod.GET });
return response;
};

Expand Down Expand Up @@ -134,30 +150,30 @@ export const addEventComment = async (
return response;
};

export const getEventsParameters = async (): Promise<any> => {
export const getEventsParameters = async (): Promise<ParameterDto[]> => {
const url = new URL(`${baseUrl}/parameters`);

const response = await sendRequest<any>(url, {
const response = await sendRequest<ParameterDto[]>(url, {
method: HttpMethod.GET,
authRequired: true,
});
return response;
};

export const getEventsTags = async (): Promise<any> => {
export const getEventsTags = async (): Promise<TagDto[]> => {
const url = new URL(`${baseUrl}/tags`);

const response = await sendRequest<any>(url, {
const response = await sendRequest<TagDto[]>(url, {
method: HttpMethod.GET,
authRequired: true,
});
return response;
};

export const deleteEventsTag = async (tagId: number): Promise<any> => {
export const deleteEventsTag = async (tagId: number): Promise<Object> => {
const url = new URL(`${baseUrl}/tags/${tagId}`);

const response = await sendRequest<any>(url, {
const response = await sendRequest<Object>(url, {
method: HttpMethod.DELETE,
authRequired: true,
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { EventDto, EventDtoMapper } from "@reasn/common/src/schemas/EventDto";
import { EventStatus } from "@reasn/common/src/enums/modelsEnums";
import { EventStatus } from "@reasn/common/src/enums/schemasEnums";
import ModelMappingError from "@reasn/common/src/errors/ModelMappingError";
import { TagDto } from "@reasn/common/src/schemas/TagDto";

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ImageDto, ImageDtoMapper } from "@reasn/common/src/schemas/ImageDto";
import { ObjectType } from "@reasn/common/src/enums/modelsEnums";
import { ObjectType } from "@reasn/common/src/enums/schemasEnums";
import ModelMappingError from "@reasn/common/src/errors/ModelMappingError";

describe("ImageDto", () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {
ParticipantDto,
ParticipantDtoMapper,
} from "@reasn/common/src/schemas/ParticipantDto";
import { ParticipantStatus } from "@reasn/common/src/enums/modelsEnums";
import { ParticipantStatus } from "@reasn/common/src/enums/schemasEnums";

describe("ParticipantDto", () => {
const eventSlug = "event-slug";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import ModelMappingError from "@reasn/common/src/errors/ModelMappingError";
import { UserDto, UserDtoMapper } from "@reasn/common/src/schemas/UserDto";
import { UserInterestDto } from "@reasn/common/src/schemas/UserInterestDto";
import { UserRole } from "@reasn/common/src/enums/modelsEnums";
import { UserRole } from "@reasn/common/src/enums/schemasEnums";
import { AddressDto } from "@reasn/common/src/schemas/AddressDto";

describe("UserDto", () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { UserRole } from "../enums/modelsEnums";
import { UserRole } from "../enums/schemasEnums";

/**
* Represents the authentication data.
Expand Down
4 changes: 2 additions & 2 deletions Client/reasn-client/packages/common/src/schemas/AddressDto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ export const AddressDtoSchema = z.object({
.regex(/^\p{Lu}\p{Ll}+(?:(\s|-)\p{L}+)*$/u),
ZipCode: z
.string()
.nullable()
.refine((value) => value === null || /^[\p{L}\d\s-]{3,}$/u.test(value)),
.regex(/^[\p{L}\d\s-]{3,}$/u)
.nullable(),
});

export type AddressDto = z.infer<typeof AddressDtoSchema>;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import ModelMappingError from "../errors/ModelMappingError";
import { TagDtoSchema } from "./TagDto";
import { EventStatus } from "../enums/modelsEnums";
import { EventStatus } from "../enums/schemasEnums";
import { z } from "zod";

export const EventDtoSchema = z.object({
Expand Down
26 changes: 26 additions & 0 deletions Client/reasn-client/packages/common/src/schemas/EventRequest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { z } from "zod";
import { AddressDtoSchema } from "./AddressDto";
import { TagDtoSchema } from "./TagDto";
import { ParameterDtoSchema } from "./ParameterDto";

export const EventRequestSchema = z
.object({
name: z.string().max(64),
address: AddressDtoSchema,
description: z.string().max(4048),
startAt: z
.string()
.datetime({ offset: true })
.or(z.date())
.transform((arg) => new Date(arg)),
endAt: z
.string()
.datetime({ offset: true })
.or(z.date())
.transform((arg) => new Date(arg)),
tags: z.array(TagDtoSchema).nullable(),
parameters: z.array(ParameterDtoSchema).nullable(),
})
.refine((schema) => schema.startAt < schema.endAt);

export type EventRequest = z.infer<typeof EventRequestSchema>;
79 changes: 79 additions & 0 deletions Client/reasn-client/packages/common/src/schemas/EventResponse.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { z } from "zod";
import { AddressDtoSchema } from "@reasn/common/src/schemas/AddressDto";
import ModelMappingError from "@reasn/common/src/errors/ModelMappingError";
import { TagDtoSchema } from "@reasn/common/src/schemas/TagDto";
import { ParameterDtoSchema } from "@reasn/common/src/schemas/ParameterDto";
import { EventStatus } from "@reasn/common/src/enums/schemasEnums";

export const EventRespsoneSchema = z.object({
name: z.string().max(64),
addressId: z.number(),
address: AddressDtoSchema,
description: z.string().max(4048),
organizer: z.object({
username: z.string(),
image: z.string(),
}),
startAt: z
.string()
.datetime({ offset: true })
.or(z.date())
.transform((arg) => new Date(arg)),
endAt: z
.string()
.datetime({ offset: true })
.or(z.date())
.transform((arg) => new Date(arg)),
createdAt: z
.string()
.datetime({ offset: true })
.or(z.date())
.transform((arg) => new Date(arg)),
updatedAt: z
.string()
.datetime({ offset: true })
.or(z.date())
.transform((arg) => new Date(arg)),
slug: z
.string()
.max(128)
.regex(/^[\p{L}\d]+[\p{L}\d-]*$/u)
.nullable(),
status: z.nativeEnum(EventStatus),
tags: z.array(TagDtoSchema),
parameters: z.array(ParameterDtoSchema),
participants: z.object({
interested: z.number(),
participating: z.number(),
}),
});

export type EventResponse = z.infer<typeof EventRespsoneSchema>;

export const EventResponseMapper = {
fromObject: (entity: object): EventResponse => {
const result = EventRespsoneSchema.safeParse(entity);
if (!result.success) {
throw new ModelMappingError(
"EventResponse",
result.error.message,
result.error,
);
}
return result.data;
},
fromJSON: (jsonEntity: string): any => {
if (!jsonEntity) {
throw new ModelMappingError("EventResponse", "Empty JSON string");
}
const result = EventRespsoneSchema.safeParse(JSON.parse(jsonEntity));
if (!result.success) {
throw new ModelMappingError(
"EventResponse",
result.error.message,
result.error,
);
}
return result.data;
},
};
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import ModelMappingError from "../errors/ModelMappingError";
import { ObjectType } from "../enums/modelsEnums";
import { ObjectType } from "../enums/schemasEnums";
import { z } from "zod";

export const ImageDtoSchema = z.object({
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import ModelMappingError from "../errors/ModelMappingError";
import { ParticipantStatus } from "../enums/modelsEnums";
import { ParticipantStatus } from "../enums/schemasEnums";
import { z } from "zod";

export const ParticipantDtoSchema = z.object({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ export const RegisterRequestSchema = z.object({
.regex(/^((?=\S*?[A-Z])(?=\S*?[a-z])(?=\S*?[0-9]).{6,})\S$/u),
phone: z
.string()
.nullable()
.refine((value) => value === null || /^\+\d{1,3}\s\d{1,15}$/.test(value)),
.regex(/^\+\d{1,3}\s\d{1,15}$/)
.nullable(),
address: AddressDtoSchema,
Role: z.enum(["User", "Organizer"]),
});
Expand Down
2 changes: 1 addition & 1 deletion Client/reasn-client/packages/common/src/schemas/UserDto.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import ModelMappingError from "../errors/ModelMappingError";
import { UserInterestDtoSchema } from "../schemas/UserInterestDto";
import { AddressDtoSchema } from "../schemas/AddressDto";
import { UserRole } from "../enums/modelsEnums";
import { UserRole } from "../enums/schemasEnums";
import { z } from "zod";

export const UserDtoSchema = z.object({
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { UserRole } from "../enums/modelsEnums";
import { UserRole } from "../enums/schemasEnums";
import { AuthData } from "../interfaces/AuthData";

const AUTH_DATA_KEY = "REASN_AUTH_DATA";
Expand Down
Loading

0 comments on commit aa2abf5

Please sign in to comment.