diff --git a/lib/actions/holder-actions.js b/lib/actions/holder-actions.js --- a/lib/actions/holder-actions.js +++ b/lib/actions/holder-actions.js @@ -1,6 +1,11 @@ // @flow +import blobService from '../facts/blob-service.js'; +import type { AuthMetadata } from '../shared/identity-client-context.js'; import type { BlobHashAndHolder } from '../types/holder-types.js'; +import { toBase64URL } from '../utils/base64.js'; +import { makeBlobServiceEndpointURL } from '../utils/blob-service.js'; +import { createDefaultHTTPRequestHeaders } from '../utils/services-utils.js'; type MultipleBlobHolders = $ReadOnlyArray; export const storeEstablishedHolderActionType = 'STORE_ESTABLISHED_HOLDER'; @@ -21,3 +26,87 @@ notAdded: MultipleBlobHolders, notRemoved: MultipleBlobHolders, }; + +// This function can be simplified when batch holders operations +// are implemented on Blob Service +async function performBlobServiceHolderActions( + action: 'establish' | 'remove', + inputs: MultipleBlobHolders, + authMetadata: AuthMetadata, +): Promise<{ + succeeded: MultipleBlobHolders, + failed: MultipleBlobHolders, +}> { + if (inputs.length === 0) { + return { succeeded: [], failed: [] }; + } + + const assignHolderEndpoint = + action === 'establish' + ? blobService.httpEndpoints.ASSIGN_HOLDER + : blobService.httpEndpoints.DELETE_BLOB; + const endpointURL = makeBlobServiceEndpointURL(assignHolderEndpoint); + const defaultHeaders = createDefaultHTTPRequestHeaders(authMetadata); + + const promises = inputs.map(async input => { + const blobHash = toBase64URL(input.blobHash); + try { + const response = await fetch(endpointURL, { + method: assignHolderEndpoint.method, + body: JSON.stringify({ + holder: input.holder, + blob_hash: blobHash, + }), + headers: { + ...defaultHeaders, + 'content-type': 'application/json', + }, + }); + const holderAlreadyEstablishedResponse = + action === 'establish' && response.status === 409; + if (response.ok || holderAlreadyEstablishedResponse) { + return { ...input, success: true }; + } + return { ...input, success: false }; + } catch (e) { + return { ...input, success: false }; + } + }); + + const results = await Promise.all(promises); + const succeeded = results + .filter(it => it.success) + .map(({ success, ...rest }) => rest); + const failed = results + .filter(it => !it.success) + .map(({ success, ...rest }) => rest); + + return { + succeeded, + failed, + }; +} + +async function processHoldersAction( + input: ProcessHoldersStartedPayload, + authMetadata: AuthMetadata, +): Promise { + const [ + { succeeded: added, failed: notAdded }, + { succeeded: removed, failed: notRemoved }, + ] = await Promise.all([ + performBlobServiceHolderActions( + 'establish', + input.holdersToAdd, + authMetadata, + ), + performBlobServiceHolderActions( + 'remove', + input.holdersToRemove, + authMetadata, + ), + ]); + return { added, notAdded, removed, notRemoved }; +} + +export { processHoldersAction };