Comparing version 0.3.1 to 0.3.2
{ | ||
"name": "apicase", | ||
"version": "0.3.1", | ||
"version": "0.3.2", | ||
"description": "Create, group and manage your APIs with json declaration", | ||
@@ -5,0 +5,0 @@ "author": "Anton Kosykh", |
@@ -26,15 +26,30 @@ import * as Utils from './utils' | ||
}, | ||
// Normalize and add mixin | ||
addMixin (name, mixin) { | ||
this.mixins[name] = Utils.normalizeMixin(mixin) | ||
}, | ||
// Normalize and add hook | ||
addHook (name, hook) { | ||
this.hooks[name].push(Utils.normalizeHook(hook)) | ||
}, | ||
// Add hooks list | ||
addHooks (list) { | ||
merge(this.hooks, Utils.normalizeHooks(list)) | ||
}, | ||
// Add service in container | ||
// unfolds children services (if exists) | ||
addService (name, service) { | ||
this.services[name] = createService(service, result) | ||
set(this.services, name, createService(service, result)) | ||
if (!service.children) { | ||
this.services[name] = service | ||
return | ||
} | ||
Object.assign( | ||
this.services, | ||
mapValues( | ||
Utils.flatServices({ name: service }), | ||
service => createService(service, this) | ||
) | ||
) | ||
}, | ||
// Call service | ||
async go (name, data, options = {}) { | ||
@@ -45,2 +60,10 @@ return await this.services[name].go(data, options) | ||
if (config.base) { | ||
result.base = config.base | ||
} | ||
if (config.headers) { | ||
result.headers = config.headers | ||
} | ||
if (config.services) { | ||
@@ -52,5 +75,7 @@ result.services = mapValues( | ||
} | ||
if (config.hooks) { | ||
result.addHooks(config.hooks) | ||
} | ||
if (config.mixins) { | ||
@@ -61,11 +86,18 @@ forEach(config.mixins, (mixin, name) => { | ||
} | ||
if (config.headers) { | ||
result.headers = config.headers | ||
} | ||
if (config.base) { | ||
result.base = config.base | ||
} | ||
// Add nested variant for services | ||
// Note that you should not use reserved words for service names | ||
// It can cause unexpected behavior | ||
// TODO: show warning on using bad names for services | ||
Object.defineProperty(result, 'tree', { | ||
get () { | ||
return zipObjectDeep( | ||
Object.keys(this.services).map(item => item.split('/').join('.')), | ||
Object.values(this.services) | ||
) | ||
} | ||
}) | ||
return result | ||
} |
import Service from './service.js' | ||
import Container from './container.js' | ||
import { mergeHooksList } from './utils.js' | ||
import { ApiAbort, ApiError } from './errors' | ||
export default { | ||
module.exports = { | ||
ApiAbort, | ||
ApiError, | ||
Service, | ||
@@ -7,0 +10,0 @@ Container, |
import * as Utils from './utils' | ||
import omit from 'lodash-es/omit' | ||
import mapValues from 'lodash-es/mapValues' | ||
export default function createResult (service) { | ||
return { | ||
let result = { | ||
ok: false, | ||
query: null, | ||
headers: null, | ||
body: null, | ||
pending: false, | ||
aborted: false, | ||
ok: false, | ||
reason: null, | ||
response: null, | ||
result: null, | ||
// Set query | ||
setQuery (query) { | ||
this.query = query | ||
}, | ||
// Set headers | ||
setHeaders (headers) { | ||
this.headers = headers | ||
}, | ||
// Set response and result | ||
setResult (response, res = undefined) { | ||
@@ -27,6 +31,14 @@ this.response = response | ||
}, | ||
// Set aborted flag | ||
// set abort reason or null | ||
setAborted (reason = null) { | ||
this.aborted = true | ||
this.reason = reason | ||
}, | ||
// Normalize new mixins | ||
// merge it with already added | ||
setMixins (list) { | ||
Object.defineProperties(this, mapValues( | ||
Object.assign( | ||
{}. | ||
{}, | ||
service.container.mixins, | ||
@@ -40,2 +52,18 @@ service.config.mixins, | ||
} | ||
// Returns clean data without methods | ||
Object.defineProperty(result, 'clean', { | ||
get () { | ||
return omit(this, [ | ||
'clean', | ||
'setQuery', | ||
'setResult', | ||
'setMixins', | ||
'setAborted', | ||
'setHeaders' | ||
]) | ||
} | ||
}) | ||
return result | ||
} |
import * as Utils from './utils' | ||
import { ApiAbort } from './errors' | ||
import createResult from './result' | ||
@@ -14,2 +15,3 @@ | ||
// Merge hooks | ||
let prepareHooks = function (hooks = {}) { | ||
@@ -23,2 +25,3 @@ return Utils.mergeHooksList( | ||
// Merge mixins | ||
let prepareMixins = function (mixins = {}) { | ||
@@ -32,2 +35,4 @@ return Object.assign( | ||
// Add query string for GET requests | ||
// return path-to-regexp method | ||
let prepareUrl = function (data = {}) { | ||
@@ -42,2 +47,3 @@ let url = `${state.container.base}${state.config.url}` | ||
// Generate options object for fetch | ||
let prepareOptions = function (data, headers) { | ||
@@ -51,2 +57,3 @@ return { | ||
// Merge headers | ||
let prepareHeaders = function (headers = {}) { | ||
@@ -67,2 +74,3 @@ let normalizeHeaders = (headers) => | ||
// Returns corrected request body object | ||
let prepareBody = function (data = {}) { | ||
@@ -76,2 +84,3 @@ return state.config.method === 'GET' | ||
// Prepare query callback | ||
let prepareCallback = function (params = {}) { | ||
@@ -104,28 +113,28 @@ let self = state | ||
let prepareBeforeHook = function (hook) { | ||
// Wrap before hook in function | ||
// with abort features | ||
let prepareBeforeHook = function (hook, temp) { | ||
return async (ctx, next) => { | ||
let result | ||
let abortInfo = {} | ||
await hook.handler( | ||
ctx, | ||
(data, abortData = {}) => { | ||
result = data | ||
abortInfo = abortData | ||
(data = undefined) => { | ||
if (data === false) { | ||
temp.abort = true | ||
return | ||
} | ||
if (data instanceof ApiAbort) { | ||
temp.abort = data | ||
return | ||
} | ||
if (data !== undefined) { | ||
ctx.query = data | ||
} | ||
} | ||
) | ||
switch (result) { | ||
case undefined: | ||
next() | ||
break | ||
case false: | ||
ctx.aborted = true | ||
ctx.abortInfo = abortInfo | ||
break | ||
default: | ||
ctx.query = result | ||
next() | ||
} | ||
if (!temp.abort) next() | ||
} | ||
} | ||
// Wrap success and after hooks in function | ||
// that no need to call next() | ||
let prepareAfterHook = function (hook) { | ||
@@ -138,2 +147,11 @@ return async (ctx, next) => { | ||
// Wrap aborted hook in function | ||
// with abort reason in second argument | ||
let prepareAbortedHook = function (hook, reason) { | ||
return async (ctx, next) => { | ||
await hook.handler(ctx, reason) | ||
next() | ||
} | ||
} | ||
let state = { | ||
@@ -161,42 +179,73 @@ config, | ||
result.setQuery(data) | ||
result.aborted = false | ||
let callback = prepareCallback(omit(params, ['hooks', 'mixins'])) | ||
let queue = [ | ||
...hooks.before.map(prepareBeforeHook), | ||
callback | ||
] | ||
return new Promise(async (resolve) => { | ||
let endCallback = (ctx, next) => { | ||
this.calls++ | ||
resolve(ctx) | ||
resolve(ctx.clean) | ||
next() | ||
} | ||
try { | ||
await compose(hooks.before.map(prepareBeforeHook))(result) | ||
if (result.aborted) { | ||
if ('type' in result.abortInfo) { | ||
await compose(hooks[result.abortInfo.type].map(prepareAfterHook))(result) | ||
let temp = { | ||
abort: false | ||
} | ||
await compose( | ||
hooks.before.map(hook => prepareBeforeHook(hook, temp)) | ||
)(result) | ||
// If call is aborted in before hook | ||
if (temp.abort) { | ||
// next(false) | ||
if (temp.abort === true) { | ||
result.setAborted(null) | ||
return | ||
} | ||
if ('name' in result.abortInfo) { | ||
if (typeof temp.abort !== 'object') { | ||
throw new Error('Abort info should be an object') | ||
} | ||
result.setAborted(temp.abort.reason) | ||
// next(new ApiAbort(data, { type: 'error' })) | ||
if (temp.abort.info.type) { | ||
await compose(hooks[temp.abort.info.type].map(prepareAfterHook))(result) | ||
} | ||
// next(new ApiAbort(data, { name: 'someHook' })) | ||
if (temp.abort.info.name) { | ||
let abortHooks = flatten(values(hooks)) | ||
if (!abortHooks.length) return | ||
await compose(abortHooks.map(prepareAfterHook))(result) | ||
await compose( | ||
abortHooks.map(prepareAfterHook) | ||
)(result) | ||
} | ||
return | ||
// next(new ApiAbort(reason)) | ||
if (!temp.abort.info.type && !temp.abort.info.name) { | ||
await compose( | ||
hooks.aborted.map(hook => prepareAbortedHook(hook, temp.abort.reason)) | ||
)(result) | ||
} | ||
} else { | ||
// Start query | ||
result.pending = true | ||
await callback(result) | ||
result.pending = false | ||
// Success hooks | ||
await compose( | ||
hooks.success.map(prepareAfterHook) | ||
)(result) | ||
} | ||
result.pending = true | ||
await callback(result) | ||
result.pending = false | ||
await compose(hooks.success.map(prepareAfterHook))(result) | ||
} catch (err) { | ||
// Log error in debug mode | ||
if (this.container.debug) { | ||
console.error(err) | ||
} | ||
result.pending = false | ||
await compose(hooks.error.map(prepareAfterHook))(result) | ||
// Call error hooks | ||
if (result.aborted) return | ||
await compose( | ||
hooks.error.map(prepareAfterHook) | ||
)(result) | ||
} finally { | ||
await compose([ | ||
...hooks.finished.map(prepareAfterHook), | ||
endCallback | ||
])(result) | ||
if (result.aborted) return | ||
await compose( | ||
hooks.finished.map(prepareAfterHook).concat(endCallback) | ||
)(result) | ||
} | ||
@@ -203,0 +252,0 @@ }) |
@@ -13,2 +13,3 @@ import pick from 'lodash-es/pick' | ||
// Convert mixin to getter | ||
export const normalizeMixin = function (mixin) { | ||
@@ -31,2 +32,3 @@ if (isFunction(mixin)) { | ||
// Convert hook into { name, handler } object | ||
export const normalizeHook = function (hook) { | ||
@@ -48,2 +50,3 @@ if (isFunction(hook)) { | ||
// Normalize hooks array | ||
export const normalizeHooks = function (hooks) { | ||
@@ -53,11 +56,12 @@ return mapValues( | ||
(hooks, hookType) => | ||
Array.isArray(hooks) | ||
? hooks.map(normalizeHook) | ||
: [hooks].map(normalizeHook) | ||
flatten([hooks]).map(normalizeHook) | ||
) | ||
} | ||
// Normalize hooks object | ||
// add missed hook types arrays | ||
// add new hooks into result object | ||
export const mergeHooksList = function (...lists) { | ||
return mergeWith( | ||
{ before: [], success: [], error: [], finished: [] }, | ||
{ before: [], success: [], error: [], finished: [], aborted: [] }, | ||
...lists, | ||
@@ -71,2 +75,4 @@ (a, b) => | ||
// Convert services with children | ||
// into flat object with `service/nested/name` keys | ||
export const flatServices = function (services, alias = '') { | ||
@@ -83,2 +89,3 @@ let result = {} | ||
// Convert json object to query string | ||
export const jsonToQueryString = json => { | ||
@@ -85,0 +92,0 @@ return !isPlainObject(json) || !Object.keys(json).length |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
479567
13
5639