Skip to content

Commit

Permalink
cherry-pick new feature to v1
Browse files Browse the repository at this point in the history
Merge branch 'feat/getUserProperties' into v1
  • Loading branch information
Luca Marzi committed Jul 24, 2020
2 parents f422c50 + 4884ae9 commit 07c8bcf
Show file tree
Hide file tree
Showing 19 changed files with 239 additions and 19 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,16 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## Unreleased

- Added `getUserProperties` to decorated `fastify.Request`, which returns the user properties

### Changed

- Update @mia-platform/lc39 2.2.2 -> 2.4.0
- Update ajv 6.10.2 -> 6.12.0
- Update fastify-plugin 1.6.0 -> 1.6.1
- Update smiple-get 3.0.3 -> 3.1.0

## v1.1.1 - 2019-12-09
## Add
Expand Down
18 changes: 17 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,14 +80,30 @@ to validate the querystring, the parameters, the payload, the response.
In addition to validation, you will also have a swagger documentation available at the `/documentation/` path.

Thanks to TypeScript's type definitions, editors can actually provide autocompletion for the additional methods
of the request object such as `getUserId` or `getGroups`.
of the request object such as `getUserId` or `getGroups` or `getUserProperties`

In the async initialization function you can also access the `fastify` instance, so you can register any plugin,
see [here][fastify-ecosystem] for a list of currently available plugins.
In addition, you can register additional [`content-type` parsers][fastify-parsers].

NB: the fifth parameter of `rawCustomPlugin` should be used wisely. See tests for that.

## Testing
CustomPlugin expose getDirectServiceProxy and getServiceProxy for testing purpose:
### getDirectServiceProxy
Import the function in you test:
``` javascript
const { getDirectServiceProxy } = require('@mia-platform/custom-plugin-lib')
const myServiceProxy = getDirectServiceProxy(MY_SERVICE_NAME)
```
all the options accepted by the getDirectServiceProxy can be passed (es: `{ port: CUSTOM_PORT }`).

