diff --git a/src/storage/object/copy.js b/src/storage/object/copy.js index 0d2a918..9196f3c 100644 --- a/src/storage/object/copy.js +++ b/src/storage/object/copy.js @@ -43,7 +43,7 @@ export const copyFile = async (client, daCtx, sourceKey, details, isRename) => { Version: crypto.randomUUID(), Timestamp: `${Date.now()}`, Users: JSON.stringify(daCtx.users), - Path: details.destination, + Path: Key, }; input.MetadataDirective = 'REPLACE'; } @@ -57,6 +57,10 @@ export const copyFile = async (client, daCtx, sourceKey, details, isRename) => { }; export default async function copyObject(env, daCtx, details, isRename) { + if (details.source === details.destination) { + return { body: '', status: 409 }; + } + const config = getS3Config(env); const client = new S3Client(config); const input = buildInput(daCtx.org, details.source); diff --git a/test/storage/object/copy.test.js b/test/storage/object/copy.test.js index 1f9d60b..fca7079 100644 --- a/test/storage/object/copy.test.js +++ b/test/storage/object/copy.test.js @@ -1,67 +1,89 @@ -import assert from 'assert'; -import esmock from 'esmock'; -import { copyFile } from '../../../src/storage/object/copy.js'; - +/* + * Copyright 2024 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the 'License'); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ +import assert from 'node:assert'; +import { CopyObjectCommand, ListObjectsV2Command, S3Client } from '@aws-sdk/client-s3'; import { mockClient } from 'aws-sdk-client-mock'; -import { S3Client } from '@aws-sdk/client-s3'; + +import copyObject from '../../../src/storage/object/copy.js'; const s3Mock = mockClient(S3Client); describe('Object copy', () => { + beforeEach(() => { + s3Mock.reset(); + }); + + it('does not allow copying to the same location', async () => { + const details = { + source: 'mydir', + destination: 'mydir', + }; + const resp = await copyObject({}, {}, details, false); + assert.strictEqual(resp.status, 409); + }); + it('Copies a file', async () => { + s3Mock.on(ListObjectsV2Command).resolves({ Contents: [{ Key: 'mydir/xyz.html' }] }); + const s3Sent = []; - const mockS3Client = { - send(command) { - s3Sent.push(command); - } - }; + s3Mock.on(CopyObjectCommand).callsFake((input => { + s3Sent.push(input); + })); const ctx = { org: 'foo', - users: [{email: "haha@foo.com"}], + users: [{email: 'haha@foo.com'}], }; - const sourceKey = 'mydir/xyz.html'; const details = { source: 'mydir', destination: 'mydir/newdir', }; - copyFile(mockS3Client, ctx, sourceKey, details, false); + await copyObject({}, ctx, details, false); - assert.equal(1, s3Sent.length); - const input = s3Sent[0].input; - assert.equal('foo-content', input.Bucket); - assert.equal('foo-content/mydir/xyz.html', input.CopySource); - assert.equal('mydir/newdir/xyz.html', input.Key); + assert.strictEqual(s3Sent.length, 3); + const input = s3Sent[0]; + assert.strictEqual(input.Bucket, 'foo-content'); + assert.strictEqual(input.CopySource, 'foo-content/mydir/xyz.html'); + assert.strictEqual(input.Key, 'mydir/newdir/xyz.html'); const md = input.Metadata; assert(md.ID, "ID should be set"); assert(md.Version, "Version should be set"); - assert.equal('string', typeof(md.Timestamp), "Timestamp should be set as a string"); - assert.equal(md.Users, '[{"email":"haha@foo.com"}]'); - assert.equal(md.Path, 'mydir/newdir'); + assert.strictEqual(typeof(md.Timestamp), 'string', 'Timestamp should be set as a string'); + assert.strictEqual(md.Users, '[{"email":"haha@foo.com"}]'); + assert.strictEqual(md.Path, 'mydir/newdir/xyz.html'); }); it('Copies a file for rename', async () => { + s3Mock.on(ListObjectsV2Command).resolves({ Contents: [{ Key: 'mydir/dir1/myfile.html' }] }); + const s3Sent = []; - const mockS3Client = { - send(command) { - s3Sent.push(command); - } - }; + s3Mock.on(CopyObjectCommand).callsFake((input => { + s3Sent.push(input); + })); const ctx = { org: 'testorg' }; - const sourceKey = 'mydir/dir1/myfile.html'; const details = { source: 'mydir/dir1', destination: 'mydir/dir2', }; - copyFile(mockS3Client, ctx, sourceKey, details, true); + await copyObject({}, ctx, details, true); + - assert.equal(1, s3Sent.length); - const input = s3Sent[0].input; - assert.equal('testorg-content', input.Bucket); - assert.equal('testorg-content/mydir/dir1/myfile.html', input.CopySource); - assert.equal('mydir/dir2/myfile.html', input.Key); - assert(input.Metadata === undefined, "Should not redefine metadata for rename"); + assert.strictEqual(s3Sent.length, 3); + const input = s3Sent[0]; + assert.strictEqual(input.Bucket, 'testorg-content'); + assert.strictEqual(input.CopySource, 'testorg-content/mydir/dir1/myfile.html'); + assert.strictEqual(input.Key, 'mydir/dir2/myfile.html'); + assert.ifError(input.Metadata); }); });