Browse Source

first tested example added

modified-method-api
ojizero 4 years ago
parent
commit
ff93a9c9ab
No known key found for this signature in database
GPG Key ID: FEBB7097845B0C7E
  1. 13
      examples/sample-elasticsearch-client/README.md
  2. 71
      examples/sample-elasticsearch-client/index.js
  3. 1756
      examples/sample-elasticsearch-client/package-lock.json
  4. 15
      examples/sample-elasticsearch-client/package.json
  5. 0
      examples/sample-rest-api/client.js
  6. 0
      examples/sample-rest-api/index.js
  7. 12
      examples/sample-rest-api/package.json
  8. 0
      examples/sample-rest-api/server.js
  9. 20
      src/client.ts
  10. 20
      src/method.ts
  11. 4
      src/validation.ts

13
examples/sample-elasticsearch-client/README.md

@ -0,0 +1,13 @@ @@ -0,0 +1,13 @@
# Portal sample client - Elasticsearch
This is a sample client build for Elasticsearch using Portal, is provides an API to create index, insert a document and read a document.
## Usage
```
npm start # starts a docker container with elasticsearch instance
node ./index.js # run the sample code
npm stop # stop and remove the docker container
```

71
examples/sample-elasticsearch-client/index.js

@ -0,0 +1,71 @@ @@ -0,0 +1,71 @@
const {
Joi,
createPortalClient,
} = require('@ojizero/portal')
function makeESClient (host) {
const client = createPortalClient({
baseUrl: host,
onError: 'resolve',
})
return {
createIndex: client.route({
path: '/:indexName',
method: 'PUT',
}),
getDocument: client.route({
path: '/:indexName/:docType/:docId',
method: 'GET',
}),
addDocument: client.route({
path: '/:indexName/:docType/:docId',
method: 'POST',
})
// TODO: elastic's bulk API currently is incompatible with portal :D
// bulkOperation: client.route({
// path: '/_bulk',
// method: 'POST',
// // You can pass any Joi based schema for validation
// // Or you can use the simplified syntax provided by portal
// // Or you can use any custom object withe a `validate` method
// body: Joi.string().required(),
// // Joi.object({
// // body: Joi.array().items(
// // Joi.object({
// // index: Joi.object({
// // _index: Joi.string().required(),
// // _type: Joi.string().required(),
// // _id: Joi.string(),
// // }).required(),
// // }),
// // Joi.object(),
// // ).required()
// // }).required(),
// // headers: {
// // 'Content-Type': 'application/text',
// // }
// }),
}
}
module.exports = makeESClient
if (require.main === module) {
(async function () {
const client = makeESClient('http://localhost:9200')
let response
response = await client.createIndex('test-index')
delete response._rawResponse // This is a HUGE object
console.log({ createIndex: { stringifiedResponse: JSON.stringify(response) } })
response = await client.addDocument('test-index', 'test-type', 'test-id', { a: { test: 'document' } })
delete response._rawResponse // This is a HUGE object
console.log({ addDocument: { stringifiedResponse: JSON.stringify(response) } })
response = await client.getDocument('test-index', 'test-type', 'test-id')
delete response._rawResponse // This is a HUGE object
console.log({ getDocument: { stringifiedResponse: JSON.stringify(response) } })
})()
}

1756
examples/sample-elasticsearch-client/package-lock.json generated

File diff suppressed because it is too large Load Diff

15
examples/sample-elasticsearch-client/package.json

@ -0,0 +1,15 @@ @@ -0,0 +1,15 @@
{
"name": "sample-elasticsearch-client",
"version": "1.0.0",
"description": "Sample library demoing portal",
"private": true,
"main": "index.js",
"scripts": {
"start": "docker run --publish '9200:9200' --name elastic_portal_demo --detach elasticsearch:6.5.4 && while ! curl localhost:9200; do sleep 10; done",
"stop": "docker kill elastic_portal_demo && docker rm elastic_portal_demo"
},
"license": "MIT",
"dependencies": {
"@ojizero/portal": "../../"
}
}

0
examples/sample-rest-api/client.js

0
examples/sample-rest-api/index.js

12
examples/sample-rest-api/package.json

@ -0,0 +1,12 @@ @@ -0,0 +1,12 @@
{
"name": "sample-rest-api",
"version": "1.0.0",
"description": "Sample library demoing portal",
"private": true,
"main": "index.js",
"scripts": {},
"license": "MIT",
"dependencies": {
"@ojizero/portal": "../../"
}
}

0
examples/sample-rest-api/server.js

20
src/client.ts

