Parcourir la source

initial chages to support different clients

tags/v1.0.0
ojizero il y a 1 an
Parent
révision
7edfb16e9c
Aucun compte lié à l'adresse e-mail de l'auteur

+ 5
- 3
README.md Voir le fichier

@@ -57,15 +57,17 @@ It also adds support for standardized validation for request arguments, query st
With NPM

```
npm i -S @ojizero/portal
npm i -S got @ojizero/portal
```

Or if you're into Yarn

```
yarn add @ojizero/portal
yarn add got @ojizero/portal
```

We separate installation of `got` and `portal` to prepare for later support of multiple internal clients, mainly to support browsers using `ky` without introducing breaking changes.

## Usage

Aimed to be used as a building block for API client libraries
@@ -274,7 +276,7 @@ export interface Response {
},
body: any,
headers: IncomingHttpHeaders,
_rawResponse: RawResponse,
[Symbol.for('portal:symbols:raw-response')]: RawResponse,
}
```


+ 2
- 6
examples/sample-elasticsearch-client/README.md Voir le fichier

@@ -4,10 +4,6 @@ This is a sample client build for Elasticsearch using Portal, is provides an API

## 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
```bash
npm test # starts a docker image for elasticsearch, runs index.js then kill the container
```

+ 1
- 1
examples/sample-elasticsearch-client/index.js Voir le fichier

@@ -55,6 +55,6 @@ if (require.main === module) {
{"a":{"test":"bulkoperation"}}
`
})
console.log({ getDocument: { stringifiedResponse: JSON.stringify(response) } })
console.log({ bulkOperation: { stringifiedResponse: JSON.stringify(response) } })
})()
}

+ 2
- 1
examples/sample-elasticsearch-client/package.json Voir le fichier

@@ -6,7 +6,8 @@
"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"
"stop": "docker kill elastic_portal_demo && docker rm elastic_portal_demo",
"test": "npm start && node index.js && npm stop"
},
"license": "MIT",
"dependencies": {

+ 3
- 0
examples/sample-rest-api/README.md Voir le fichier

@@ -0,0 +1,3 @@
# Portal sample client - Generic rest API

> TODO:

+ 37
- 5
src/index.ts Voir le fichier

@@ -1,23 +1,55 @@
import PortalClient, { Client, Config } from './client'
import PortalClient, { Client, ClientFn, Config as ClientConfig } from './client'

import methodGenerator, { MethodFactory } from './method'
import resrouceGenerator, { ResourceFactory } from './resource'

import got from 'got'
let got: ClientFn

try {
got = require('got')
} catch (_) {}


function forNode (clientType: ClientType | undefined): clientType is NodeClient {
return typeof clientType === 'undefined' || (clientType.type === 'node' || clientType.type === 'got')
}

function getClient (config: PortalConfig): ClientFn {
if (forNode(config.client)) return got

throw new Error(`Unspported client config ${config.client}`)
}

export interface ClientType {
type: 'node' | 'got', // | 'browser' | 'ky' | 'custom',
}

export interface NodeClient extends ClientType {
type: 'node' | 'got',
}

export interface PortalConfig {
client?: NodeClient,
}

export type Config = ClientConfig & PortalConfig

export const clientSymbol = Symbol.for('portal:symbols:client')

export interface Portal {
route: MethodFactory,
resource: ResourceFactory,
_client: Client,
[clientSymbol]: Client,
}

export function createPortalClient (config: Config): Portal {
const client = new PortalClient(got, config)
const clientFn = getClient(config)
const client = new PortalClient(clientFn, config)

const portal: Portal = {
route: methodGenerator(client),
resource: resrouceGenerator(client),
_client: client,
[clientSymbol]: client,
}

return portal

+ 12
- 6
src/resource.ts Voir le fichier

@@ -66,7 +66,7 @@ export class Resource {
Object.defineProperty(this, route, {
value: routeMethod,
writable: false,
enumerable: true, // TODO: is it okay to have them enumerable ?
enumerable: true,
})

// Add is aliased as set
@@ -74,7 +74,7 @@ export class Resource {
Object.defineProperty(this, 'set', {
value: routeMethod,
writable: false,
enumerable: true, // TODO: is it okay to have them enumerable ?
enumerable: true,
})
}
// Del is aliased as delete
@@ -82,7 +82,7 @@ export class Resource {
Object.defineProperty(this, 'delete', {
value: routeMethod,
writable: false,
enumerable: true, // TODO: is it okay to have them enumerable ?
enumerable: true,
})
}
})
@@ -108,18 +108,24 @@ export class Resource {
return method(this.client)
}

setExtraMethods (extraMethods?: { [k:string]: any }) {
setExtraMethods (extraMethods?: { [k:string]: MethodSpec }) {
if (!extraMethods) return

Object.entries(extraMethods)
.forEach(([method, spec]) => {
// TODO: should it be prefixed by basePath ?
const sep = spec.path.startsWith('/') ? '' : '/'

spec = {
...spec,
path: `${this.baseRoute}${sep}${spec.path}`,
}

const fn = this.methodFactory(spec)

Object.defineProperty(this, method, {
value: fn,
writable: false,
enumerable: true, // TODO: is it okay to have them enumerable ?
enumerable: true,
})
})
}

+ 5
- 3
src/simplified-joi.ts Voir le fichier

@@ -12,6 +12,10 @@ const JOI_MAPPING: { [k: string]: SchemaLike } = {
// array: Joi.array(),
}

const isNotRequiredRegex = /notrequired/i

const isNotRequired = isNotRequiredRegex.test.bind(isNotRequiredRegex)

function isObject (value: any): value is { [k: string]: any } {
return !!value && typeof value === 'object'
}
@@ -45,15 +49,13 @@ export function transformSchema (schema: any): SchemaLike {

const [schemaName, schemaOptions = ''] = (schema as string).split('|', 2)

const isNotRequired = /notrequired/i

schema = JOI_MAPPING[schemaName]

if (typeof schema === 'undefined') {
throw new Error(`Requested Joi validator ${schema} unsupported or undefined.`)
}

return isNotRequired.test(schemaOptions) ? schema : schema.required()
return isNotRequired(schemaOptions) ? schema : schema.required()
}

export default transformSchema

Chargement…
Annuler
Enregistrer