Skip to content

Commit

Permalink
Merge pull request #58 from veselin-angelov/feature/copy-object
Browse files Browse the repository at this point in the history
feat: added copy object method
  • Loading branch information
MartinAndreev authored Mar 15, 2024
2 parents 7272be6 + 7664b6a commit 2dd92be
Show file tree
Hide file tree
Showing 9 changed files with 103 additions and 38 deletions.
22 changes: 11 additions & 11 deletions site/docs/api/classes/BucketsService.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ custom_edit_url: null

#### Defined in

[services/buckers.service.ts:34](https://github.com/LabO8/nestjs-s3/blob/65a196f/src/services/buckers.service.ts#L34)
[services/buckets.service.ts:34](https://github.com/LabO8/nestjs-s3/blob/65a196f/src/services/buckers.service.ts#L34)

## Methods

Expand All @@ -41,7 +41,7 @@ custom_edit_url: null

#### Defined in

[services/buckers.service.ts:36](https://github.com/LabO8/nestjs-s3/blob/65a196f/src/services/buckers.service.ts#L36)
[services/buckets.service.ts:36](https://github.com/LabO8/nestjs-s3/blob/65a196f/src/services/buckers.service.ts#L36)

___

Expand All @@ -61,7 +61,7 @@ ___

#### Defined in

[services/buckers.service.ts:48](https://github.com/LabO8/nestjs-s3/blob/65a196f/src/services/buckers.service.ts#L48)
[services/buckets.service.ts:48](https://github.com/LabO8/nestjs-s3/blob/65a196f/src/services/buckers.service.ts#L48)

___

Expand All @@ -81,7 +81,7 @@ ___

#### Defined in

[services/buckers.service.ts:60](https://github.com/LabO8/nestjs-s3/blob/65a196f/src/services/buckers.service.ts#L60)
[services/buckets.service.ts:60](https://github.com/LabO8/nestjs-s3/blob/65a196f/src/services/buckers.service.ts#L60)

___

Expand All @@ -95,7 +95,7 @@ ___

#### Defined in

[services/buckers.service.ts:56](https://github.com/LabO8/nestjs-s3/blob/65a196f/src/services/buckers.service.ts#L56)
[services/buckets.service.ts:56](https://github.com/LabO8/nestjs-s3/blob/65a196f/src/services/buckers.service.ts#L56)

___

Expand All @@ -115,7 +115,7 @@ ___

#### Defined in

[services/buckers.service.ts:66](https://github.com/LabO8/nestjs-s3/blob/65a196f/src/services/buckers.service.ts#L66)
[services/buckets.service.ts:66](https://github.com/LabO8/nestjs-s3/blob/65a196f/src/services/buckers.service.ts#L66)

___

Expand All @@ -136,7 +136,7 @@ ___

#### Defined in

[services/buckers.service.ts:119](https://github.com/LabO8/nestjs-s3/blob/65a196f/src/services/buckers.service.ts#L119)
[services/buckets.service.ts:119](https://github.com/LabO8/nestjs-s3/blob/65a196f/src/services/buckers.service.ts#L119)

___

Expand All @@ -157,7 +157,7 @@ ___

#### Defined in

[services/buckers.service.ts:83](https://github.com/LabO8/nestjs-s3/blob/65a196f/src/services/buckers.service.ts#L83)
[services/buckets.service.ts:83](https://github.com/LabO8/nestjs-s3/blob/65a196f/src/services/buckers.service.ts#L83)

___

Expand All @@ -178,7 +178,7 @@ ___

#### Defined in

[services/buckers.service.ts:74](https://github.com/LabO8/nestjs-s3/blob/65a196f/src/services/buckers.service.ts#L74)
[services/buckets.service.ts:74](https://github.com/LabO8/nestjs-s3/blob/65a196f/src/services/buckers.service.ts#L74)

___

Expand All @@ -199,7 +199,7 @@ ___

#### Defined in

[services/buckers.service.ts:107](https://github.com/LabO8/nestjs-s3/blob/65a196f/src/services/buckers.service.ts#L107)
[services/buckets.service.ts:107](https://github.com/LabO8/nestjs-s3/blob/65a196f/src/services/buckers.service.ts#L107)

___

Expand All @@ -220,4 +220,4 @@ ___

#### Defined in

[services/buckers.service.ts:95](https://github.com/LabO8/nestjs-s3/blob/65a196f/src/services/buckers.service.ts#L95)
[services/buckets.service.ts:95](https://github.com/LabO8/nestjs-s3/blob/65a196f/src/services/buckers.service.ts#L95)
12 changes: 7 additions & 5 deletions src/helpers/prepare-options.helper.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import { OptionsWithAutoPrefix } from '../types';
import { DisableAutoPrefix, OptionsWithAutoPrefix, PrefixContext } from '../types';

export const prepareOptions = (
options: OptionsWithAutoPrefix,
): {
options: Omit<OptionsWithAutoPrefix, 'disableAutoPrefix'>;
disableAutoPrefix: boolean;
} => {
options: Omit<OptionsWithAutoPrefix, keyof DisableAutoPrefix | keyof PrefixContext>;
} & DisableAutoPrefix &
PrefixContext => {
const disableAutoPrefix = options?.disableAutoPrefix ?? false;
const prefixContext = options?.prefixContext ?? null;

delete options?.disableAutoPrefix;
delete options?.prefixContext;

return { options, disableAutoPrefix };
return { options, disableAutoPrefix, prefixContext };
};
File renamed without changes.
2 changes: 1 addition & 1 deletion src/services/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export * from './buckers.service';
export * from './buckets.service';
export * from './objects.service';
export * from './prefix.service';
export * from './signed-url.service';
Expand Down
58 changes: 50 additions & 8 deletions src/services/objects.service.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import {
CopyObjectCommand,
CopyObjectOutput,
DeleteObjectCommand,
DeleteObjectOutput,
DeleteObjectsCommand,
Expand All @@ -17,11 +19,14 @@ import { Inject, Injectable } from '@nestjs/common';
import * as fs from 'fs';
import { S3_SERVICE } from '../constants';
import {
CopyObjectOptions,
DeleteObjectOptions,
DeleteObjectsOptions,
DisableAutoPrefix,
GetObjectOptions,
ListObjectsOptions,
ListObjectsV2Options,
PrefixContext,
PutObjectOptions,
} from '../types';
import { PrefixService } from './prefix.service';
Expand All @@ -40,13 +45,13 @@ export class ObjectsService {
remote: string,
options?: PutObjectOptions,
): Promise<PutObjectOutput> {
const { disableAutoPrefix, options: preparedOptions } = prepareOptions(options);
const { disableAutoPrefix, prefixContext, options: preparedOptions } = prepareOptions(options);

return this.client.send(
new PutObjectCommand({
Bucket: bucket,
Body: body,
Key: disableAutoPrefix ? remote : this.prefixService.prefix(remote, bucket, options?.prefixContext),
Key: disableAutoPrefix ? remote : this.prefixService.prefix(remote, bucket, prefixContext),
...preparedOptions,
}),
);
Expand All @@ -68,12 +73,12 @@ export class ObjectsService {
remote: string,
options?: DeleteObjectOptions,
): Promise<DeleteObjectOutput> {
const { disableAutoPrefix, options: preparedOptions } = prepareOptions(options);
const { disableAutoPrefix, prefixContext, options: preparedOptions } = prepareOptions(options);

return this.client.send(
new DeleteObjectCommand({
Bucket: bucket,
Key: disableAutoPrefix ? remote : this.prefixService.prefix(remote, bucket, options?.prefixContext),
Key: disableAutoPrefix ? remote : this.prefixService.prefix(remote, bucket, prefixContext),
...preparedOptions,
}),
);
Expand All @@ -84,14 +89,14 @@ export class ObjectsService {
remotes: string[],
options?: DeleteObjectsOptions,
): Promise<DeleteObjectsOutput> {
const { disableAutoPrefix, options: preparedOptions } = prepareOptions(options);
const { disableAutoPrefix, prefixContext, options: preparedOptions } = prepareOptions(options);

return this.client.send(
new DeleteObjectsCommand({
Bucket: bucket,
Delete: {
Objects: remotes.map((r) => ({
Key: disableAutoPrefix ? r : this.prefixService.prefix(r, bucket, options?.prefixContext),
Key: disableAutoPrefix ? r : this.prefixService.prefix(r, bucket, prefixContext),
})),
},
...preparedOptions,
Expand All @@ -100,12 +105,49 @@ export class ObjectsService {
}

public async getObject(bucket: string, remote: string, options?: GetObjectOptions): Promise<GetObjectOutput> {
const { disableAutoPrefix, options: preparedOptions } = prepareOptions(options);
const { disableAutoPrefix, prefixContext, options: preparedOptions } = prepareOptions(options);

return this.client.send(
new GetObjectCommand({
Bucket: bucket,
Key: disableAutoPrefix ? remote : this.prefixService.prefix(remote, bucket, options?.prefixContext),
Key: disableAutoPrefix ? remote : this.prefixService.prefix(remote, bucket, prefixContext),
...preparedOptions,
}),
);
}

public async copyObject(
sourceBucket: string,
sourceKey: string,
destinationBucket: string,
destinationKey: string,
options?: {
sourceOptions?: DisableAutoPrefix & PrefixContext;
destinationOptions?: CopyObjectOptions;
},
): Promise<CopyObjectOutput> {
const sourceOpts = options?.sourceOptions ?? {};
const destOpts = options?.destinationOptions ?? {};

const { disableAutoPrefix: sourceDisableAutoPrefix, prefixContext: sourcePrefixContext } = sourceOpts;
const {
disableAutoPrefix: destDisableAutoPrefix,
prefixContext: destPrefixContext,
options: preparedOptions,
} = prepareOptions(destOpts);

const prefixedSourceKey = sourceDisableAutoPrefix
? sourceKey
: this.prefixService.prefix(sourceKey, sourceBucket, sourcePrefixContext);
const prefixedDestinationKey = destDisableAutoPrefix
? destinationKey
: this.prefixService.prefix(destinationKey, destinationBucket, destPrefixContext);

return this.client.send(
new CopyObjectCommand({
CopySource: encodeURIComponent(`${sourceBucket}/${prefixedSourceKey}`),
Bucket: destinationBucket,
Key: prefixedDestinationKey,
...preparedOptions,
}),
);
Expand Down
16 changes: 8 additions & 8 deletions src/services/signed-url.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ export class SignedUrlService {
expiresIn: number = DEFAULT_EXPIRES_IN,
options?: PutObjectOptions,
): Promise<PutSignedUrl> {
const { disableAutoPrefix, options: preparedOptions } = prepareOptions(options);
const key = disableAutoPrefix ? remote : this.prefixService.prefix(remote, bucket, options?.prefixContext);
const { disableAutoPrefix, prefixContext, options: preparedOptions } = prepareOptions(options);
const key = disableAutoPrefix ? remote : this.prefixService.prefix(remote, bucket, prefixContext);

const command = new PutObjectCommand({
Bucket: bucket,
Expand All @@ -50,11 +50,11 @@ export class SignedUrlService {
expiresIn: number = DEFAULT_EXPIRES_IN,
options?: GetObjectOptions,
): Promise<string> {
const { disableAutoPrefix, options: preparedOptions } = prepareOptions(options);
const { disableAutoPrefix, prefixContext, options: preparedOptions } = prepareOptions(options);

const command = new GetObjectCommand({
Bucket: bucket,
Key: disableAutoPrefix ? remote : this.prefixService.prefix(remote, bucket, options?.prefixContext),
Key: disableAutoPrefix ? remote : this.prefixService.prefix(remote, bucket, prefixContext),
...preparedOptions,
});

Expand All @@ -69,11 +69,11 @@ export class SignedUrlService {
expiresIn: number = DEFAULT_EXPIRES_IN,
options?: DeleteObjectOptions,
): Promise<string> {
const { disableAutoPrefix, options: preparedOptions } = prepareOptions(options);
const { disableAutoPrefix, prefixContext, options: preparedOptions } = prepareOptions(options);

const command = new DeleteObjectCommand({
Bucket: bucket,
Key: disableAutoPrefix ? remote : this.prefixService.prefix(remote, bucket, options?.prefixContext),
Key: disableAutoPrefix ? remote : this.prefixService.prefix(remote, bucket, prefixContext),
...preparedOptions,
});

Expand All @@ -88,13 +88,13 @@ export class SignedUrlService {
expiresIn: number = DEFAULT_EXPIRES_IN,
options?: DeleteObjectsOptions,
): Promise<string> {
const { disableAutoPrefix, options: preparedOptions } = prepareOptions(options);
const { disableAutoPrefix, prefixContext, options: preparedOptions } = prepareOptions(options);

const command = new DeleteObjectsCommand({
Bucket: bucket,
Delete: {
Objects: remotes.map((r) => ({
Key: disableAutoPrefix ? r : this.prefixService.prefix(r, bucket, options?.prefixContext),
Key: disableAutoPrefix ? r : this.prefixService.prefix(r, bucket, prefixContext),
})),
},
...preparedOptions,
Expand Down
10 changes: 9 additions & 1 deletion src/types/object-command-options.type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,14 @@ export type PutObjectOptions = Omit<PutObjectCommandInput, 'Bucket' | 'Body' | '
DisableAutoPrefix &
PrefixContext;
export type DeleteObjectOptions = Omit<DeleteObjectCommandInput, 'Bucket' | 'Key'> & DisableAutoPrefix & PrefixContext;
export type CopyObjectOptions = Omit<PutObjectCommandInput, 'Bucket' | 'Key' | 'CopySource'> &
DisableAutoPrefix &
PrefixContext;
export type ListObjectsOptions = Omit<ListObjectsCommandInput, 'Bucket'>;
export type ListObjectsV2Options = Omit<ListObjectsV2CommandInput, 'Bucket'>;
export type OptionsWithAutoPrefix = PutObjectOptions | DeleteObjectOptions | DeleteObjectsOptions | GetObjectOptions;
export type OptionsWithAutoPrefix =
| PutObjectOptions
| DeleteObjectOptions
| DeleteObjectsOptions
| GetObjectOptions
| CopyObjectOptions;
4 changes: 2 additions & 2 deletions src/utils/deletion.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,15 @@ export class DeletionService {
deleteOptions?: DeleteObjectsOptions,
listOptions?: Omit<ListObjectsV2Options, 'Prefix' | 'ContinuationToken'>,
): Promise<boolean | DeleteObjectOutput[]> {
const { disableAutoPrefix, options: preparedOptions } = prepareOptions(deleteOptions);
const { disableAutoPrefix, prefixContext, options: preparedOptions } = prepareOptions(deleteOptions);

let continuationToken = null;
const result: DeleteObjectOutput[] = [];
let data: ListObjectsV2Output;

do {
data = await this.objectsService.listObjectsV2(bucket, {
Prefix: disableAutoPrefix ? prefix : this.prefixService.prefix(prefix, bucket, deleteOptions?.prefixContext),
Prefix: disableAutoPrefix ? prefix : this.prefixService.prefix(prefix, bucket, prefixContext),
ContinuationToken: continuationToken,
...listOptions,
});
Expand Down
17 changes: 15 additions & 2 deletions tests/e2e/objects-service.e2e.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ describe('Object service', () => {

const bucketName = uuidv4();
const testPath = path.resolve(__dirname, 'data');
const testFiles = ['test.txt', 'test-file-path.txt', 'test-get.txt'];
const testFiles = ['test.txt', 'test-file-path.txt', 'test-get.txt', 'test-file-to-copy.txt', 'test-file-copied.txt'];

beforeAll(async () => {
testingModule = await Test.createTestingModule({
Expand Down Expand Up @@ -79,12 +79,25 @@ describe('Object service', () => {
expect(result).not.toEqual(null);
});

it('should be get an object that exists from a existing bucket', async () => {
it('should get an object that exists from a existing bucket', async () => {
const remote = 'test-get.txt';
await objectService.putObjectFromPath(bucketName, path.resolve(testPath, 'test.txt'), remote);

const object = await objectService.getObject(bucketName, remote);

expect(object.Body).toBeInstanceOf(Stream);
});

it('should be able to copy a file by remote', async () => {
await objectService.putObjectFromPath(bucketName, path.resolve(testPath, 'test.txt'), 'test-file-to-copy.txt');

const result = await objectService.copyObject(
bucketName,
'test-file-to-copy.txt',
bucketName,
'test-file-copied.txt',
);

expect(result).not.toEqual(null);
});
});

0 comments on commit 2dd92be

Please sign in to comment.