Skip to content

Commit

Permalink
Merge fb6172a into bd33a51
Browse files Browse the repository at this point in the history
  • Loading branch information
giuliowaitforitdavide committed Jan 30, 2023
2 parents bd33a51 + fb6172a commit 6ad65b3
Show file tree
Hide file tree
Showing 7 changed files with 126 additions and 44 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Unreleased

### Fixed
- merging of baseProperties with customProperties causing inconsistent microservices' env Variables schema

### Added

- add optional metrics on request duration. The metrics collection is enabled by mean of the variable `ENABLE_HTTP_CLIENT_METRICS` (default: false)
Expand Down
52 changes: 32 additions & 20 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,30 +94,41 @@ const baseSchema = {
},
}

function mergeObjectOrArray(toBeMergedValue, alreadyMergedValues, isArray) {
return isArray ? [
...toBeMergedValue ?? [],
...alreadyMergedValues,
] : {
...toBeMergedValue ?? {},
...alreadyMergedValues,
}
}

function mergeJsonSchemas(schema, otherSchema) {
const { properties: schemaProperties, required: requiredSchema = [], ...schemaRemainingProperties } = schema
const {
properties: otherSchemaProperties,
required: requiredOtherSchema = [],
...otherSchemaRemainingProperties
} = otherSchema
const mergedSchema = {
type: 'object',
properties: {
...schemaProperties,
...otherSchemaProperties,
},
required: [...requiredSchema, ...requiredOtherSchema],
allOf: [
schemaRemainingProperties,
otherSchemaRemainingProperties,
],
additionalProperties: false,
type: 'object',
}

Object.keys(schema).forEach(key => {
mergedSchema[key] = typeof schema[key] === 'object'
? mergeObjectOrArray(mergedSchema[key], schema[key], Array.isArray(schema[key]))
: schema[key]
})

Object.keys(otherSchema).forEach(key => {
mergedSchema[key] = typeof otherSchema[key] === 'object'
? mergeObjectOrArray(mergedSchema[key], otherSchema[key], Array.isArray(otherSchema[key]))
: otherSchema[key]
})

return mergedSchema
}

function getOverlappingKeys(properties, otherProperties) {
if (!otherProperties) {
return []
}
const propertiesNames = Object.keys(properties)
const otherPropertiesNames = Object.keys(otherProperties)
const overlappingProperties = propertiesNames.filter(propertyName =>
Expand Down Expand Up @@ -315,16 +326,17 @@ async function decorateRequestAndFastifyInstance(fastify, { asyncInitFunction, s
})
}

const defaultSchema = { type: 'object', required: [], properties: {} }

function initCustomServiceEnvironment(envSchema = defaultSchema) {
function initCustomServiceEnvironment(envSchema) {
return function customService(asyncInitFunction, serviceOptions) {
async function index(fastify, opts) {
const overlappingPropertiesNames = getOverlappingKeys(baseSchema.properties, envSchema.properties)
const overlappingPropertiesNames = getOverlappingKeys(baseSchema.properties, envSchema?.properties)
if (overlappingPropertiesNames.length > 0) {
throw new Error(`The provided Environment JSON Schema includes properties declared in the Base JSON Schema of the custom-plugin-lib, please remove them from your schema. The properties to remove are: ${overlappingPropertiesNames.join(', ')}`)
}
fastify.register(fastifyEnv, { schema: mergeJsonSchemas(baseSchema, envSchema), data: opts })

const schema = envSchema ? mergeJsonSchemas(baseSchema, envSchema) : baseSchema
fastify.register(fastifyEnv, { schema, data: opts })
fastify.register(fastifyFormbody)
fastify.register(fp(decorateRequestAndFastifyInstance), { asyncInitFunction, serviceOptions })
}
Expand Down
22 changes: 22 additions & 0 deletions tests/environment.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -230,5 +230,27 @@ tap.test('Test Environment variables', test => {
}
})

test.test('Should pass since all of the required fields are present', async assert => {
const env = {
...baseEnv,
MY_REQUIRED_ENV_VAR: 'value',
}

await assert.resolves(async() => {
await setupFastify('./tests/services/advanced-first-level-properties-service.js', env)
})
})

test.test('Should fail since one of the required fields is not present', async assert => {
// NOTE: use try catch instead of assert.reject to customize error message assertion
assert.plan(1)
try {
await setupFastify('./tests/services/advanced-first-level-properties-service.js', baseEnv)
} catch (error) {
const errorMessage = 'env must have required property \'MY_REQUIRED_ENV_VAR\''
assert.strictSame(error.message, errorMessage)
}
})

test.end()
})
34 changes: 18 additions & 16 deletions tests/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ const GROUPS_HEADER_KEY = 'groups-header-key'
const CLIENTTYPE_HEADER_KEY = 'clienttype-header-key'
const BACKOFFICE_HEADER_KEY = 'backoffice-header-key'
const MICROSERVICE_GATEWAY_SERVICE_NAME = 'microservice-gateway'
const MY_REQUIRED_ENV_VAR = 'value'

const baseEnv = {
USERID_HEADER_KEY,
Expand All @@ -48,7 +49,7 @@ tap.test('Plain Custom Service', test => {
}

test.test('Hello World', async assert => {
const fastify = await setupFastify(baseEnv)
const fastify = await setupFastify({ ...baseEnv, MY_REQUIRED_ENV_VAR })

const response = await fastify.inject({
method: 'GET',
Expand All @@ -62,7 +63,7 @@ tap.test('Plain Custom Service', test => {
})

test.test('Access platform values', async assert => {
const fastify = await setupFastify(baseEnv)
const fastify = await setupFastify({ ...baseEnv, MY_REQUIRED_ENV_VAR })
const response = await fastify.inject({
method: 'GET',
url: '/platform-values',
Expand Down Expand Up @@ -90,6 +91,7 @@ tap.test('Plain Custom Service', test => {
test.test('Access platform "miauserproperties" - when USER_PROPERTIES_HEADER_KEY NOT defined - uses default header key', async assert => {
const envarWithoutUserProperties = {
...baseEnv,
MY_REQUIRED_ENV_VAR,
}
delete envarWithoutUserProperties.USER_PROPERTIES_HEADER_KEY

Expand Down Expand Up @@ -117,7 +119,7 @@ tap.test('Plain Custom Service', test => {
})

test.test('Access platform values when not declared', async assert => {
const fastify = await setupFastify(baseEnv)
const fastify = await setupFastify({ ...baseEnv, MY_REQUIRED_ENV_VAR })
const response = await fastify.inject({
method: 'GET',
url: '/platform-values',
Expand All @@ -138,7 +140,7 @@ tap.test('Plain Custom Service', test => {
})

test.test('Send form encoded data', async assert => {
const fastify = await setupFastify(baseEnv)
const fastify = await setupFastify({ ...baseEnv, MY_REQUIRED_ENV_VAR })

const response = await fastify.inject({
method: 'POST',
Expand All @@ -162,7 +164,7 @@ tap.test('Plain Custom Service', test => {
test.test('Can return a stream', async assert => {
const filename = './tests/services/plain-custom-service.js'
const readFile = promisify(fs.readFile)
const fastify = await setupFastify(baseEnv)
const fastify = await setupFastify({ ...baseEnv, MY_REQUIRED_ENV_VAR })
const response = await fastify.inject({
method: 'GET',
url: '/stream',
Expand All @@ -177,7 +179,7 @@ tap.test('Plain Custom Service', test => {

test.test('Send some json, with validation', async assert => {
const payload = { some: 'stuff' }
const fastify = await setupFastify(baseEnv)
const fastify = await setupFastify({ ...baseEnv, MY_REQUIRED_ENV_VAR })
const response = await fastify.inject({
method: 'POST',
url: '/validation',
Expand Down Expand Up @@ -254,7 +256,7 @@ tap.test('Plain Custom Service', test => {
test.test('custom body parsing', async assert => {
const customType = 'application/custom-type'
const payload = { hello: 'world' }
const fastify = await setupFastify(baseEnv)
const fastify = await setupFastify({ ...baseEnv, MY_REQUIRED_ENV_VAR })
const response = await fastify.inject({
method: 'POST',
url: '/',
Expand All @@ -269,7 +271,7 @@ tap.test('Plain Custom Service', test => {
})

test.test('Healtiness handler can see decoration', async assert => {
const fastify = await setupFastify(baseEnv)
const fastify = await setupFastify({ ...baseEnv, MY_REQUIRED_ENV_VAR })
const response = await fastify.inject({
method: 'GET',
url: '/-/healthz',
Expand All @@ -279,7 +281,7 @@ tap.test('Plain Custom Service', test => {
})

test.test('returns mia headers - empty', async assert => {
const fastify = await setupFastify(baseEnv)
const fastify = await setupFastify({ ...baseEnv, MY_REQUIRED_ENV_VAR })

const response = await fastify.inject({
method: 'GET',
Expand All @@ -291,7 +293,7 @@ tap.test('Plain Custom Service', test => {
})

test.test('returns mia headers - filled', async assert => {
const fastify = await setupFastify(baseEnv)
const fastify = await setupFastify({ ...baseEnv, MY_REQUIRED_ENV_VAR })

const response = await fastify.inject({
method: 'GET',
Expand Down Expand Up @@ -329,7 +331,7 @@ tap.test('Advanced Custom Service', test => {

test.test('Require some environment variables', async assert => {
const MY_AWESOME_ENV = 'foobar'
const fastify = await setupFastify({ ...baseEnv, MY_AWESOME_ENV })
const fastify = await setupFastify({ ...baseEnv, MY_AWESOME_ENV, MY_REQUIRED_ENV_VAR })
const response = await fastify.inject({
method: 'GET',
url: '/env',
Expand All @@ -344,7 +346,7 @@ tap.test('Advanced Custom Service', test => {
test.test('Decorate fastify with custom functionalities', async assert => {
const MY_AWESOME_ENV = 'foobar'
const payload = { hello: 'world' }
const fastify = await setupFastify({ ...baseEnv, MY_AWESOME_ENV })
const fastify = await setupFastify({ ...baseEnv, MY_AWESOME_ENV, MY_REQUIRED_ENV_VAR })
const response = await fastify.inject({
method: 'POST',
url: '/custom',
Expand All @@ -359,7 +361,7 @@ tap.test('Advanced Custom Service', test => {
})

test.test('default env var', async assert => {
const fastify = await setupFastify(baseEnv)
const fastify = await setupFastify({ ...baseEnv, MY_REQUIRED_ENV_VAR })
const response = await fastify.inject({
method: 'GET',
url: '/env',
Expand All @@ -383,7 +385,7 @@ tap.test('Advanced config', test => {
}

test.test('it accepts advanced config', async assert => {
const fastify = await setupFastify(baseEnv)
const fastify = await setupFastify({ ...baseEnv, MY_REQUIRED_ENV_VAR })

const response = await fastify.inject({
method: 'POST',
Expand Down Expand Up @@ -431,7 +433,7 @@ tap.test('Service with API formats', t => {
}

t.test('it validates date-time', async t => {
const fastify = await setupFastify(baseEnv)
const fastify = await setupFastify({ ...baseEnv, MY_REQUIRED_ENV_VAR })
const someDate = '2021-10-12T15:46:39.081Z'

const response = await fastify.inject({
Expand All @@ -454,7 +456,7 @@ tap.test('Service with API formats', t => {
})

t.test('fails for invalid date', async t => {
const fastify = await setupFastify(baseEnv)
const fastify = await setupFastify({ ...baseEnv, MY_REQUIRED_ENV_VAR })

const response = await fastify.inject({
method: 'POST',
Expand Down
15 changes: 8 additions & 7 deletions tests/postDecorator.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const GROUPS_HEADER_KEY = 'groups-header-key'
const CLIENTTYPE_HEADER_KEY = 'clienttype-header-key'
const BACKOFFICE_HEADER_KEY = 'backoffice-header-key'
const MICROSERVICE_GATEWAY_SERVICE_NAME = 'microservice-gateway'
const MY_REQUIRED_ENV_VAR = 'value'
const baseEnv = {
USERID_HEADER_KEY,
USER_PROPERTIES_HEADER_KEY,
Expand All @@ -44,7 +45,7 @@ async function setupFastify(envVariables) {

tap.test('Test Post Decorator function', test => {
test.test('Return the signal to not change the response', async assert => {
const fastify = await setupFastify(baseEnv)
const fastify = await setupFastify({ ...baseEnv, MY_REQUIRED_ENV_VAR })

const response = await fastify.inject({
method: 'POST',
Expand All @@ -70,7 +71,7 @@ tap.test('Test Post Decorator function', test => {
})

test.test('Return a modified body response', async assert => {
const fastify = await setupFastify(baseEnv)
const fastify = await setupFastify({ ...baseEnv, MY_REQUIRED_ENV_VAR })

const response = await fastify.inject({
method: 'POST',
Expand Down Expand Up @@ -101,7 +102,7 @@ tap.test('Test Post Decorator function', test => {
})

test.test('Return a modified headers response', async assert => {
const fastify = await setupFastify(baseEnv)
const fastify = await setupFastify({ ...baseEnv, MY_REQUIRED_ENV_VAR })

const response = await fastify.inject({
method: 'POST',
Expand Down Expand Up @@ -131,7 +132,7 @@ tap.test('Test Post Decorator function', test => {
})

test.test('Test a bad handler that doesn\'t return the right type', async assert => {
const fastify = await setupFastify(baseEnv)
const fastify = await setupFastify({ ...baseEnv, MY_REQUIRED_ENV_VAR })

const response = await fastify.inject({
method: 'POST',
Expand Down Expand Up @@ -161,7 +162,7 @@ tap.test('Test Post Decorator function', test => {
})

test.test('abortChain', async assert => {
const fastify = await setupFastify(baseEnv)
const fastify = await setupFastify({ ...baseEnv, MY_REQUIRED_ENV_VAR })

const response = await fastify.inject({
method: 'POST',
Expand Down Expand Up @@ -193,7 +194,7 @@ tap.test('Test Post Decorator function', test => {
})

test.test('is able to access to the mia headers correctly', async assert => {
const fastify = await setupFastify(baseEnv)
const fastify = await setupFastify({ ...baseEnv, MY_REQUIRED_ENV_VAR })

const response = await fastify.inject({
method: 'POST',
Expand Down Expand Up @@ -225,7 +226,7 @@ tap.test('Test Post Decorator function', test => {
})

test.test('addPostDecorator is chainable', async assert => {
const fastify = await setupFastify(baseEnv)
const fastify = await setupFastify({ ...baseEnv, MY_REQUIRED_ENV_VAR })

const response = await fastify.inject({
method: 'POST',
Expand Down
3 changes: 2 additions & 1 deletion tests/services/advanced-custom-service.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@
'use strict'
const envSchema = {
type: 'object',
required: ['MY_AWESOME_ENV'],
required: ['MY_AWESOME_ENV', 'MY_REQUIRED_ENV_VAR'],
properties: {
MY_AWESOME_ENV: { type: 'string', default: 'the default value' },
MY_REQUIRED_ENV_VAR: { type: 'string' },
},
}

Expand Down
Loading

0 comments on commit 6ad65b3

Please sign in to comment.