-
Notifications
You must be signed in to change notification settings - Fork 0
added s3 extended apis #6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
|
|
||
| import { S3Client, S3ClientConfig } from '@aws-sdk/client-s3'; | ||
| import { ListObjectsV2ExtendedCommand } from '@scality/cloudserverclient/clients/s3Extended'; | ||
|
|
||
| const config: S3ClientConfig = { | ||
| endpoint: 'http://localhost:8000', | ||
| credentials: { | ||
| accessKeyId: 'accessKey1', | ||
| secretAccessKey: 'verySecretKey1', | ||
| }, | ||
| region: 'us-east-1', | ||
| }; | ||
|
|
||
| const client = new S3Client(config); | ||
|
|
||
| const response = await client.send( | ||
| new ListObjectsV2ExtendedCommand({ | ||
| Bucket: 'aBucketName', | ||
| Query: 'content-length > 0', | ||
| }), | ||
| ); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| import { | ||
| ListObjectsCommand, | ||
| ListObjectsCommandInput, | ||
| ListObjectsV2Command, | ||
| ListObjectsV2CommandInput, | ||
| ListObjectVersionsCommand, | ||
| ListObjectVersionsCommandInput | ||
| } from '@aws-sdk/client-s3'; | ||
|
|
||
| const extendCommandWithExtraParametersMiddleware = (query: string) => | ||
| (next: any) => async (args: any) => { | ||
|
Check warning on line 11 in src/clients/s3Extended.ts
|
||
| const request = args.request as any; | ||
| if (request.query) { | ||
| request.query.search = query; | ||
| } else { | ||
| request.query = { search: query }; | ||
| } | ||
| return next(args); | ||
| }; | ||
|
|
||
| export interface ListObjectsExtendedInput extends ListObjectsCommandInput { | ||
| Query: string; | ||
| } | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. don't we also support an extra
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll rethink about this later when i do the follow up on backbeat where I will have to think about it. |
||
|
|
||
| export class ListObjectsExtendedCommand extends ListObjectsCommand { | ||
| constructor(input: ListObjectsExtendedInput) { | ||
| super(input); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shoud we pass the whole
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yeah its ok and tested, and I don't see what non complicated alternatives we would have |
||
|
|
||
| this.middlewareStack.add( | ||
| extendCommandWithExtraParametersMiddleware(input.Query), | ||
| { step: 'build', name: 'extendCommandWithExtraParameters' } | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| export interface ListObjectsV2ExtendedInput extends ListObjectsV2CommandInput { | ||
| Query: string; | ||
| } | ||
|
|
||
| export class ListObjectsV2ExtendedCommand extends ListObjectsV2Command { | ||
| constructor(input: ListObjectsV2ExtendedInput) { | ||
| super(input); | ||
|
|
||
| this.middlewareStack.add( | ||
| extendCommandWithExtraParametersMiddleware(input.Query), | ||
| { step: 'build', name: 'extendCommandWithExtraParameters' } | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| export interface ListObjectVersionsExtendedInput extends ListObjectVersionsCommandInput { | ||
| Query: string; | ||
| } | ||
|
|
||
| export class ListObjectVersionsExtendedCommand extends ListObjectVersionsCommand { | ||
| constructor(input: ListObjectVersionsExtendedInput) { | ||
| super(input); | ||
|
|
||
| this.middlewareStack.add( | ||
| extendCommandWithExtraParametersMiddleware(input.Query), | ||
| { step: 'build', name: 'extendCommandWithExtraParameters' } | ||
| ); | ||
| } | ||
| } | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. maybe an exercise for later, but I think we could factorize this, and use advanced typescript to "generate" the new class by adding a so we would end up with something like: (not required/blocking, and may not even work ; but I think this is possible and could help with maintenance..... though not sure at what cost)
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I just did a quick test with ai : it is possible to do something like this. If we had a lot of s3 command to exports it would probably be worth it, but here it just adding a lot of complexity (especially if we want to later handle multiple new parameters, + body vs query parameter) and doesn't seem worth it. |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,5 @@ | ||
| export * from './clients/backbeatRoutes'; | ||
| export * from './clients/bucketQuota'; | ||
| export * from './clients/s3Extended'; | ||
| export { CloudserverClient, CloudserverClientConfig } from './clients/cloudserver'; | ||
| export * from './utils'; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| enum BackendType { | ||
| MONGO = 'mongo', | ||
| METADATA = 'metadata', | ||
| } | ||
|
|
||
| export function describeForMongoBackend(name: string, fn: () => void): void { | ||
| if (process.env.BACKEND_TYPE === BackendType.METADATA) { | ||
| describe.skip(`${name} (tests skipped: mongo backend only)`, fn); | ||
| } else { | ||
| describe(name, fn); | ||
| } | ||
| } | ||
|
|
||
| export function describeForMetadataBackend(name: string, fn: () => void): void { | ||
| if (process.env.BACKEND_TYPE === BackendType.METADATA) { | ||
| describe(name, fn); | ||
| } else { | ||
| describe.skip(`${name} (tests skipped: metadata backend only)`, fn); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -11,8 +11,9 @@ import { | |
| } from '../src/index'; | ||
| import assert from 'assert'; | ||
| import { createTestClient, testConfig } from './testSetup'; | ||
| import { describeForMetadataBackend } from './testHelpers'; | ||
|
|
||
| describe('CloudServer Lifecycle API Tests', () => { | ||
| describeForMetadataBackend('CloudServer Lifecycle API Tests', () => { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think maybe you can call (it works with mocha, not sure about jest)
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I just tried this also, it's not super nice compared with existing solution because we have to do it in a before all, so we end up with 2 before all which i wanna avoid. Also with this, all the tests end up running, but are stopped early, so in the logs we see all tests as "run and skipped", instead of just skipping the whole test file with the describe |
||
| let backbeatRoutesClient: BackbeatRoutesClient; | ||
|
|
||
| beforeAll(() => { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,135 @@ | ||
| import { S3Client, PutObjectCommand, DeleteObjectCommand } from '@aws-sdk/client-s3'; | ||
| import { createTestClient, testConfig } from './testSetup'; | ||
| import { describeForMongoBackend } from './testHelpers'; | ||
| import assert from 'assert'; | ||
| import { | ||
| ListObjectsExtendedCommand, | ||
| ListObjectsV2ExtendedCommand, | ||
| ListObjectVersionsExtendedCommand, | ||
| } from '../src/clients/s3Extended'; | ||
|
|
||
| describeForMongoBackend('S3 Extended API Tests', () => { | ||
| let s3client: S3Client; | ||
| const key2ndObject = `${testConfig.objectKey}2nd`; | ||
| const body2ndObject = `${testConfig.objectData}2nd`; | ||
|
|
||
| beforeAll(async () => { | ||
| const testClients = createTestClient(); | ||
| s3client = testClients.s3client; | ||
|
|
||
| const putObjectCommand = new PutObjectCommand({ | ||
| Bucket: testConfig.bucketName, | ||
| Key: key2ndObject, | ||
| Body: body2ndObject, | ||
| }); | ||
| await s3client.send(putObjectCommand); | ||
| }); | ||
|
|
||
| it('should test ListObjectsExtended', async () => { | ||
| const getCommand1= new ListObjectsExtendedCommand({ | ||
| Bucket: testConfig.bucketName, | ||
| Query: `content-length >= ${testConfig.objectData.length}`, | ||
| MaxKeys: 5 | ||
| }); | ||
| const getData1 = await s3client.send(getCommand1); | ||
| assert.strictEqual(getData1.Contents?.length, 2); | ||
|
|
||
| const maxKey = 1; | ||
| const getCommand2= new ListObjectsExtendedCommand({ | ||
| Bucket: testConfig.bucketName, | ||
| Query: `content-length >= ${testConfig.objectData.length}`, | ||
| MaxKeys: maxKey | ||
| }); | ||
| const getData2 = await s3client.send(getCommand2); | ||
| assert.strictEqual(getData2.Contents?.length, maxKey); | ||
| assert.strictEqual(getData2.IsTruncated, true); | ||
|
|
||
| const getCommand3 = new ListObjectsExtendedCommand({ | ||
| Bucket: testConfig.bucketName, | ||
| Query: `content-length > ${testConfig.objectData.length}`, | ||
| MaxKeys: 5 | ||
| }); | ||
| const getData3 = await s3client.send(getCommand3); | ||
| assert.strictEqual(getData3.Contents?.length, 1); | ||
| assert.strictEqual(getData3.Contents[0].Key, key2ndObject); | ||
|
|
||
| const getCommand4 = new ListObjectsExtendedCommand({ | ||
| Bucket: testConfig.bucketName, | ||
| Query: `key = ${key2ndObject}`, | ||
| MaxKeys: 5 | ||
| }); | ||
| const getData4 = await s3client.send(getCommand4); | ||
| assert.strictEqual(getData4.Contents?.length, 1); | ||
| assert.strictEqual(getData4.Contents[0].Key, key2ndObject); | ||
|
|
||
| const getCommand5 = new ListObjectsExtendedCommand({ | ||
| Bucket: testConfig.bucketName, | ||
| Query: `key = iDontExists`, | ||
| MaxKeys: 5 | ||
| }); | ||
| const getData5 = await s3client.send(getCommand5); | ||
| assert.strictEqual(getData5.Contents, undefined); | ||
| }); | ||
|
|
||
| it('should test ListObjectsV2Extended', async () => { | ||
| const getCommand1= new ListObjectsV2ExtendedCommand({ | ||
| Bucket: testConfig.bucketName, | ||
| Query: `content-length >= ${testConfig.objectData.length}`, | ||
| MaxKeys: 5, | ||
| FetchOwner: true, | ||
| }); | ||
| const getData1 = await s3client.send(getCommand1); | ||
| assert.strictEqual(getData1.Contents?.length, 2); | ||
| assert.strictEqual(getData1.KeyCount, 2); | ||
| assert.strictEqual(getData1.Contents[0].Owner?.DisplayName, 'Bart'); | ||
|
|
||
| const getCommand2 = new ListObjectsV2ExtendedCommand({ | ||
| Bucket: testConfig.bucketName, | ||
| Query: `content-length >= 0`, | ||
| MaxKeys: 5, | ||
| StartAfter: testConfig.objectKey, // Skip first object | ||
| }); | ||
| const getData2 = await s3client.send(getCommand2); | ||
| assert.strictEqual(getData2.Contents?.length, 1); | ||
| assert.strictEqual(getData2.Contents[0].Key, key2ndObject); | ||
| }); | ||
|
|
||
| it('should test ListObjectVersionsExtended', async () => { | ||
| const getCommand1 = new ListObjectVersionsExtendedCommand({ | ||
| Bucket: testConfig.bucketName, | ||
| Query: `content-length >= 0`, | ||
| MaxKeys: 100, | ||
| }); | ||
| const getData1 = await s3client.send(getCommand1); | ||
| assert.strictEqual(getData1.Versions?.length, 2); | ||
| assert.strictEqual(getData1.Versions[0].IsLatest, true); | ||
|
|
||
| // Delete one object to create a DeleteMarker | ||
| const deleteCommand = new DeleteObjectCommand({ | ||
| Bucket: testConfig.bucketName, | ||
| Key: key2ndObject, | ||
| }); | ||
| await s3client.send(deleteCommand); | ||
|
|
||
| const getCommand2 = new ListObjectVersionsExtendedCommand({ | ||
| Bucket: testConfig.bucketName, | ||
| Query: `content-length >= 0`, | ||
| MaxKeys: 100, | ||
| }); | ||
| const getData2 = await s3client.send(getCommand2); | ||
| assert.strictEqual(getData2.DeleteMarkers?.[0].Key, key2ndObject); | ||
| assert.ok(getData2.DeleteMarkers?.[0].VersionId); | ||
|
|
||
| assert.ok(getData2.Versions); | ||
| const firstVersion = getData2.Versions[0]; | ||
| const getCommand3 = new ListObjectVersionsExtendedCommand({ | ||
| Bucket: testConfig.bucketName, | ||
| Query: `content-length >= 0`, | ||
| KeyMarker: firstVersion.Key, | ||
| VersionIdMarker: firstVersion.VersionId, | ||
| MaxKeys: 100, | ||
| }); | ||
| const getData3 = await s3client.send(getCommand3); | ||
| assert.notStrictEqual(getData3.Versions?.[0]?.Key, firstVersion.Key); | ||
| }); | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can this be typed (avoid
any)?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Technically yes but the type here is very convoluted, requires importing stuff from aws sdk types that we don't really understand, I think it's ok this way