Last active
November 3, 2023 16:55
-
-
Save diasjuniorr/9fd9d199e78787ee45aff25ca2cc3f30 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { serve } from "https://deno.land/[email protected]/http/server.ts"; | |
import { corsHeaders } from "../_shared/cors.ts"; | |
import { USER_ROLES } from "../_shared/roles.ts"; | |
import { | |
throwCustomError, | |
throwInternalCustomError, | |
handleErrorResponse, | |
} from "../_shared/customError.ts"; | |
import { UNAUTHORIZED_ERROR, USER_NOT_FOUND_ERROR, newMissingParametersError } from "../_shared/errors.ts"; | |
import { createNewSupabaseClient } from "../_shared/client.ts"; | |
// Define functional utilities | |
// Curry a function to apply it partially | |
const curry = (fn) => { | |
const arity = fn.length; | |
return function $curry(...args) { | |
if (args.length < arity) { | |
return $curry.bind(null, ...args); | |
} | |
return fn(...args); | |
}; | |
}; | |
// Compose functions from right to left | |
const compose = (...fns) => (x) => fns.reduceRight((acc, fn) => fn(acc), x); | |
// Create a pipe function for composing functions from left to right | |
const pipe = (...fns) => (x) => fns.reduce((acc, fn) => fn(acc), x); | |
// Define the functional implementation | |
// Utility function to throw custom error | |
const throwError = (error) => () => { | |
throw error; | |
}; | |
// Check user role | |
const checkUserRole = (role) => { | |
if (role !== USER_ROLES.ADMIN && role !== USER_ROLES.SALESMAN) { | |
throwCustomError(UNAUTHORIZED_ERROR); | |
} | |
return role; | |
}; | |
// Parse request body | |
const parseRequestBody = async (req) => { | |
const { | |
costumer_id, | |
total_pericias, | |
total_price, | |
start_date, | |
end_date, | |
pericias, | |
}: CreateInvoiceRequest = await req.json(); | |
const requiredFields = [ | |
{ field: costumer_id, name: "costumer_id" }, | |
{ field: total_pericias, name: "total_pericias" }, | |
{ field: total_price, name: "total_price" }, | |
{ field: start_date, name: "start_date" }, | |
{ field: end_date, name: "end_date" }, | |
{ field: pericias, name: "pericias" }, | |
]; | |
for (const field of requiredFields) { | |
if (!field.field) throwCustomError(newMissingParametersError(`${field.name} is required`)); | |
} | |
if (!pericias.length) throwCustomError(newMissingParametersError("pericias is required")); | |
return { costumer_id, total_pericias, total_price, start_date, end_date, pericias }; | |
}; | |
// Insert invoice into the database | |
const insertInvoice = async (nextValue, now, invoiceData, supabaseClient) => { | |
const { data, error } = await supabaseClient | |
.from("invoices") | |
.insert({ | |
costumer_id: invoiceData.costumer_id, | |
total_pericias: invoiceData.total_pericias, | |
total_price: invoiceData.total_price, | |
start_date: invoiceData.start_date, | |
end_date: invoiceData.end_date, | |
invoice_custom_count: nextValue, | |
created_at: now.toISOString(), | |
updated_at: now.toISOString(), | |
}) | |
.select() | |
.single(); | |
if (error) throwCustomError(error.message); | |
if (!data) throwInternalCustomError("Failed to insert the invoice"); | |
return data; | |
}; | |
// Insert pericia invoices | |
const insertPericiaInvoices = async (invoiceId, pericias, supabaseClient) => { | |
const periciaInvoicesToInsert = pericias.map((pericia) => ({ | |
invoice_id: invoiceId, | |
costumer_id: pericia.costumer_id, | |
pericia_id: pericia.id, | |
car_id: pericia.car_id, | |
})); | |
const { error } = await supabaseClient.from("pericia_invoices").insert(periciaInvoicesToInsert); | |
if (error) { | |
const { data: deleteInvoiceRes, error: errorDeletingInvoice } = await supabaseClient | |
.from("invoices") | |
.delete() | |
.eq("id", invoiceId); | |
throwInternalCustomError(error.message); | |
} | |
}; | |
// Create the functional composition | |
const createInvoiceFlow = curry(async (req, supabaseClient) => | |
pipe( | |
(req) => (req.method === "OPTIONS" ? new Response("ok", { headers: corsHeaders }) : req), | |
(req) => [req, await supabaseClient.auth.getUser(req.headers.get("Authorization")!)], | |
(req, user) => [req, checkUserRole(user.user_metadata?.role || USER_ROLES.USER)], | |
(req, role) => [req, await parseRequestBody(req)], | |
(req, role, invoiceData) => [ | |
req, | |
role, | |
await supabaseClient.from("invoices").select(`invoice_custom_count`).order("invoice_custom_count", { ascending: false }), | |
new Date(), | |
invoiceData, | |
], | |
(req, role, invoicesCount, now, invoiceData) => [ | |
req, | |
role, | |
invoicesCount[0]?.invoice_custom_count ? invoicesCount[0].invoice_custom_count + 1 : 1, | |
now, | |
invoiceData, | |
], | |
(req, role, nextValue, now, invoiceData) => [ | |
req, | |
role, | |
nextValue, | |
now, | |
await insertInvoice(nextValue, now, invoiceData, supabaseClient), | |
invoiceData.pericias, | |
], | |
(req, role, nextValue, now, invoice, pericias) => [ | |
req, | |
role, | |
nextValue, | |
now, | |
invoice, | |
await insertPericiaInvoices(invoice.id, pericias, supabaseClient), | |
], | |
(req, role, nextValue, now, invoice) => [ | |
new Response(JSON.stringify({ id: invoice.id }), { | |
headers: { "Content-Type": "application/json", ...corsHeaders }, | |
status: 200, | |
}), | |
] | |
)(req) | |
); | |
// Create the server | |
const server = serve(async (req) => { | |
try { | |
return await createInvoiceFlow(req, createNewSupabaseClient(Deno.env.get("SUPABASE_URL") ?? "", Deno.env.get("SUPABASE_ANON_KEY") ?? "")); | |
} catch (error) { | |
return handleErrorResponse(error); | |
} | |
}); | |
// Start the server | |
for await (const request of server) { | |
request.respond(request); | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment