Skip to content

Commit

Permalink
feat(okam-core): add intercept api init hook support async init
Browse files Browse the repository at this point in the history
  • Loading branch information
wuhy committed Jan 24, 2019
1 parent 16adb63 commit 66ec172
Show file tree
Hide file tree
Showing 2 changed files with 147 additions and 34 deletions.
108 changes: 74 additions & 34 deletions packages/okam-core/src/na/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,31 +5,6 @@

import {isFunction, isObject, isPromise, definePropertyValue} from '../util/index';

/**
* Execute API initialization hook
*
* @inner
* @param {Function} init the initialization hook
* @param {Array} args the API call arguments
* @param {Object} ctx the extra call context for the hook
*/
function hookAPIInit(init, args, ctx) {
// API init interception
if (isFunction(init)) {
let isArrArgs = false;
if (args.length > 1 || !isObject(args[0])) {
// convert as one array type argument to make the developer
// can modify the call arguments directly
args = [args];
isArrArgs = true;
}
init.apply(this, [...args, ctx]);

// restore args type
args = isArrArgs ? args[0] : args;
}
}

/**
* Crate API done hook
*
Expand Down Expand Up @@ -150,21 +125,38 @@ function interceptPromiseResponse(hasIntercepted, promise, doneHookInfo) {
}

/**
* Proxy API
* Execute API initialization hook
*
* @inner
* @param {Object} ctx the extra context information for the hook
* @param {Function} rawApi the original API to call
* @param {Object} apiOpts the API hook options
* @param {...any} args the arguments to call the API
* @param {Function} init the initialization hook
* @param {Array} args the API call arguments
* @param {Object} ctx the extra call context for the hook
* @return {*}
*/
function proxyAPI(ctx, rawApi, apiOpts, ...args) {
let {init, done, sync} = apiOpts;

function hookAPIInit(init, args, ctx) {
// API init interception
hookAPIInit(init, args, ctx);
if (isFunction(init)) {
if (args.length > 1 || !isObject(args[0])) {
// convert as one array type argument to make the developer
// can modify the call arguments directly
args = [args];
}
return init.apply(this, [...args, ctx]);
}
}

/**
* Execute API done hook
*
* @inner
* @param {Function} done the done hook
* @param {boolean} sync whether is sync API, if true, it will not hook the
* API done result based the call arguments
* @param {Array} args the API call arguments
* @param {Object} ctx the extra call context for the hook
* @return {Object} return the api done hook info
*/
function hookAPIDone(done, sync, args, ctx) {
// API done interception
let hasDoneHook = isFunction(done);
let hasIntercepted = false;
Expand All @@ -175,7 +167,25 @@ function proxyAPI(ctx, rawApi, apiOpts, ...args) {
hasIntercepted = interceptAsyncAPIDone(sync, args, doneHookInfo.hook);
}

return {
hasDoneHook,
hasIntercepted,
doneHookInfo
};
}

/**
* Execute api
*
* @inner
* @param {Object} hookInfo the api done hook info
* @param {Function} rawApi the raw api definition
* @param {Array} args the API call arguments
* @return {*}
*/
function executeAPI(hookInfo, rawApi, args) {
// call API
let {hasDoneHook, hasIntercepted, doneHookInfo} = hookInfo;
let result = rawApi.apply(this, args);
if (hasDoneHook) {
// intercept the API call result
Expand All @@ -190,6 +200,36 @@ function proxyAPI(ctx, rawApi, apiOpts, ...args) {
return result;
}

/**
* Proxy API
*
* @inner
* @param {Object} ctx the extra context information for the hook
* @param {Function} rawApi the original API to call
* @param {Object} apiOpts the API hook options
* @param {...any} args the arguments to call the API
* @return {*}
*/
function proxyAPI(ctx, rawApi, apiOpts, ...args) {
let {init, done, sync} = apiOpts;

// API init interception
let initResult = hookAPIInit(init, args, ctx);

if (isPromise(initResult)) {
return initResult.then(
() => {
let hookInfo = hookAPIDone(done, sync, args, ctx);
return executeAPI(hookInfo, rawApi, args);
}
);
}
else {
let hookInfo = hookAPIDone(done, sync, args, ctx);
return executeAPI(hookInfo, rawApi, args);
}
}

/**
* Promisify the given function.
* Notice: the function must be async function and the the first param passed to
Expand Down
73 changes: 73 additions & 0 deletions packages/okam-core/test/tasks/na/api.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -596,4 +596,77 @@ describe('na/api', function () {

});

it('should waiting for init hook done when init hook return promise', function (done) {
let ctx = {
$api: {
api(opts) {
let err = 'err233';
setTimeout(() => {
opts.fail && opts.fail(err);
opts.complete && opts.complete(err);
});
}
}
};

ctx.$api.api = promisify(ctx.$api.api);

let interceptConf = {
api: {
init(options) {
return new Promise((resolve, reject) => {
setTimeout(() => {
options.a = 6;
options.b = true;
resolve();
});
});
},
done(err, data) {
if (err) {
return 'catch';
}
return data + '666';
}
}
};

let spyApi = spyOn(ctx.$api, 'api').andCallThrough();
let spyApiDone = spyOn(interceptConf.api, 'done').andCallThrough();

interceptApis(interceptConf, '$api', ctx);

let spyResolve = createSpy(() => {}).andCallThrough();
let spyReject = createSpy(() => {}).andCallThrough();
let spyFail = createSpy(() => {}).andCallThrough();
ctx.$api.api({a: 3, fail: spyFail}).then(
spyResolve, spyReject
);

setTimeout(() => {
assert(spyApi.calls.length === 1);
let args = spyApi.calls[0].arguments;
assert(args.length === 1);

let expectedKeys = ['a', 'b', 'fail', 'success'];
Object.keys(args[0]).forEach(k => assert(expectedKeys.includes(k)));
assert(args[0].a === 6);
assert(args[0].b === true);

let resErrInfo = 'catch';
assert(spyFail.calls.length === 1);
expect(spyFail).toHaveBeenCalledWith(resErrInfo);

expect(spyApiDone).toHaveBeenCalled();
assert(spyApiDone.calls.length === 1);
expect(spyApiDone.calls[0].arguments).toEqual(['err233', null, ctx]);

expect(spyReject).toNotHaveBeenCalled();
expect(spyResolve).toHaveBeenCalledWith(resErrInfo);
assert(spyResolve.calls.length === 1);

done();
}, 10);
});

});

0 comments on commit 66ec172

Please sign in to comment.