### getServiceProxy
It need the MICROSERVICE_GATEWAY_SERVICE_NAME so you need to pass it like this:
``` javascript
const { getServiceProxy } = require('@mia-platform/custom-plugin-lib')
const myServiceProxy = getServiceProxy(MICROSERVICE_GATEWAY_SERVICE_NAME)
```
## Configuration
To use the library, you should specify the environment variables listed [here](index.js#L22),
other variables can be specified by setting your envSchema when calling the plugin.
Expand Down
1 change: 1 addition & 0 deletions examples/default.env
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
USERID_HEADER_KEY=userid-header-key
USER_PROPERTIES_HEADER_KEY=miauserproperties
GROUPS_HEADER_KEY=groups-header-key
CLIENTTYPE_HEADER_KEY=clienttype-header-key
BACKOFFICE_HEADER_KEY=backoffice-header-key
Expand Down
6 changes: 5 additions & 1 deletion index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ declare function customPlugin(envSchema?: customPlugin.environmentSchema): custo
declare namespace customPlugin {
type CustomService = (asyncInitFunction: AsyncInitFunction) => any

function getDirectServiceProxy(serviceName: string, options?: InitServiceOptions): Service
function getServiceProxy(microserviceGatewayServiceName: string, options?: InitServiceOptions): Service
interface environmentSchema {
type: 'object',
required?: string[],
Expand All @@ -39,7 +41,7 @@ declare namespace customPlugin {
'bodyLimit' |
'logLevel' |
'config' |
'prefixTrailingSlash'
'prefixTrailingSlash'
>

interface DecoratedFastify extends fastify.FastifyInstance {
Expand All @@ -53,12 +55,14 @@ declare namespace customPlugin {

interface DecoratedRequest extends fastify.FastifyRequest<http.IncomingMessage> {
getUserId: () => string | null,
getUserProperties: () => object | null,
getGroups: () => string[],
getClientType: () => string | null,
isFromBackOffice: () => boolean,
getDirectServiceProxy: (serviceName: string, options?: InitServiceOptions) => Service,
getServiceProxy: (options?: InitServiceOptions) => Service,
USERID_HEADER_KEY: string,
USER_PROPERTIES_HEADER_KEY: string,
GROUPS_HEADER_KEY: string,
CLIENTTYPE_HEADER_KEY: string,
BACKOFFICE_HEADER_KEY: string
Expand Down
14 changes: 14 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ const addPreDecorator = require('./lib/preDecorator')
const addPostDecorator = require('./lib/postDecorator')

const USERID_HEADER_KEY = 'USERID_HEADER_KEY'
const USER_PROPERTIES_HEADER_KEY = 'USER_PROPERTIES_HEADER_KEY'
const GROUPS_HEADER_KEY = 'GROUPS_HEADER_KEY'
const CLIENTTYPE_HEADER_KEY = 'CLIENTTYPE_HEADER_KEY'
const BACKOFFICE_HEADER_KEY = 'BACKOFFICE_HEADER_KEY'
Expand All @@ -52,6 +53,12 @@ const baseSchema = {
description: 'the header key to get the user id',
minLength: 1,
},
[USER_PROPERTIES_HEADER_KEY]: {
type: 'string',
description: 'the header key to get the user permissions',
minLength: 1,
default: 'miauserproperties',
},
[GROUPS_HEADER_KEY]: {
type: 'string',
description: 'the header key to get the groups comma separated list',
Expand Down Expand Up @@ -137,9 +144,11 @@ function getServiceBuilderFromService(baseOptions = {}) {
return serviceBuilder(this[MICROSERVICE_GATEWAY_SERVICE_NAME], {}, baseOptions)
}

// TODO: test this
function getMiaHeaders() {
return {
[this.USERID_HEADER_KEY]: this.getUserId(),
[this.USER_PROPERTIES_HEADER_KEY]: JSON.stringify(this.getUserProperties()),
[this.GROUPS_HEADER_KEY]: this.getGroups().join(','),
[this.CLIENTTYPE_HEADER_KEY]: this.getClientType(),
[this.BACKOFFICE_HEADER_KEY]: this.isFromBackOffice() ? '1' : '',
Expand All @@ -157,6 +166,7 @@ async function decorateRequestAndFastifyInstance(fastify, { asyncInitFunction })
fastify.setSchemaCompiler(schema => ajv.compile(schema))

fastify.decorateRequest(USERID_HEADER_KEY, config[USERID_HEADER_KEY])
fastify.decorateRequest(USER_PROPERTIES_HEADER_KEY, config[USER_PROPERTIES_HEADER_KEY])
fastify.decorateRequest(GROUPS_HEADER_KEY, config[GROUPS_HEADER_KEY])
fastify.decorateRequest(CLIENTTYPE_HEADER_KEY, config[CLIENTTYPE_HEADER_KEY])
fastify.decorateRequest(BACKOFFICE_HEADER_KEY, config[BACKOFFICE_HEADER_KEY])
Expand Down Expand Up @@ -216,3 +226,7 @@ function initCustomServiceEnvironment(envSchema = defaultSchema) {
}

module.exports = initCustomServiceEnvironment
module.exports.getDirectServiceProxy = getDirectlyServiceBuilderFromService
module.exports.getServiceProxy = (microserviceGatewayServiceName, baseOptions = {}) => {
return serviceBuilder(microserviceGatewayServiceName, {}, baseOptions)
}
9 changes: 7 additions & 2 deletions lib/postDecorator.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,16 @@
const LEAVE_UNCHANGED_RESPONSE_STATUS_CODE = 204
const ABORT_CHAIN_STATUS_CODE = 418

const { getUserId, getGroups, getClientType, isFromBackOffice } = require('./util')
const { getUserId, getGroups, getClientType, isFromBackOffice, getUserProperties } = require('./util')

function getUserIdFromBody() {
return getUserId(this.getOriginalRequestHeaders()[this.USERID_HEADER_KEY])
}

function getUserPropertiesFromBody() {
return getUserProperties(this.getOriginalRequestHeaders()[this.USER_PROPERTIES_HEADER_KEY])
}

function getGroupsFromBody() {
return getGroups(this.getOriginalRequestHeaders()[this.GROUPS_HEADER_KEY] || '')
}
Expand Down Expand Up @@ -179,6 +183,7 @@ function addPostDecorator(path, handler) {
fastify.decorateRequest('getOriginalResponseStatusCode', getResponseStatusCode)

fastify.decorateRequest('getUserId', getUserIdFromBody)
fastify.decorateRequest('getUserProperties', getUserPropertiesFromBody)
fastify.decorateRequest('getGroups', getGroupsFromBody)
fastify.decorateRequest('getClientType', getClientTypeFromBody)
fastify.decorateRequest('isFromBackOffice', isFromBackOfficeFromBody)
Expand All @@ -200,7 +205,7 @@ function addPostDecorator(path, handler) {

addPostDecorator[Symbol.for('plugin-meta')] = {
decorators: {
request: ['USERID_HEADER_KEY', 'GROUPS_HEADER_KEY', 'CLIENTTYPE_HEADER_KEY', 'BACKOFFICE_HEADER_KEY'],
request: ['USERID_HEADER_KEY', 'USER_PROPERTIES_HEADER_KEY', 'GROUPS_HEADER_KEY', 'CLIENTTYPE_HEADER_KEY', 'BACKOFFICE_HEADER_KEY'],
},
}

Expand Down
9 changes: 7 additions & 2 deletions lib/preDecorator.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,16 @@
const LEAVE_UNCHANGED_REQUEST_STATUS_CODE = 204
const ABORT_CHAIN_STATUS_CODE = 418

const { getUserId, getGroups, getClientType, isFromBackOffice } = require('./util')
const { getUserId, getGroups, getClientType, isFromBackOffice, getUserProperties } = require('./util')

function getUserIdFromBody() {
return getUserId(this.getOriginalRequestHeaders()[this.USERID_HEADER_KEY])
}

function getUserPropertiesFromBody() {
return getUserProperties(this.getOriginalRequestHeaders()[this.USER_PROPERTIES_HEADER_KEY])
}

function getGroupsFromBody() {
return getGroups(this.getOriginalRequestHeaders()[this.GROUPS_HEADER_KEY] || '')
}
Expand Down Expand Up @@ -147,6 +151,7 @@ function addPreDecorator(path, handler) {
fastify.decorateRequest('getOriginalRequestBody', getBody)

fastify.decorateRequest('getUserId', getUserIdFromBody)
fastify.decorateRequest('getUserProperties', getUserPropertiesFromBody)
fastify.decorateRequest('getGroups', getGroupsFromBody)
fastify.decorateRequest('getClientType', getClientTypeFromBody)
fastify.decorateRequest('isFromBackOffice', isFromBackOfficeFromBody)
Expand All @@ -168,7 +173,7 @@ function addPreDecorator(path, handler) {

addPreDecorator[Symbol.for('plugin-meta')] = {
decorators: {
request: ['USERID_HEADER_KEY', 'GROUPS_HEADER_KEY', 'CLIENTTYPE_HEADER_KEY', 'BACKOFFICE_HEADER_KEY'],
request: ['USERID_HEADER_KEY', 'USER_PROPERTIES_HEADER_KEY', 'GROUPS_HEADER_KEY', 'CLIENTTYPE_HEADER_KEY', 'BACKOFFICE_HEADER_KEY'],
},
}

Expand Down
9 changes: 7 additions & 2 deletions lib/rawCustomPlugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,16 @@

'use strict'

const { getUserId, getGroups, getClientType, isFromBackOffice } = require('./util')
const { getUserId, getGroups, getClientType, isFromBackOffice, getUserProperties } = require('./util')

function getUserIdFromHeader() {
return getUserId(this.headers[this.USERID_HEADER_KEY])
}

function getUserPropertiesFromHeader() {
return getUserProperties(this.headers[this.USER_PROPERTIES_HEADER_KEY])
}

function getGroupsFromHeaders() {
return getGroups(this.headers[this.GROUPS_HEADER_KEY] || '')
}
Expand All @@ -37,6 +41,7 @@ function isFromBackOfficeFromHeaders() {
function addRawCustomPlugin(method, path, handler, schema, advancedConfigs) {
this.register(function rawCustomPlugin(fastify, options, next) {
fastify.decorateRequest('getUserId', getUserIdFromHeader)
fastify.decorateRequest('getUserProperties', getUserPropertiesFromHeader)
fastify.decorateRequest('getGroups', getGroupsFromHeaders)
fastify.decorateRequest('getClientType', getClientTypeFromHeaders)
fastify.decorateRequest('isFromBackOffice', isFromBackOfficeFromHeaders)
Expand All @@ -49,7 +54,7 @@ function addRawCustomPlugin(method, path, handler, schema, advancedConfigs) {

addRawCustomPlugin[Symbol.for('plugin-meta')] = {
decorators: {
request: ['USERID_HEADER_KEY', 'GROUPS_HEADER_KEY', 'CLIENTTYPE_HEADER_KEY', 'BACKOFFICE_HEADER_KEY', 'ADDITIONAL_HEADERS_TO_PROXY'],
request: ['USERID_HEADER_KEY', 'USER_PROPERTIES_HEADER_KEY', 'GROUPS_HEADER_KEY', 'CLIENTTYPE_HEADER_KEY', 'BACKOFFICE_HEADER_KEY', 'ADDITIONAL_HEADERS_TO_PROXY'],
},
}

Expand Down
13 changes: 13 additions & 0 deletions lib/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,18 @@ function getUserId(userId) {
return userId
}

function getUserProperties(userPropertiesAsString) {
if (!userPropertiesAsString) {
return null
}

try {
return JSON.parse(userPropertiesAsString)
} catch (error) {
return null
}
}

function getGroups(groups) {
return groups.split(',').filter(longerThan(0))
}
Expand All @@ -53,6 +65,7 @@ const extraHeadersKeys = [

module.exports = {
getUserId,
getUserProperties,
getGroups,
getClientType,
isFromBackOffice,
Expand Down
23 changes: 12 additions & 11 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@
"Jacopo Andrea Giola <jacopo@giola.org>",
"Davide Bianchi <bianchidavide12@gmail.com>",
"Riccardo Di Benedetto <dibe.ricky@gmail.com>",
"Luca Marzi <luca.marzi@mail.polimi.it>"
"Luca Marzi <luca.marzi@mail.polimi.it>",
"Fabio Percivaldi <fabio.percivaldi@hotmail.it"
],
"main": "index.js",
"repository": {
Expand All @@ -40,23 +41,23 @@
"version": "./scripts/update-version.sh ${npm_package_version} && git add CHANGELOG.md"
},
"dependencies": {
"@mia-platform/lc39": "^2.2.2",
"@types/node": "^12.7.4",
"ajv": "^6.10.2",
"@mia-platform/lc39": "^2.4.0",
"@types/node": "^13.9.1",
"ajv": "^6.12.0",
"fastify-env": "^1.0.1",
"fastify-formbody": "^3.1.0",
"fastify-plugin": "^1.6.0",
"fastify-plugin": "^1.6.1",
"http-errors": "^1.7.3",
"simple-get": "^3.0.3"
"simple-get": "^3.1.0"
},
"devDependencies": {
"@mia-platform/eslint-config-mia": "^2.0.1",
"eslint": "^6.3.0",
"@mia-platform/eslint-config-mia": "^3.0.0",
"eslint": "^6.8.0",
"fastify-routes": "^2.0.3",
"nock": "^11.3.3",
"nock": "^11.9.1",
"pre-commit": "^1.2.2",
"tap": "^14.6.1",
"typescript": "^3.6.2"
"tap": "^14.10.6",
"typescript": "^3.8.3"
},
"engines": {
"node": ">=8"
Expand Down
75 changes: 75 additions & 0 deletions tests/getServiceProxy.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
'use strict'

const tap = require('tap')
const nock = require('nock')

const MY_AWESOME_SERVICE_PROXY_URL = 'my-awesome-service'
const MICROSERVICE_GATEWAY_SERVICE_NAME = 'microservice-gateway'
const { getDirectServiceProxy, getServiceProxy } = require('../index')

tap.test('getDirectServiceProxy available for testing', async t => {
nock.disableNetConnect()
t.tearDown(() => {
nock.enableNetConnect()
})

const RETURN_MESSAGE = 'OK'
const customProxy = getDirectServiceProxy(MY_AWESOME_SERVICE_PROXY_URL)
const awesomeServiceScope = nock(`http://${MY_AWESOME_SERVICE_PROXY_URL}:80`)
.get('/test-endpoint')
.reply(200, {
message: RETURN_MESSAGE,
})

const result = await customProxy.get('/test-endpoint')

t.strictSame(result.statusCode, 200)
t.strictSame(result.payload.message, RETURN_MESSAGE)
awesomeServiceScope.done()
})

tap.test('getDirectServiceProxy accept all options', async t => {
nock.disableNetConnect()
t.tearDown(() => {
nock.enableNetConnect()
})

const RETURN_MESSAGE = 'OK'
const customProxy = getDirectServiceProxy(MY_AWESOME_SERVICE_PROXY_URL, {
port: 3000,
})

const awesomeServiceScope = nock(`http://${MY_AWESOME_SERVICE_PROXY_URL}:3000`)
.get('/test-endpoint')
.reply(200, {
message: RETURN_MESSAGE,
})

const result = await customProxy.get('/test-endpoint')

t.strictSame(result.statusCode, 200)
t.strictSame(result.payload.message, RETURN_MESSAGE)
awesomeServiceScope.done()
})

tap.test('getServiceProxy available for testing', async t => {
nock.disableNetConnect()
t.tearDown(() => {
nock.enableNetConnect()
})

const RETURN_MESSAGE = 'OK'
const customProxy = getServiceProxy(MICROSERVICE_GATEWAY_SERVICE_NAME, { port: 3000 })
const microserviceScope = nock(`http://${MICROSERVICE_GATEWAY_SERVICE_NAME}:3000`)
.get('/test-endpoint')
.reply(200, {
message: RETURN_MESSAGE,
})


const result = await customProxy.get('/test-endpoint')

t.strictSame(result.statusCode, 200)
t.strictSame(result.payload.message, RETURN_MESSAGE)
microserviceScope.done()
})
Loading

0 comments on commit 07c8bcf

Please sign in to comment.