From 7752e0ce7d4f4b27566c8ff43bb11e8ed2529583 Mon Sep 17 00:00:00 2001 From: Marc Scholten Date: Fri, 4 Feb 2022 09:28:05 +0100 Subject: [PATCH] Added a batch update operation to DataSync --- IHP/DataSync/Controller.hs | 31 ++++++++++++++++++++++++++++++- IHP/DataSync/Types.hs | 2 ++ lib/IHP/DataSync/ihp-datasync.js | 22 ++++++++++++++++++++++ 3 files changed, 54 insertions(+), 1 deletion(-) diff --git a/IHP/DataSync/Controller.hs b/IHP/DataSync/Controller.hs index ae934fb7c..105083011 100644 --- a/IHP/DataSync/Controller.hs +++ b/IHP/DataSync/Controller.hs @@ -208,7 +208,36 @@ instance ( case result of [record] -> sendJSON DidUpdateRecord { requestId, record } - otherwise -> error "Unexpected result in CreateRecordMessage handler" + otherwise -> error "Unexpected result in UpdateRecordMessage handler" + + pure () + + handleMessage UpdateRecordsMessage { table, ids, patch, requestId } = do + ensureRLSEnabled table + + let columns = patch + |> HashMap.keys + |> map fieldNameToColumnName + |> map PG.Identifier + + let values = patch + |> HashMap.elems + |> map aesonValueToPostgresValue + + let keyValues = zip columns values + + let setCalls = keyValues + |> map (\_ -> "? = ?") + |> ByteString.intercalate ", " + let query = "UPDATE ? SET " <> setCalls <> " WHERE id IN ? RETURNING *" + + let params = [PG.toField (PG.Identifier table)] + <> (join (map (\(key, value) -> [PG.toField key, value]) keyValues)) + <> [PG.toField (PG.In ids)] + + records <- sqlQueryWithRLS (PG.Query query) params + + sendJSON DidUpdateRecords { requestId, records } pure () diff --git a/IHP/DataSync/Types.hs b/IHP/DataSync/Types.hs index 3c795a589..7f6db67cc 100644 --- a/IHP/DataSync/Types.hs +++ b/IHP/DataSync/Types.hs @@ -14,6 +14,7 @@ data DataSyncMessage | CreateRecordMessage { table :: !Text, record :: !(HashMap Text Value), requestId :: !Int } | CreateRecordsMessage { table :: !Text, records :: ![HashMap Text Value], requestId :: !Int } | UpdateRecordMessage { table :: !Text, id :: !UUID, patch :: !(HashMap Text Value), requestId :: !Int } + | UpdateRecordsMessage { table :: !Text, ids :: ![UUID], patch :: !(HashMap Text Value), requestId :: !Int } | DeleteRecordMessage { table :: !Text, id :: !UUID, requestId :: !Int } deriving (Eq, Show) @@ -29,6 +30,7 @@ data DataSyncResponse | DidCreateRecord { requestId :: !Int, record :: ![Field] } -- ^ Response to 'CreateRecordMessage' | DidCreateRecords { requestId :: !Int, records :: ![[Field]] } -- ^ Response to 'CreateRecordsMessage' | DidUpdateRecord { requestId :: !Int, record :: ![Field] } -- ^ Response to 'UpdateRecordMessage' + | DidUpdateRecords { requestId :: !Int, records :: ![[Field]] } -- ^ Response to 'UpdateRecordsMessage' | DidDeleteRecord { requestId :: !Int } data Subscription = Subscription { id :: !UUID, channelSubscription :: !PGListener.Subscription } diff --git a/lib/IHP/DataSync/ihp-datasync.js b/lib/IHP/DataSync/ihp-datasync.js index 44e64c354..098d550d9 100644 --- a/lib/IHP/DataSync/ihp-datasync.js +++ b/lib/IHP/DataSync/ihp-datasync.js @@ -376,6 +376,28 @@ export async function updateRecord(table, id, patch) { } } +export async function updateRecords(table, ids, patch) { + if (typeof table !== "string") { + throw new Error(`Table name needs to be a string, you passed ${JSON.stringify(table)} in a call to updateRecords(${JSON.stringify(table)}, ${JSON.stringify(ids)}, ${JSON.stringify(patch, null, 4)})`); + } + if (!Array.isArray(ids)) { + throw new Error(`IDs need to be an array, you passed ${JSON.stringify(ids)} in a call to updateRecords(${JSON.stringify(table)}, ${JSON.stringify(ids)}, ${JSON.stringify(patch, null, 4)})`); + } + if (patch !== Object(patch)) { + throw new Error(`Patch needs to be an object, you passed ${JSON.stringify(patch)} in a call to updateRecords(${JSON.stringify(table)}, ${JSON.stringify(ids)}, ${JSON.stringify(patch, null, 4)})`); + } + + const request = { tag: 'UpdateRecordsMessage', table, ids, patch }; + + try { + const response = await DataSyncController.getInstance().sendMessage(request); + + return response.records; + } catch (e) { + throw new Error(e.message); + } +} + export async function deleteRecord(table, id) { if (typeof table !== "string") { throw new Error(`Table name needs to be a string, you passed ${JSON.stringify(table)} in a call to deleteRecord(${JSON.stringify(table)}, ${JSON.stringify(id)})`);