Browse Source

first tested example added

tags/v1.0.0-alpha.0
ojizero 1 year ago
parent
commit
ff93a9c9ab
No account linked to committer's email address

+ 13
- 0
examples/sample-elasticsearch-client/README.md View File

@@ -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
- 0
examples/sample-elasticsearch-client/index.js View File

@@ -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
- 0
examples/sample-elasticsearch-client/package-lock.json
File diff suppressed because it is too large
View File


+ 15
- 0
examples/sample-elasticsearch-client/package.json View File

@@ -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
- 0
examples/sample-rest-api/client.js View File


+ 0
- 0
examples/sample-rest-api/index.js View File


+ 12
- 0
examples/sample-rest-api/package.json View File

@@ -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
- 0
examples/sample-rest-api/server.js View File


+ 14
- 6
src/client.ts View File

@@ -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 {}
// 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 {
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 {
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 {

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 {
retries,
json: isJson,
timeout: timeout * 1000,
body: JSON.stringify(payload),
throwHttpErrors: onError !== 'resolve',
body: isJson ? payload : JSON.stringify(payload),
}
}


+ 14
- 6
src/method.ts View File

@@ -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 {
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 {
queryString = undefined,
contentType = applicationJson,
accept = applicationJson,
headers = {},
} = spec

const method = _method.toUpperCase()
@@ -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 {
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

+ 2
- 2
src/validation.ts View File

@@ -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:

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