@ -48,12 +48,20 @@ export interface Response { @@ -48,12 +48,20 @@ export interface Response {
_rawResponse: RawResponse,
}
export type RequestBodyObject = { [k: string]: any }
export type RequestBody = string | RequestBodyObject // | Array<RequestBodyObject> // TODO: TS is made when i add the array :(
function isObject (body: RequestBody): body is RequestBodyObject {
return typeof body === 'object'
}
// Add compatibility with Got type
export interface RequestOptions extends HttpsRequestOptions {
baseUrl?: string,
url?: string,
json?: boolean,
body?: string,
body?: RequestBody,
retries?: number,
throwHttpErrors?: boolean,
headers?: OutgoingHttpHeaders,
@ -76,7 +84,7 @@ export interface RequestConfig extends RequestOptions, Config {} @@ -76,7 +84,7 @@ export interface RequestConfig extends RequestOptions, Config {}
// export type RequestConfig = Config
export interface Client {
request (method: string, path: string, payload: {}, options: any): Promise<Response>
request (method: string, path: string, payload: RequestBody, options: any): Promise<Response>
}
export class PortalClient implements Client {
@ -91,7 +99,7 @@ export class PortalClient implements Client { @@ -91,7 +99,7 @@ export class PortalClient implements Client {
this.config = config
}
async request (method: string, path: string, payload: {}, options: RequestConfig): Promise<Response> {
async request (method: string, path: string, payload: RequestBody, options: RequestConfig): Promise<Response> {
const requestOptions = this.constructRequestOptions(method, path, payload, options)
const response = await this.client(requestOptions)
@ -99,7 +107,7 @@ export class PortalClient implements Client { @@ -99,7 +107,7 @@ export class PortalClient implements Client {
return this.transformResponse(response)
}
constructRequestOptions (method: string, path: string, payload: { [k: string]: any }, options: RequestConfig): RequestOptions {
constructRequestOptions (method: string, path: string, payload: RequestBody, options: RequestConfig): RequestOptions {
const {
baseUrl,
headers,
@ -127,7 +135,7 @@ export class PortalClient implements Client { @@ -127,7 +135,7 @@ export class PortalClient implements Client {
if (useHeader) {
headers[key] = value
} else if (usePayload) {
} else if (usePayload && isObject(payload)) {
payload[key] = value
} else if (useQueryString) {
const [url, queryString = ''] = path.split('?', 2)
@ -149,8 +157,8 @@ export class PortalClient implements Client { @@ -149,8 +157,8 @@ export class PortalClient implements Client {
retries,
json: isJson,
timeout: timeout * 1000,
body: JSON.stringify(payload),
throwHttpErrors: onError !== 'resolve',
body: isJson ? payload : JSON.stringify(payload),
}
}

20
src/method.ts

@ -7,6 +7,7 @@ import { @@ -7,6 +7,7 @@ import {
} from 'querystring'
import { ValdiationSpec, ensureValidData } from './validation';
import { OutgoingHttpHeaders } from 'http';
const applicationJson = 'application/json'
@ -19,6 +20,7 @@ export interface MethodSpec { @@ -19,6 +20,7 @@ export interface MethodSpec {
contentType?: string,
accept?: string,
// strict?: boolean,
headers?: OutgoingHttpHeaders,
}
export type RouteFunction = (...args: any[]) => Promise<Response>
@ -34,6 +36,7 @@ export function method (client: Client): MethodFactory { @@ -34,6 +36,7 @@ export function method (client: Client): MethodFactory {
queryString = undefined,
contentType = applicationJson,
accept = applicationJson,
headers = {},
} = spec
const method = _method.toUpperCase()
@ -42,6 +45,7 @@ export function method (client: Client): MethodFactory { @@ -42,6 +45,7 @@ export function method (client: Client): MethodFactory {
headers: {
'Accept': accept,
'Content-Type': contentType,
...headers,
}
}
@ -87,19 +91,23 @@ export function method (client: Client): MethodFactory { @@ -87,19 +91,23 @@ export function method (client: Client): MethodFactory {
payload = undefined
}
ensureValidData(params, args)
ensureValidData(body, payload)
ensureValidData(queryString, query)
ensureValidData(params, args, 'Parameters')
ensureValidData(body, payload, 'Payload')
ensureValidData(queryString, query, 'Query string')
let paramsCount = (path.match(/:[^\/]*/) || []).length
// Regexp here isn global we wanna
// match all avaialbel parameters
let paramsCount = (path.match(/:[^\/:]+/g) || []).length
let fullPath: string = args.reduce((acc, arg) => {
paramsCount -= 1
return acc.replace(/:[^\/]*/, arg)
// Regexp here isn't global we wanna
// match the first parameter only
return acc.replace(/:[^\/:]+/, arg)
}, path)
if (paramsCount !== 0) throw new Error('TODO: give me a meangingful error')
if (paramsCount !== 0) throw new Error('Number of provided parameters does not macth request path arguments')
if (query) {
let attachedQuery

4
src/validation.ts

@ -8,7 +8,7 @@ function isCustomValidator (spec: any): spec is Validator { @@ -8,7 +8,7 @@ function isCustomValidator (spec: any): spec is Validator {
return !!spec && !spec.isJoi && 'validate' in spec
}
export function ensureValidData (spec: SchemaLike | Validator | undefined, data: any) {
export function ensureValidData (spec: SchemaLike | Validator | undefined, data: any, name?: string) {
if (!spec) return
if (isCustomValidator(spec)) {
@ -21,7 +21,7 @@ export function ensureValidData (spec: SchemaLike | Validator | undefined, data: @@ -21,7 +21,7 @@ export function ensureValidData (spec: SchemaLike | Validator | undefined, data:
if (!error) return
throw new Error('TODO: give me a meangingful error')
throw new Error(`${!!name ? name+': ': ''}Provided data {${data}} failed to meat provided spec {${spec}}`)
}
export { SchemaLike }

Loading…
Cancel
Save