Skip to content

Instantly share code, notes, and snippets.

@diasjuniorr
Last active November 3, 2023 16:55
Show Gist options
  • Save diasjuniorr/9fd9d199e78787ee45aff25ca2cc3f30 to your computer and use it in GitHub Desktop.
Save diasjuniorr/9fd9d199e78787ee45aff25ca2cc3f30 to your computer and use it in GitHub Desktop.
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