Skip to content

Commit

Permalink
Merge pull request #1366 from andrew-bierman/backend-update-trip-feature
Browse files Browse the repository at this point in the history
Update Trip endpoints
  • Loading branch information
taronaleksanian authored Dec 15, 2024
2 parents 9eca493 + c349373 commit 5a150f6
Show file tree
Hide file tree
Showing 12 changed files with 213 additions and 79 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,13 @@ const tripActivityValues = Object.values(TripActivity) as [string, ...string[]];

export const addTripForm = z.object({
name: z.string(),
description: z.string(),
activity: z.enum(tripActivityValues),
is_public: z.union([z.literal('0'), z.literal('1')]),
description: z.string().optional().nullable(),
activity: z.enum(tripActivityValues).optional(),
is_public: z.union([z.literal('0'), z.literal('1')]).optional(),
});

// @ts-ignore
const coordinateSchema = z.lazy(() =>
z.union([z.number(), z.array(coordinateSchema)]),
z.union([z.number(), z.array(z.number())]),
);

const baseGeometrySchema = z.object({
Expand Down Expand Up @@ -48,33 +47,35 @@ export const getTripById = z.object({
});

export const addTripDetails = z.object({
start_date: z.string(),
activity: z.enum(tripActivityValues).optional(),
bounds: z.tuple([z.array(z.number()), z.array(z.number())]).optional(),
end_date: z.string(),
destination: z.string(),
activity: z.enum(tripActivityValues),
parks: z.string().optional(),
trails: z.string().optional(),
geoJSON: z.string(),
owner_id: z.string(),
pack_id: z.string(),
bounds: z.tuple([z.array(z.number()), z.array(z.number())]),
parks: z.string().optional(),
start_date: z.string(),
trails: z.string().optional(),
});

export const addTrip = addTripDetails.merge(addTripForm);
export type AddTripType = z.infer<typeof addTrip>;

export const editTrip = z.object({
id: z.string(),
name: z.string().optional(),
description: z.string().optional(),
activity: z.enum(tripActivityValues).optional(),
start_date: z.string().optional(),
end_date: z.string().optional(),
destination: z.string().optional(),
export const editTrip = addTrip.merge(
z.object({
id: z.string().min(1),
}),
);

export type EditTripType = z.infer<typeof editTrip>;

export const setTripVisibility = z.object({
tripId: z.string().min(1),
is_public: z.union([z.literal('0'), z.literal('1')]),
});
export type SetTripVisibilityType = z.infer<typeof setTripVisibility>;

export const deleteTrip = z.object({
tripId: z.string().nonempty(),
tripId: z.string().min(1),
});

export const queryTrip = z.object({
Expand Down
14 changes: 8 additions & 6 deletions server/src/controllers/trip/addTrip.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { publicProcedure, protectedProcedure } from '../../trpc';
import { addTripService } from '../../services/trip/addTripService';
import { protectedProcedure } from '../../trpc';
import { Context } from 'hono';
import { addTripService } from '../../services/trip/trip.service';
import * as validator from '@packrat/validations';

export const addTrip = async (c) => {
export const addTrip = async (c: Context) => {
try {
const tripData = await c.req.json();
const trip = await addTripService(tripData);
const requestData = (await c.req.json()) satisfies validator.AddTripType;
const tripData = { ...requestData, ownerId: c.user.id };
const trip = await addTripService(tripData, c.executionCtx);
return c.json(trip, 200);
} catch (error) {
return c.json({ error: `${error.message}` }, 500);
Expand All @@ -14,7 +16,7 @@ export const addTrip = async (c) => {

export function addTripRoute() {
return protectedProcedure.input(validator.addTrip).mutation(async (opts) => {
const tripData = opts.input;
const tripData = { ownerId: opts.ctx.user.id, ...opts.input };
return await addTripService(tripData, opts.ctx.executionCtx);
});
}
11 changes: 5 additions & 6 deletions server/src/controllers/trip/editTrip.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { protectedProcedure } from '../../trpc';
import * as validator from '@packrat/validations';
import { Trip } from '../../drizzle/methods/trip';
import { editTripService } from '../../services/trip/trip.service';
import { Context } from 'hono';

export const editTrip = async (c) => {
export const editTrip = async (c: Context) => {
try {
const tripData = await c.req.json();
const tripClass = new Trip();
const trip = await tripClass.update(tripData);
const trip = await editTripService(tripData, c.executionCtx);
return c.json({ trip }, 200);
} catch (error) {
return c.json({ error: `${error.message}` }, 500);
Expand All @@ -16,8 +16,7 @@ export const editTrip = async (c) => {
export function editTripRoute() {
return protectedProcedure.input(validator.editTrip).mutation(async (opts) => {
const tripData = { ...opts.input };
const tripClass = new Trip();
const trip = await tripClass.update(tripData);
const trip = await editTripService(tripData, opts.ctx.executionCtx);
return trip;
});
}
1 change: 1 addition & 0 deletions server/src/controllers/trip/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ export * from './editTrip';
export * from './getPublicTrips';
export * from './getTrip';
export * from './getTripById';
export * from './setTripVisibility';
12 changes: 12 additions & 0 deletions server/src/controllers/trip/setTripVisibility.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { protectedProcedure } from '../../trpc';
import * as validator from '@packrat/validations';
import { setTripVisibilityService } from '../../services/trip/trip.service';

export function setTripVisibilityRoute() {
return protectedProcedure
.input(validator.setTripVisibility)
.mutation(async (opts) => {
const trip = await setTripVisibilityService(opts.input);
return trip;
});
}
4 changes: 1 addition & 3 deletions server/src/db/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -404,7 +404,7 @@ export const trip = sqliteTable('trip', {
.primaryKey()
.$defaultFn(() => createId()),
name: text('name').notNull(),
description: text('description').notNull(),
description: text('description'),
parks: text('parks', { mode: 'json' }).$type<
Array<{ id: string; name: string }>
>(),
Expand All @@ -413,7 +413,6 @@ export const trip = sqliteTable('trip', {
>(),
start_date: text('start_date').notNull(),
end_date: text('end_date').notNull(),
destination: text('destination').notNull(),
owner_id: text('owner_id').references(() => user.id, {
onDelete: 'cascade',
}),
Expand Down Expand Up @@ -463,7 +462,6 @@ export const tripRelations = relations(trip, ({ one, many }) => ({
fields: [trip.pack_id],
references: [pack.id],
}),
// geojsons: many(tripGeojsons),
tripGeojsons: many(tripGeojsons),
}));

Expand Down
26 changes: 21 additions & 5 deletions server/src/drizzle/methods/Geojson.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,34 @@
import { eq } from 'drizzle-orm';
import { DbClient } from '../../db/client';
import { type InsertGeoJson, geojson } from '../../db/schema';

export class GeoJson {
async create(geoJSON: InsertGeoJson) {
try {
const db = DbClient.instance;
const record = await db
.insert(geojson)
.values({ geoJSON })
.returning()
.get();
const record = await db.insert(geojson).values(geoJSON).returning().get();
return record;
} catch (error) {
throw new Error(`Failed to create geojson record: ${error.message}`);
}
}

async update(
id: string,
data: Partial<InsertGeoJson>,
filter = eq(geojson.id, id),
) {
try {
const geojsonValue = await DbClient.instance
.update(geojson)
.set(data)
.where(filter)
.returning()
.get();

return geojsonValue;
} catch (error) {
throw new Error(`Failed to geojson record: ${error.message}`);
}
}
}
53 changes: 28 additions & 25 deletions server/src/drizzle/methods/trip.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import { eq } from 'drizzle-orm';
import { DbClient } from '../../db/client';
import { type InsertTrip, trip as TripTable } from '../../db/schema';
import {
type InsertTrip,
type Trip as SelectTrip,
trip as TripTable,
} from '../../db/schema';

export class Trip {
async update(trip: Partial<InsertTrip>) {
async update(trip: InsertTrip) {
try {
if (!trip.id) {
throw new Error('Trip id is required for update operation');
Expand Down Expand Up @@ -48,7 +52,7 @@ export class Trip {

async findById(id: string) {
try {
const trip = await DbClient.instance.query.trip.findFirst({
return DbClient.instance.query.trip.findFirst({
where: eq(TripTable.id, id),
with: {
owner: {
Expand All @@ -59,27 +63,27 @@ export class Trip {
},
packs: {
columns: { id: true, name: true },
with: {
itemPacks: {
columns: { packId: true },
with: {
item: {
columns: {
id: true,
name: true,
weight: true,
quantity: true,
unit: true,
},
with: {
category: {
columns: { id: true, name: true },
},
},
},
},
},
},
// with: {
// itemPacks: {
// columns: { packId: true },
// with: {
// item: {
// columns: {
// id: true,
// name: true,
// weight: true,
// quantity: true,
// unit: true,
// },
// with: {
// category: {
// columns: { id: true, name: true },
// },
// },
// },
// },
// },
// },
},
tripGeojsons: {
columns: {},
Expand All @@ -89,7 +93,6 @@ export class Trip {
},
},
});
return trip;
} catch (error) {
throw new Error(`Failed to find trip by id: ${error.message}`);
}
Expand Down
3 changes: 3 additions & 0 deletions server/src/routes/trpcRouter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import {
getPublicTripsRoute,
getTripByIdRoute,
getTripsRoute,
setTripVisibilityRoute,
} from '../controllers/trip';
import {
addTemplateRoute,
Expand Down Expand Up @@ -113,6 +114,7 @@ import {
import { router as trpcRouter } from '../trpc';

import { getOfflineMapsRoute, saveOfflineMapRoute } from '../modules/map';
import { setTripVisibility } from '@packrat/validations';

export const appRouter = trpcRouter({
getUserById: getUserByIdRoute(),
Expand Down Expand Up @@ -146,6 +148,7 @@ export const appRouter = trpcRouter({
getTripById: getTripByIdRoute(),
addTrip: addTripRoute(),
editTrip: editTripRoute(),
setTripVisibility: setTripVisibilityRoute(),
deleteTrip: deleteTripRoute(),
// templates routes
getPackTemplates: getPackTemplatesRoute(),
Expand Down
41 changes: 28 additions & 13 deletions server/src/services/trip/addTripService.ts
Original file line number Diff line number Diff line change
@@ -1,39 +1,54 @@
import { calculateTripScore } from 'src/utils/scoreTrip';
import * as validator from '@packrat/validations';
import { GeoJson } from '../../drizzle/methods/Geojson';
import { TripGeoJson } from '../../drizzle/methods/TripGeoJson';
import { Trip } from '../../drizzle/methods/trip';
import { validateGeojsonId, validateGeojsonType } from '../../utils/geojson';
import { GeojsonStorageService } from '../geojsonStorage';
import { scoreTripService } from './scoreTripService';

export const addTripService = async (
tripData: any,
tripData: validator.AddTripType & { ownerId: string },
executionCtx: ExecutionContext,
) => {
try {
const { geoJSON, ...otherTripData } = tripData;
const geoJSON = tripData.geoJSON;
const tripClass = new Trip();

// Create Trip
const newTrip = await tripClass.create({
...otherTripData,
trails: otherTripData.trails ? JSON.parse(otherTripData.trails) : null,
parks: otherTripData.parks ? JSON.parse(otherTripData.parks) : null,
name: tripData.name,
description: tripData.description,
start_date: tripData.start_date,
end_date: tripData.end_date,
activity: tripData.activity || 'trip',
owner_id: tripData.ownerId,
pack_id: tripData.pack_id,
is_public: tripData.is_public === '0',
trails: tripData.trails ? JSON.parse(tripData.trails) : null,
parks: tripData.parks ? JSON.parse(tripData.parks) : null,
...(tripData.bounds && {
bounds: [tripData.bounds[0], tripData.bounds[1]],
}),
});

await scoreTripService(newTrip.id);

const geojsonClass = new GeoJson();
const tripGeoJsonClass = new TripGeoJson();
if (!geoJSON) {
throw new Error("Geojson data doesn't exist");
}

// const insertedGeoJson = await geojsonClass.create(geoJSON);
// await tripGeoJsonClass.create({
// tripId: newTrip.id,
// geojsonId: insertedGeoJson.id,
// });
const insertedGeoJson = await geojsonClass.create({
geoJSON,
});

await tripGeoJsonClass.create({
tripId: newTrip.id,
geojsonId: insertedGeoJson.id,
});

executionCtx.waitUntil(
GeojsonStorageService.save('trip', JSON.stringify(geoJSON), newTrip.id),
GeojsonStorageService.save('trip', geoJSON, newTrip.id),
);

return newTrip;
Expand Down
Loading

0 comments on commit 5a150f6

Please sign in to comment.