creating new hook, coupling completion of presigned url and upload, to avoid race condition

This commit is contained in:
Khalim Conn-Kowlessar 2026-02-11 12:00:30 +00:00
parent 5e34c41bd7
commit 41e59b0aca

View file

@ -123,47 +123,35 @@ function useCreateRemoteAssessment({
const { assetListFileKey, valuationDataFileKey } = useMemo(
() => generateS3Keys(userId, portfolioId),
[userId, portfolioId]
[userId, portfolioId],
);
const uploadMutation = useMutation({
mutationFn: uploadCsvToS3,
});
const uploadFileMutation = useMutation({
mutationFn: async ({
userId,
portfolioId,
fileKey,
csvFile,
}: {
userId: string;
portfolioId: string;
fileKey: string;
csvFile: Blob;
}) => {
// 1) Get presigned URL
const { url } = await generatePresignedUrl({
userId,
portfolioId,
fileKey,
});
const presignedMutation = useMutation({
mutationFn: generatePresignedUrl,
onSuccess: (data) => {
let csvFile: Blob;
if (data.fileKey === assetListFileKey) {
const assetList: {
uprn: number | null | undefined;
address: string;
postcode: string;
property_type?: string;
built_form?: string;
}[] = [
{
uprn,
address: addressLineOne,
postcode,
},
];
// 2) Upload to S3
await uploadCsvToS3({
presignedUrl: url,
file: csvFile,
});
// if we have property type and built form, include them. Handle typescript optionality
if (propertyType) {
assetList[0]["property_type"] = propertyType;
}
if (builtForm) {
assetList[0]["built_form"] = builtForm;
}
csvFile = new Blob([convertToCSV(assetList)], { type: "text/csv" });
} else {
const valuationData = [{ uprn, valuation }];
csvFile = new Blob([convertToCSV(valuationData)], { type: "text/csv" });
}
uploadMutation.mutate({ file: csvFile, presignedUrl: data.url });
return true;
},
});
@ -196,27 +184,51 @@ function useCreateRemoteAssessment({
}
async function handleSubmit(formData: RemoteAssessmentFormValues) {
// Build asset CSV
const assetList = [
{
uprn,
address: addressLineOne,
postcode,
...(propertyType && { property_type: propertyType }),
...(builtForm && { built_form: builtForm }),
},
];
const assetCsv = new Blob([convertToCSV(assetList)], {
type: "text/csv",
});
// Build valuation CSV
const valuationCsv = new Blob([convertToCSV([{ uprn, valuation }])], {
type: "text/csv",
});
// Upload asset list and valuation data and wait
await Promise.all([
presignedMutation.mutateAsync({
uploadFileMutation.mutateAsync({
userId,
portfolioId,
fileKey: assetListFileKey,
csvFile: assetCsv,
}),
presignedMutation.mutateAsync({
uploadFileMutation.mutateAsync({
userId,
portfolioId,
fileKey: valuationDataFileKey,
csvFile: valuationCsv,
}),
]);
// Now trigger engine (files guaranteed in S3)
await triggerEngine(formData);
}
return {
handleSubmit,
triggerEngine,
isUploading: uploadMutation.isLoading || presignedMutation.isLoading,
hasError: uploadMutation.isError || presignedMutation.isError,
isUploading: uploadFileMutation.isPending,
hasError: uploadFileMutation.isError,
};
}