diff --git a/app/src/services/object.js b/app/src/services/object.js index 3b9c1735..3bcc163d 100644 --- a/app/src/services/object.js +++ b/app/src/services/object.js @@ -210,6 +210,30 @@ const service = { } }, + /** + * @function exists + * Checks if an object db record exists + * @param {string} objId The object uuid to read + * @param {object} [etrx=undefined] An optional Objection Transaction object + * @returns {Promise} true if object exists in db, false otherwise + * @throws The error encountered upon db transaction failure + */ + exists: async (objId, etrx = undefined) => { + let trx; + try { + trx = etrx ? etrx : await ObjectModel.startTransaction(); + + const response = await ObjectModel.query(trx).findById(objId); + + if (!etrx) await trx.commit(); + + return Promise.resolve(response) ? true : false; + } catch (err) { + if (!etrx && trx) await trx.rollback(); + throw err; + } + }, + /** * @function update * Update an object DB record diff --git a/app/src/services/sync.js b/app/src/services/sync.js index 1477e3d2..6598ff99 100644 --- a/app/src/services/sync.js +++ b/app/src/services/sync.js @@ -21,10 +21,12 @@ const service = { * @function _deriveObjectId * Checks an S3 Object for any previous `coms-id` tag traces and returns it if found. * Otherwise it writes a new `coms-id` to the S3 Object if none were previously found. + * If the `coms-id` conflicts with an existing COMS object, it is replaced with a new one. * @param {object | boolean} s3Object The result of an S3 HeadObject operation * @param {string} path String representing the canonical path for the specified object * @param {string | null} bucketId uuid of bucket or `null` if syncing object in default bucket - * @returns {Promise} Resolves to an existing objectId or creates a new one + * @returns {Promise} Resolves to an existing objectId (if not already in COMS) + * or creates a new one */ _deriveObjectId: async (s3Object, path, bucketId) => { let objId = uuidv4(); @@ -32,12 +34,21 @@ const service = { if (typeof s3Object === 'object') { // If regular S3 Object const TagSet = await storageService.getObjectTagging({ filePath: path, bucketId: bucketId }) .then(result => result.TagSet ?? []); + const s3ObjectComsId = TagSet.find(obj => (obj.Key === 'coms-id'))?.Value; - if (s3ObjectComsId && uuidValidate(s3ObjectComsId)) { + if (s3ObjectComsId + && uuidValidate(s3ObjectComsId) + && !(await objectService.exists(s3ObjectComsId))) { + // re-use existing coms-id (if no conflict) objId = s3ObjectComsId; - } else { // Update S3 Object if there is still remaining space in the TagSet - if (TagSet.length < 10) { // putObjectTagging replaces S3 tags so new TagSet must contain existing values + } else { + // remove `coms-id` tag since it conflicts with an existing COMS object + TagSet.filter(x => x.Key != 'coms-id'); + + // Update S3 Object if there is still remaining space in the TagSet + if (TagSet.length < 10) { + // putObjectTagging replaces S3 tags so new TagSet must contain existing values await storageService.putObjectTagging({ filePath: path, bucketId: bucketId, @@ -175,7 +186,7 @@ const service = { // Case: not in COMS - insert new COMS object else { - const objId = await service._deriveObjectId(s3Object, path, bucketId); + let objId = await service._deriveObjectId(s3Object, path, bucketId); response = await objectService.create({ id: objId,