"add PartyEvent API endpoints with service layer and test cases"
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
// src/app/api/party-event/create/route.ts
|
||||
//
|
||||
// PURPOSE:
|
||||
// Create new party event in db
|
||||
//
|
||||
// RULES:
|
||||
// T.B.A.
|
||||
|
||||
import type { NextRequest } from 'next/server';
|
||||
|
||||
import { STATUS, response, handleError } from 'src/utils/response';
|
||||
|
||||
import { isDev } from 'src/constants';
|
||||
import { createEvent } from 'src/app/services/party-event.service';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
/** **************************************
|
||||
* POST - Create PartyEvent
|
||||
*************************************** */
|
||||
export async function POST(req: NextRequest) {
|
||||
const { partyEventData } = await req.json();
|
||||
|
||||
try {
|
||||
if (isDev) {
|
||||
console.log({ partyEventData });
|
||||
}
|
||||
|
||||
const created = await createEvent(partyEventData);
|
||||
|
||||
if (isDev) {
|
||||
console.log('Event created successfully');
|
||||
}
|
||||
|
||||
return response(created, STATUS.OK);
|
||||
} catch (error) {
|
||||
console.error('Error creating event:', { partyEventData });
|
||||
return handleError('PartyEvent - Create', error);
|
||||
}
|
||||
}
|
@@ -0,0 +1,34 @@
|
||||
###
|
||||
|
||||
POST http://localhost:7272/api/party-event/create
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"partyEventData": {
|
||||
"title": "Summer Music Festival",
|
||||
"description": "Annual summer music festival featuring local bands and artists",
|
||||
"startDate": "2024-07-15T18:00:00Z",
|
||||
"endDate": "2024-07-15T23:00:00Z",
|
||||
"location": "Central Park, Hong Kong",
|
||||
"coverUrl": "",
|
||||
"images": [
|
||||
"data:image/png;base64,C",
|
||||
"data:image/png;base64,C"
|
||||
],
|
||||
"tags": [
|
||||
"Music",
|
||||
"Festival"
|
||||
],
|
||||
"status": "upcoming",
|
||||
"capacity": 500,
|
||||
"price": 150.00,
|
||||
"organizer": "HK Music Society",
|
||||
"category": "Music",
|
||||
"isFeatured": true,
|
||||
"registrationDeadline": "2024-07-10T00:00:00Z",
|
||||
"requirements": "Age 18+",
|
||||
"schedule": "18:00 Doors open\n19:00 First performance\n21:00 Main act",
|
||||
"speakers": ["DJ Lee", "Band XYZ"],
|
||||
"sponsors": ["HK Radio", "Music Magazine"]
|
||||
}
|
||||
}
|
@@ -0,0 +1,22 @@
|
||||
import type { NextRequest } from 'next/server';
|
||||
|
||||
import { STATUS, response, handleError } from 'src/utils/response';
|
||||
|
||||
import { deleteEvent } from 'src/app/services/party-event.service';
|
||||
|
||||
/** **************************************
|
||||
* PATCH - Delete PartyEvent
|
||||
*************************************** */
|
||||
export async function PATCH(req: NextRequest) {
|
||||
try {
|
||||
const { eventId } = await req.json();
|
||||
|
||||
if (!eventId) throw new Error('eventId cannot be null');
|
||||
|
||||
await deleteEvent(eventId);
|
||||
|
||||
return response({ eventId }, STATUS.OK);
|
||||
} catch (error) {
|
||||
return handleError('PartyEvent - Delete', error);
|
||||
}
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
###
|
||||
|
||||
PATCH http://localhost:7272/api/party-event/delete
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"eventId": "e99f09a7-dd88-49d5-b1c8-1daf80c2d7b01"
|
||||
}
|
@@ -0,0 +1,56 @@
|
||||
// src/app/api/party-event/details/route.ts
|
||||
//
|
||||
// PURPOSE:
|
||||
// Get party event from db by id
|
||||
//
|
||||
// RULES:
|
||||
// T.B.A.
|
||||
|
||||
import type { NextRequest } from 'next/server';
|
||||
|
||||
import { logger } from 'src/utils/logger';
|
||||
import { STATUS, response, handleError } from 'src/utils/response';
|
||||
|
||||
import { L_INFO, L_ERROR } from 'src/constants';
|
||||
import { getEvent } from 'src/app/services/party-event.service';
|
||||
import { createAppLog } from 'src/app/services/app-log.service';
|
||||
|
||||
import { flattenNextjsRequest } from '../../auth/sign-in/flattenNextjsRequest';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
**************************************
|
||||
* GET PartyEvent detail
|
||||
***************************************
|
||||
*/
|
||||
export async function GET(req: NextRequest) {
|
||||
const debug = { 'req.headers': flattenNextjsRequest(req) };
|
||||
|
||||
try {
|
||||
const { searchParams } = req.nextUrl;
|
||||
|
||||
// RULES: eventId must exist
|
||||
const eventId = searchParams.get('eventId');
|
||||
if (!eventId) {
|
||||
return response({ message: 'Event ID is required!' }, STATUS.BAD_REQUEST);
|
||||
}
|
||||
|
||||
// NOTE: eventId confirmed exist, run below
|
||||
const event = await getEvent(eventId);
|
||||
|
||||
if (!event) {
|
||||
return response({ message: 'Event not found!' }, STATUS.NOT_FOUND);
|
||||
}
|
||||
|
||||
logger('[PartyEvent] details', event.id);
|
||||
|
||||
createAppLog(L_INFO, 'Get event detail OK', debug);
|
||||
|
||||
return response({ event }, STATUS.OK);
|
||||
} catch (error) {
|
||||
createAppLog(L_ERROR, 'event detail error', debug);
|
||||
|
||||
return handleError('PartyEvent - Get details', error);
|
||||
}
|
||||
}
|
@@ -0,0 +1,9 @@
|
||||
###
|
||||
|
||||
# Get details for a specific party event
|
||||
GET http://localhost:7272/api/party-event/details?eventId=e99f09a7-dd88-49d5-b1c8-1daf80c2d7b01
|
||||
|
||||
###
|
||||
|
||||
# Alternative format with different ID
|
||||
GET http://localhost:7272/api/party-event/details?eventId=evt_987654321
|
@@ -0,0 +1,16 @@
|
||||
import type { NextRequest, NextResponse } from 'next/server';
|
||||
|
||||
import { STATUS, response, handleError } from 'src/utils/response';
|
||||
|
||||
/**
|
||||
***************************************
|
||||
* GET - helloworld
|
||||
***************************************
|
||||
*/
|
||||
export async function GET(req: NextRequest, res: NextResponse) {
|
||||
try {
|
||||
return response({ helloworld: 'party-event' }, STATUS.OK);
|
||||
} catch (error) {
|
||||
return handleError('Helloworld - Get all', error);
|
||||
}
|
||||
}
|
@@ -0,0 +1,4 @@
|
||||
###
|
||||
GET /api/party-event/helloworld HTTP/1.1
|
||||
Host: localhost:7272
|
||||
|
38
03_source/cms_backend/src/app/api/party-event/list/route.ts
Normal file
38
03_source/cms_backend/src/app/api/party-event/list/route.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
// src/app/api/party-event/list/route.ts
|
||||
//
|
||||
// PURPOSE:
|
||||
// List all party events from db
|
||||
//
|
||||
// RULES:
|
||||
// T.B.A.
|
||||
|
||||
import type { NextRequest } from 'next/server';
|
||||
|
||||
import { STATUS, response, handleError } from 'src/utils/response';
|
||||
|
||||
import { L_INFO, L_ERROR } from 'src/constants';
|
||||
import { createAppLog } from 'src/app/services/app-log.service';
|
||||
import { listEvents } from 'src/app/services/party-event.service';
|
||||
|
||||
import { flattenNextjsRequest } from '../../auth/sign-in/flattenNextjsRequest';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
/** **************************************
|
||||
* GET - PartyEvents list
|
||||
*************************************** */
|
||||
export async function GET(req: NextRequest) {
|
||||
const debug = { 'req.headers': flattenNextjsRequest(req) };
|
||||
|
||||
try {
|
||||
const events = await listEvents();
|
||||
|
||||
createAppLog(L_INFO, 'party-event list ok', {});
|
||||
|
||||
return response({ events }, STATUS.OK);
|
||||
} catch (error) {
|
||||
createAppLog(L_ERROR, 'party-event list error', debug);
|
||||
|
||||
return handleError('PartyEvent - Get list', error);
|
||||
}
|
||||
}
|
14
03_source/cms_backend/src/app/api/party-event/list/test.http
Normal file
14
03_source/cms_backend/src/app/api/party-event/list/test.http
Normal file
@@ -0,0 +1,14 @@
|
||||
###
|
||||
|
||||
# Basic list all party events
|
||||
GET http://localhost:7272/api/party-event/list
|
||||
|
||||
###
|
||||
|
||||
# List upcoming party events
|
||||
GET http://localhost:7272/api/party-event/list?status=upcoming
|
||||
|
||||
###
|
||||
|
||||
# List featured party events
|
||||
GET http://localhost:7272/api/party-event/list?isFeatured=true
|
75
03_source/cms_backend/src/app/api/party-event/route.ts
Normal file
75
03_source/cms_backend/src/app/api/party-event/route.ts
Normal file
@@ -0,0 +1,75 @@
|
||||
import type { NextRequest, NextResponse } from 'next/server';
|
||||
|
||||
import { STATUS, response, handleError } from 'src/utils/response';
|
||||
|
||||
import { listEvents, deleteEvent, updateEvent, createEvent } from 'src/app/services/party-event.service';
|
||||
|
||||
/**
|
||||
**************************************
|
||||
* GET - PartyEvent
|
||||
***************************************
|
||||
*/
|
||||
export async function GET(req: NextRequest, res: NextResponse) {
|
||||
try {
|
||||
const events = await listEvents();
|
||||
return response(events, STATUS.OK);
|
||||
} catch (error) {
|
||||
return handleError('PartyEvent - Get list', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
***************************************
|
||||
* POST - Create PartyEvent
|
||||
***************************************
|
||||
*/
|
||||
export async function POST(req: NextRequest) {
|
||||
const OPERATION = 'PartyEvent - Create';
|
||||
const { data } = await req.json();
|
||||
|
||||
try {
|
||||
const event = await createEvent(data);
|
||||
return response(OPERATION, STATUS.OK);
|
||||
} catch (error) {
|
||||
return handleError(OPERATION, error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
***************************************
|
||||
* PUT - Update PartyEvent
|
||||
***************************************
|
||||
*/
|
||||
export async function PUT(req: NextRequest) {
|
||||
const { searchParams } = req.nextUrl;
|
||||
const eventId = searchParams.get('eventId');
|
||||
const { data } = await req.json();
|
||||
|
||||
try {
|
||||
if (!eventId) throw new Error('eventId cannot be null');
|
||||
|
||||
const result = await updateEvent(eventId, data);
|
||||
return response(result, STATUS.OK);
|
||||
} catch (error) {
|
||||
return handleError('PartyEvent - Update', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
***************************************
|
||||
* DELETE - Delete PartyEvent
|
||||
***************************************
|
||||
*/
|
||||
export async function DELETE(req: NextRequest) {
|
||||
const { searchParams } = req.nextUrl;
|
||||
const eventId = searchParams.get('eventId');
|
||||
|
||||
try {
|
||||
if (!eventId) throw new Error('eventId cannot be null');
|
||||
|
||||
await deleteEvent(eventId);
|
||||
return response({ success: true }, STATUS.OK);
|
||||
} catch (error) {
|
||||
return handleError('PartyEvent - Delete', error);
|
||||
}
|
||||
}
|
@@ -0,0 +1,34 @@
|
||||
import type { NextRequest } from 'next/server';
|
||||
|
||||
import { logger } from 'src/utils/logger';
|
||||
import { STATUS, response, handleError } from 'src/utils/response';
|
||||
import { IEventItem } from '../update/route';
|
||||
|
||||
// import { searchEvents } from 'src/app/services/party-event.service';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
/** **************************************
|
||||
* GET - Search PartyEvents
|
||||
*************************************** */
|
||||
export async function GET(req: NextRequest) {
|
||||
try {
|
||||
const { searchParams } = req.nextUrl;
|
||||
const query = searchParams.get('query')?.trim().toLowerCase();
|
||||
|
||||
if (!query) {
|
||||
return response({ results: [] }, STATUS.OK);
|
||||
}
|
||||
|
||||
const results: IEventItem[] = [];
|
||||
// TODO: search party event not implemented
|
||||
console.log('search party event not implemented');
|
||||
// const results = await searchEvents(query);
|
||||
|
||||
logger('[PartyEvent] search-results', results?.length);
|
||||
|
||||
return response({ results }, STATUS.OK);
|
||||
} catch (error) {
|
||||
return handleError('PartyEvent - Get search', error);
|
||||
}
|
||||
}
|
@@ -0,0 +1,24 @@
|
||||
###
|
||||
|
||||
# Search party events by title
|
||||
GET http://localhost:7272/api/party-event/search?query=Music
|
||||
|
||||
###
|
||||
|
||||
# Search party events by location
|
||||
GET http://localhost:7272/api/party-event/search?query=Central+Park
|
||||
|
||||
###
|
||||
|
||||
# Search party events by tag
|
||||
GET http://localhost:7272/api/party-event/search?query=Festival
|
||||
|
||||
###
|
||||
|
||||
# Combined search with multiple parameters
|
||||
GET http://localhost:7272/api/party-event/search?query=Summer&location=Hong+Kong&category=Music
|
||||
|
||||
###
|
||||
|
||||
# No results expected
|
||||
GET http://localhost:7272/api/party-event/search?query=zzzzzz
|
@@ -0,0 +1,57 @@
|
||||
// src/app/api/party-event/update/route.ts
|
||||
//
|
||||
// PURPOSE:
|
||||
// Update party event in db by id
|
||||
//
|
||||
// RULES:
|
||||
// T.B.A.
|
||||
|
||||
import type { NextRequest } from 'next/server';
|
||||
|
||||
import { STATUS, response, handleError } from 'src/utils/response';
|
||||
|
||||
import { updateEvent } from 'src/app/services/party-event.service';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
/** **************************************
|
||||
* PUT - Update PartyEvent
|
||||
*************************************** */
|
||||
export async function PUT(req: NextRequest) {
|
||||
const { partyEventData } = await req.json();
|
||||
const { id } = partyEventData;
|
||||
|
||||
if (!id) return response({ message: 'id not found' }, STATUS.ERROR);
|
||||
|
||||
try {
|
||||
const result = await updateEvent(id, partyEventData);
|
||||
|
||||
return response({ result }, STATUS.OK);
|
||||
} catch (error) {
|
||||
console.error('Error updating event:', { partyEventData });
|
||||
return handleError('PartyEvent - Update', error);
|
||||
}
|
||||
}
|
||||
|
||||
export type IEventItem = {
|
||||
id: string;
|
||||
title: string;
|
||||
description: string;
|
||||
startDate: Date;
|
||||
endDate: Date;
|
||||
location: string;
|
||||
coverUrl: string;
|
||||
images: string[];
|
||||
tags: string[];
|
||||
status: string;
|
||||
capacity: number;
|
||||
price: number;
|
||||
organizer: string;
|
||||
category: string;
|
||||
isFeatured: boolean;
|
||||
registrationDeadline: Date;
|
||||
requirements: string[];
|
||||
schedule: string;
|
||||
speakers: string[];
|
||||
sponsors: string[];
|
||||
};
|
@@ -0,0 +1,35 @@
|
||||
###
|
||||
|
||||
PUT http://localhost:7272/api/party-event/update
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"partyEventData": {
|
||||
"id":"e99f09a7-dd88-49d5-b1c8-1daf80c2d7b01",
|
||||
"title": "Summer Music Festival",
|
||||
"description": "Annual summer music festival featuring local bands and artists",
|
||||
"startDate": "2024-07-15T18:00:00Z",
|
||||
"endDate": "2024-07-15T23:00:00Z",
|
||||
"location": "Central Park, Hong Kong",
|
||||
"coverUrl": "",
|
||||
"images": [
|
||||
"data:image/png;base64,C",
|
||||
"data:image/png;base64,C"
|
||||
],
|
||||
"tags": [
|
||||
"Music",
|
||||
"Festival"
|
||||
],
|
||||
"status": "upcoming",
|
||||
"capacity": 500,
|
||||
"price": 150.00,
|
||||
"organizer": "HK Music Society",
|
||||
"category": "Music",
|
||||
"isFeatured": true,
|
||||
"registrationDeadline": "2024-07-10T00:00:00Z",
|
||||
"requirements": "Age 18+",
|
||||
"schedule": "18:00 Doors open\n19:00 First performance\n21:00 Main act",
|
||||
"speakers": ["DJ Lee", "Band XYZ"],
|
||||
"sponsors": ["HK Radio", "Music Magazine"]
|
||||
}
|
||||
}
|
@@ -0,0 +1,92 @@
|
||||
// src/app/services/party-event.service.ts
|
||||
//
|
||||
// PURPOSE:
|
||||
// - Service for handling EventItem (PartyEvent) Record
|
||||
//
|
||||
|
||||
import type { EventItem } from '@prisma/client';
|
||||
|
||||
import prisma from '../lib/prisma';
|
||||
|
||||
type CreateEvent = {
|
||||
name: string;
|
||||
title: string;
|
||||
eventDate: Date;
|
||||
location: string;
|
||||
duration_m: number;
|
||||
ageBottom?: number;
|
||||
ageTop?: number;
|
||||
currency?: string;
|
||||
price?: number;
|
||||
priceSale?: number;
|
||||
coverUrl?: string;
|
||||
images?: string[];
|
||||
description?: string;
|
||||
subDescription?: string;
|
||||
publish?: string;
|
||||
category?: string;
|
||||
tags?: string[];
|
||||
joinMembers?: any[];
|
||||
};
|
||||
|
||||
type UpdateEvent = {
|
||||
name?: string;
|
||||
title?: string;
|
||||
eventDate?: Date;
|
||||
location?: string;
|
||||
duration_m?: number;
|
||||
ageBottom?: number;
|
||||
ageTop?: number;
|
||||
currency?: string;
|
||||
price?: number;
|
||||
priceSale?: number;
|
||||
coverUrl?: string;
|
||||
images?: string[];
|
||||
description?: string;
|
||||
subDescription?: string;
|
||||
publish?: string;
|
||||
category?: string;
|
||||
tags?: string[];
|
||||
joinMembers?: any[];
|
||||
};
|
||||
|
||||
async function listEvents(): Promise<EventItem[]> {
|
||||
return prisma.eventItem.findMany({
|
||||
include: { reviews: true },
|
||||
});
|
||||
}
|
||||
|
||||
async function getEvent(eventId: string): Promise<EventItem | null> {
|
||||
return prisma.eventItem.findUnique({
|
||||
where: { id: eventId },
|
||||
include: { reviews: true },
|
||||
});
|
||||
}
|
||||
|
||||
async function getEventByNameOrTitle(searchText: string): Promise<EventItem[] | null> {
|
||||
return prisma.eventItem.findMany({
|
||||
where: {
|
||||
OR: [{ name: { contains: searchText, mode: 'insensitive' } }, { title: { contains: searchText, mode: 'insensitive' } }],
|
||||
},
|
||||
include: { reviews: true },
|
||||
});
|
||||
}
|
||||
|
||||
async function createEvent(eventData: any) {
|
||||
return await prisma.eventItem.create({ data: eventData });
|
||||
}
|
||||
|
||||
async function updateEvent(eventId: string, updateForm: UpdateEvent) {
|
||||
return prisma.eventItem.update({
|
||||
where: { id: eventId },
|
||||
data: updateForm,
|
||||
});
|
||||
}
|
||||
|
||||
async function deleteEvent(eventId: string) {
|
||||
return prisma.eventItem.delete({
|
||||
where: { id: eventId },
|
||||
});
|
||||
}
|
||||
|
||||
export { getEvent, listEvents, createEvent, updateEvent, deleteEvent, getEventByNameOrTitle, type CreateEvent, type UpdateEvent };
|
Reference in New Issue
Block a user