Skip to content

Commit

Permalink
create siteAlert procedure
Browse files Browse the repository at this point in the history
  • Loading branch information
dhakalaashish committed Aug 17, 2023
1 parent e1d2f6c commit 7af46bb
Show file tree
Hide file tree
Showing 3 changed files with 156 additions and 10 deletions.
143 changes: 142 additions & 1 deletion apps/server/src/server/api/routers/alert.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { TRPCError } from "@trpc/server";
import { queryAlertSchema } from '../zodSchemas/alert.schema'
import { queryAlertSchema, createAlertSchema } from '../zodSchemas/alert.schema'
import {
createTRPCRouter,
protectedProcedure,
publicProcedure,
} from "../trpc";
import { getLocalTime, subtractDays } from "../../../utils/date";
import { GeoEventProvider } from "@prisma/client";
import { createXXHash3 } from "hash-wasm";

export const alertRouter = createTRPCRouter({

Expand Down Expand Up @@ -141,4 +143,143 @@ export const alertRouter = createTRPCRouter({
});
}
}),

create: protectedProcedure
.input(createAlertSchema)
.mutation(async ({ ctx, input }) => {
try {
const {
siteId,
type,
latitude,
longitude,
eventDate: inputEventDate,
detectedBy: geoEventProviderClientId,
confidence,
...rest
} = input;
const geoEventProviderClientApiKey = ctx.req.headers["x-api-key"];

// Ensure the user is authenticated
//Authentication ensure user is authenticated either with access token or with GeoEventProviderApiKey
if (!geoEventProviderClientApiKey && !ctx.user) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: `Missing Authorization header`,
});
}

if (!geoEventProviderClientId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: `Missing Provider Client Id Authorization`,
});
}

// Check whether the User is a GeoEventProviderClient or if the request has a GeoEventProviderApiKey and GeoEventProviderClientId
// Logic:
// get geoeventprovider from database where clientId = geoEventProviderClientId and (apiKey = geoEventProviderApiKey or userId = user.id)
// if no geoeventprovider is found throw error
// This logic ensures that either a geoEventProviderClient can continue, or that the one accessing this route must have a correct geoEventProviderClientKey

let provider: (GeoEventProvider | null) = null;

// If apiKey exists and is a string
if (geoEventProviderClientApiKey && typeof geoEventProviderClientApiKey === 'string') {
// Find provider where clientId and apiKey
provider = await ctx.prisma.geoEventProvider.findFirst({
where: {
clientId: geoEventProviderClientId,
clientApiKey: geoEventProviderClientApiKey,
},
});
} else if (ctx.user?.id) {
// Find provider where clientId and userId
provider = await ctx.prisma.geoEventProvider.findFirst({
where: {
clientId: geoEventProviderClientId,
userId: ctx.user?.id,
},
});
}

if (!provider) {
throw new TRPCError({
code: "NOT_FOUND",
message: `Provider not found`,
});
}

// Get site from the database using siteId; if not found, throw an error
const site = await ctx.prisma.site.findUnique({ where: { id: siteId } });
if (!site) {
throw new TRPCError({
code: "NOT_FOUND",
message: `Site Not Found`,
});
}

// To ensure same data isn't stored twice we will use id as a unique identifier
// generated from a hash of latitude, longitude, eventDate, type and x-client-id
// This will allow us to store the same event multiple times if it is from different providers
// but will not store the same event twice from the same provider

// Create checksum
const hasher = await createXXHash3();
hasher.init(); // Reset the hasher
const eventDate = inputEventDate ? inputEventDate : new Date()
const checksum = hasher.update(
latitude.toString() +
longitude.toString() +
eventDate.toISOString() +
type +
geoEventProviderClientId
).digest('hex');

// Verify if the event already exists
const existingSiteAlert = await ctx.prisma.siteAlert.findUnique({ where: { id: checksum } });

// If the event already exists, send a success message saying the creation process was cancelled
// Because the event was already stored in our database.
if (existingSiteAlert) {
return {
status: 'success',
message: 'Cancelled. This alert was already present in the database.'
}
}

// Create SiteAlert
const siteAlert = await ctx.prisma.siteAlert.create({
data: {
siteId,
type,
latitude,
longitude,
eventDate: eventDate,
detectedBy: geoEventProviderClientId,
confidence,
...rest,
isProcessed: false,
},
});

// Return success message with the created siteAlert
return {
status: 'success',
data: siteAlert,
};
}
catch (error) {
console.log(error);
if (error instanceof TRPCError) {
// If the error is already a TRPCError, just re-throw it
throw error;
}
// If it's a different type of error, throw a new TRPCError
throw new TRPCError({
code: "INTERNAL_SERVER_ERROR",
message: `${error}`,
});
}
}),
});
11 changes: 3 additions & 8 deletions apps/server/src/server/api/routers/geoEvent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,15 +37,13 @@ export const geoEventRouter = createTRPCRouter({
throw new TRPCError({ code: "BAD_REQUEST", message: `The value of req.headers['x-client-id'] must be a string` });
}

// Check whether the User is a GeoEventProviderClient or if the request has a GeoEventProviderApiKey and GeoEventProviderClientId
// Logic:
// get geoeventprovider from database where clientId = geoEventProviderClientId and (apiKey = geoEventProviderApiKey or userId = user.id)
// if no geoeventprovider is found throw error
// if geoeventprovider is found and apiKey is not equal to geoEventProviderClientApiKey
// throw error
// if geoeventprovider is found and userId is not equal to user.id continue normally
// if geoeventprovider is found and userId is equal to user.id continue normally
// This logic ensures that either a geoEventProviderClient can continue, or that the one accessing this route must have a correct geoEventProviderClientKey


// Aashish: The code below replicates above commented logic.
let provider: (GeoEventProvider | null) = null;

// If apiKey exists and is a string
Expand Down Expand Up @@ -81,9 +79,6 @@ export const geoEventRouter = createTRPCRouter({
});
}




// To ensure same data isn't stored twice we will use id as a unique identifier
// generated from a hash of latitude, longitude, eventDate, type and x-client-id
// This will allow us to store the same event multiple times if it is from different providers
Expand Down
12 changes: 11 additions & 1 deletion apps/server/src/server/api/zodSchemas/alert.schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,17 @@ export const queryAlertSchema = z.object({
id: z.string(),
})


export const createAlertSchema = z.object({
siteId: z.string(),
type: z.enum(["fire"]),
latitude: z.number(),
longitude: z.number(),
eventDate: z.date().optional(),
detectedBy: z.string(),
confidence: z.enum(["medium", "low", "high"]),
distance: z.number().optional(),
data: z.record(z.unknown()).optional(),
});



0 comments on commit 7af46bb

Please sign in to comment.