Skip to content

Commit

Permalink
refactor selectObjectContent to ts (#1252)
Browse files Browse the repository at this point in the history
  • Loading branch information
prakashsvmx authored Feb 2, 2024
1 parent 953dfa9 commit 6b9845d
Show file tree
Hide file tree
Showing 13 changed files with 346 additions and 532 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ The complete API Reference is available here:
- [set-object-legal-hold.js](https://github.com/minio/minio-js/blob/master/examples/set-object-legalhold.mjs)
- [get-object-legal-hold.js](https://github.com/minio/minio-js/blob/master/examples/get-object-legal-hold.mjs)
- [compose-object.js](https://github.com/minio/minio-js/blob/master/examples/compose-object.js)
- [select-object-content.js](https://github.com/minio/minio-js/blob/master/examples/select-object-content.js)
- [select-object-content.js](https://github.com/minio/minio-js/blob/master/examples/select-object-content.mjs)

#### Presigned Operations

Expand Down
21 changes: 8 additions & 13 deletions docs/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -1603,18 +1603,17 @@ composePromise

<a name="selectObjectContent"></a>

### selectObjectContent(bucketName, objectName, selectOpts[, callback])
### selectObjectContent(bucketName, objectName, selectOpts)

Select contents of an object (S3 Select).

**Parameters**

| Param | Type | Description |
| --------------- | ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| `bucketName` | _string_ | Name of the bucket. |
| `objectName` | _string_ | Name of the object. |
| `selectOpts` | _object_ | |
| `callback(err)` | _function_ | Callback function is called with non `null` value in case of error. If no callback is passed, a `Promise` is returned, with the `SelectResults` type |
| Param | Type | Description |
| ------------ | -------- | ------------------- |
| `bucketName` | _string_ | Name of the bucket. |
| `objectName` | _string_ | Name of the object. |
| `selectOpts` | _object_ | |

**Example 1**
Select all values
Expand All @@ -1631,12 +1630,8 @@ const selectOpts = {
requestProgress: { Enabled: true },
}

minioClient.selectObjectContent('bucketName', 'objectName', selectOpts, function (err, res) {
if (err) {
return console.log('Unable to process select object content.', err.message)
}
console.log('Success')
})
const res = await minioClient.selectObjectContent('bucketName', 'objectName', selectOpts)
console.log(res)
```

## 4. Presigned operations
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,5 @@ const selectRequestConfig = {
// scanRange:{ start:50, end:100 }
}

s3Client.selectObjectContent('my-bucketname', 'my-objectname', selectRequestConfig, function (e, stat) {
if (e) {
return console.log(e)
}
console.log(stat)
})
const stat = await s3Client.selectObjectContent('test-bucket', 'sample_data.csv', selectRequestConfig)
console.log(stat.records.toString())
67 changes: 37 additions & 30 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@
"@nodelib/fs.walk": "^1.2.8",
"@types/async": "^3.2.20",
"@types/block-stream2": "^2.1.2",
"@types/buffer-crc32": "^0.2.4",
"@types/chai": "^4.3.11",
"@types/chai-as-promised": "^7.1.8",
"@types/lodash": "^4.14.194",
Expand Down
81 changes: 80 additions & 1 deletion src/internal/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import xml2js from 'xml2js'

import { CredentialProvider } from '../CredentialProvider.ts'
import * as errors from '../errors.ts'
import type { SelectResults } from '../helpers.ts'
import { DEFAULT_REGION, LEGAL_HOLD_STATUS, RETENTION_MODES, RETENTION_VALIDITY_UNITS } from '../helpers.ts'
import { signV4 } from '../signing.ts'
import { fsp, streamPromise } from './async.ts'
Expand Down Expand Up @@ -74,6 +75,7 @@ import type {
ResponseHeader,
ResultCallback,
Retention,
SelectOptions,
StatObjectOpts,
Tag,
TaggingOpts,
Expand All @@ -83,8 +85,13 @@ import type {
VersionIdentificator,
} from './type.ts'
import type { ListMultipartResult, UploadedPart } from './xml-parser.ts'
import {
parseCompleteMultipart,
parseInitiateMultipart,
parseObjectLegalHoldConfig,
parseSelectObjectContentResponse,
} from './xml-parser.ts'
import * as xmlParsers from './xml-parser.ts'
import { parseCompleteMultipart, parseInitiateMultipart, parseObjectLegalHoldConfig } from './xml-parser.ts'

const xml = new xml2js.Builder({ renderOpts: { pretty: false }, headless: true })

Expand Down Expand Up @@ -2211,4 +2218,76 @@ export class TypedClient {

await this.removeTagging({ bucketName, objectName, removeOpts })
}

async selectObjectContent(
bucketName: string,
objectName: string,
selectOpts: SelectOptions,
): Promise<SelectResults | undefined> {
if (!isValidBucketName(bucketName)) {
throw new errors.InvalidBucketNameError(`Invalid bucket name: ${bucketName}`)
}
if (!isValidObjectName(objectName)) {
throw new errors.InvalidObjectNameError(`Invalid object name: ${objectName}`)
}
if (!_.isEmpty(selectOpts)) {
if (!isString(selectOpts.expression)) {
throw new TypeError('sqlExpression should be of type "string"')
}
if (!_.isEmpty(selectOpts.inputSerialization)) {
if (!isObject(selectOpts.inputSerialization)) {
throw new TypeError('inputSerialization should be of type "object"')
}
} else {
throw new TypeError('inputSerialization is required')
}
if (!_.isEmpty(selectOpts.outputSerialization)) {
if (!isObject(selectOpts.outputSerialization)) {
throw new TypeError('outputSerialization should be of type "object"')
}
} else {
throw new TypeError('outputSerialization is required')
}
} else {
throw new TypeError('valid select configuration is required')
}

const method = 'POST'
const query = `select&select-type=2`

const config: Record<string, unknown>[] = [
{
Expression: selectOpts.expression,
},
{
ExpressionType: selectOpts.expressionType || 'SQL',
},
{
InputSerialization: [selectOpts.inputSerialization],
},
{
OutputSerialization: [selectOpts.outputSerialization],
},
]

// Optional
if (selectOpts.requestProgress) {
config.push({ RequestProgress: selectOpts?.requestProgress })
}
// Optional
if (selectOpts.scanRange) {
config.push({ ScanRange: selectOpts.scanRange })
}

const builder = new xml2js.Builder({
rootName: 'SelectObjectContentRequest',
renderOpts: { pretty: false },
headless: true,
})
const payload = builder.buildObject(config)

const res = await this.makeRequestAsync({ method, bucketName, objectName, query }, payload)
const body = await readAsBuffer(res)
return parseSelectObjectContentResponse(body)
}
}
41 changes: 41 additions & 0 deletions src/internal/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -275,3 +275,44 @@ export type RemoveTaggingParams = {
objectName?: string
removeOpts?: TaggingOpts
}

export type InputSerialization = {
CompressionType?: 'NONE' | 'GZIP' | 'BZIP2'
CSV?: {
AllowQuotedRecordDelimiter?: boolean
Comments?: string
FieldDelimiter?: string
FileHeaderInfo?: 'NONE' | 'IGNORE' | 'USE'
QuoteCharacter?: string
QuoteEscapeCharacter?: string
RecordDelimiter?: string
}
JSON?: {
Type: 'DOCUMENT' | 'LINES'
}
Parquet?: EmptyObject
}

export type OutputSerialization = {
CSV?: {
FieldDelimiter?: string
QuoteCharacter?: string
QuoteEscapeCharacter?: string
QuoteFields?: string
RecordDelimiter?: string
}
JSON?: {
RecordDelimiter?: string
}
}

export type SelectProgress = { Enabled: boolean }
export type ScanRange = { Start: number; End: number }
export type SelectOptions = {
expression: string
expressionType?: string
inputSerialization: InputSerialization
outputSerialization: OutputSerialization
requestProgress?: SelectProgress
scanRange?: ScanRange
}
Loading

0 comments on commit 6b9845d

Please sign in to comment.