In Koa, can we access the Koa Context without using the pre-bound this?
For example: this is assigned to the Koa Context:
app.use(function *(next) {
this.url;
})
But is there something like:
app.use(function *(next, ctx) {
ctx.url;
})
Why? Say we use an arrow function, this won't be the koa context:
app.use( function *( next ) {
console.log( this.url ); // logs: "/"
( () => console.log( this.url ) )(); // logs: "undefined"
});
I know we could do:
app.use( function *( next ) {
var ctx = this;
( () => console.log( ctx.url ) )(); // logs: "/"
});
and other forms of binding but I wanted to check, that by design, this is the only way.
Accessing this
Koa runs each middleware with this bound to a Context. Access to the request and response require going through the context, this.
This is absolutely by design. If you need to, store a reference to the context in a variable:
var self = this
//or
var ctx = this
Or use Function.prototype.bind:
myArray.forEach( function( item ){
console.log( this.url )
}.bind( this ) )
Design of Koa
I'll paraphrase another answer and the Koa FAQ.
Express is designed to look and feel much like the "standard node way of doing things"; it follows the example set by the http module.
On the other hand, Koa is meant to replace "the node way". The author has chosen to implement his own ideal for handling requests and middleware. Koa exposes it's own versions of the request and response in this.request and this.response, keeping the original node req and res in this.req and this.res, respectively.
These examples should bring the different mindsets into focus.
The Express methodology is very action based.
app.use( function( req, res ){
//SEND the response
res.send( 'some text' )
})
The Koa methodology is very "fact based".
app.use( function*( next ){
//The response IS this
this.body = 'some text'
})
So it seems that instead of passing req and res to functions in a procedural manner, Koa exposes them as properties (or "facts") of the request.
New answer:
It's now available in koa v2.
Old answer:
It is not possible. Middleware is provided to the application via thunks (single argument functions) which are provided by co.
As of writing (April 2015) thunks in co are deprecated. So Koa stays with them or moves to promises is unknown. My 2p, coming from other functional languages, thunks (and bound context) hinders expressiveness... Maybe I'm wrong, but there with arrow functions is one example
Related
My situation is as follows: I am performing sequential HTTP requests, where one HTTP request depends on the response of the previous. I would like to combine the response data of all these HTTP requests into one observable. I have implemented this before using an async generator. The code for this was relatively simple:
async function* AsyncGeneratorVersion() {
let moreItems = true; // whether there is a next page
let lastAssetId: string | undefined = undefined; // used for pagination
while (moreItems) {
// fetch current batch (this performs the HTTP request)
const batch = await this.getBatch(/* arguments */, lastAssetId);
moreItems = batch.more_items;
lastAssetId = batch.last_assetid;
yield* batch.getSteamItemsWithDescription();
}
}
I am trying to move away from async generators, and towards RxJs Observables. My best (and working) attempt is as follows:
const observerVersion = new Observable<SteamItem>((subscriber) => {
(async () => {
let moreItems = true;
let lastAssetId: string | undefined = undefined;
while (moreItems) {
// fetch current batch (this performs the HTTP request)
const batch = await this.getBatch(/* arguments */, lastAssetId);
moreItems = batch.more_items;
lastAssetId = batch.last_assetid;
const items = batch.getSteamItemsWithDescription();
for (const item of items) subscriber.next(item);
}
subscriber.complete();
})();
});
Now, I believe that there must be some way of improving this Observer variant - this code does not seem very reactive to me. I have tried several things using pipe, however unfortunately these were all unsuccessful.
I found concatMap to come close to a solution. This allowed me to concat the next HTTP request as an observable (done with the this.getBatch method), however I could not find a good way to also not abandon the response of the current HTTP request.
How can this be achieved? In short I believe this problem could be described as appending data to an observable inside the observable itself. (But perhaps this is not a good way of handling this situation)
TLDR;
Here's a working StackBlitz demo.
Explanation
Here would be my approach:
// Faking an actual request
const makeReq = (prevArg, response) =>
new Promise((r) => {
console.log(`Running promise with the prev arg as: ${prevArg}!`);
setTimeout(r, 1000, { prevArg, response });
});
// Preparing the sequential requests.
const args = [1, 2, 3, 4, 5];
from(args)
.pipe(
// Running the reuqests sequantially.
mergeScan(
(acc, crtVal) => {
// `acc?.response` will refer to the previous response
// and we're using it for the next request.
return makeReq(acc?.response, crtVal);
},
// The seed(works the same as `reduce`).
null,
// Making sure that only one request is run at a time.
1
),
// Combining all the responses into one object
// and emitting it after all the requests are done.
reduce((acc, val, idx) => ({ ...acc, [`request${idx + 1}`]: val }), {})
)
.subscribe(console.warn);
Firstly, from(array) will emit each item from the array, synchronously and one by one.
Then, there is mergeScan. It is exactly the result of combining scan and merge. With scan, we can accumulate values(in this case we're using it to have access to the response of the previous request) and what merge does is to allow us to use observables.
To make things a bit easier to understand, think of the Array.prototype.reduce function. It looks something like this:
[].reduce((acc, value) => { return { ...acc }}, /* Seed value */{});
What merge does in mergeScan is to allow us to use the accumulator something like (acc, value) => new Observable(...) instead of return { ...acc }. The latter indicates a synchronous behavior, whereas with the former we can have asynchronous behavior.
Let's go a bit step by step:
when 1 is emitted, makeReq(undefined, 1) will be invoked
after the first makeReq(from above) resolves, makeReq(1, 2) will be invoked
after makeReq(1, 2) resolves, makeReq(2, 3) will be invoked and so on...
Somebody I consulted regarding this matter came up with this solution, I think it's quite elegant:
defer(() => this.getBatch(options)).pipe(
expand(({ more_items, last_assetid }) =>
more_items
? this.getBatch({ ...options, startAssetId: last_assetid })
: EMPTY,
),
concatMap((batch) => batch.getSteamItemsWithDescription()),
);
From my understanding the use of expand here is very similar to the use of mergeScan in #Andrei's answer
I have the following scenario: There is a service which is called "ContextProvider" that holds information regarding the context of the applicaiton (Logged In User, Things he can acess, etc). Right now I am observing this as following:
this.contextProvider.Context.subscribe(context => {
//Do Something
})
Now I have a service that will also be observable. I want this service to observe the context and return an observable. This would be easy with the map function:
let observable = this.contextProvider.Context.pipe(map(context => {
let aux: number = somevar + someothervar;
return aux;
})) //observable variable now holds the type Observable<number>
My scenario is a little bit more complex, because in order to fetch the result, I have to make an Http call, which is also an observable/promise:
let observable = this.contextProvider.Context.pipe(map(context => {
return this.httpClient.get<number>("Some URL").pipe(take(1));
})); //observable var now holds Obsevable<Observable<number>>
How can I make the "observable" var hold Observable?
EDIT: The URL value depends on the some values of the "context" variable
If I understand right your problem, you need to use concatMap for this case, like this
this.contextProvider.Context.pipe(
concatMap(context => {
return this.httpClient.get<number>("Some URL" + context.someData);
}));
You can find more patterns around the use of Observables with http calls in this article
I am trying to use the same AWS Lambda function to do two things with the same DynamoDB dataset.
(a) Provide Alexa Skill Responses
I have already implemented this, and the Skill is operating correctly. I am using NodeJS and the Amazon Skills Kit version 2. The last few lines of my index.js are as follows:
const skillBuilder = Alexa.SkillBuilders.standard();
exports.handler = skillBuilder
.addRequestHandlers(
LaunchRequest,
HelpIntent,
// .... various other intents
UnhandledIntent
)
.addErrorHandlers(ErrorHandler)
.lambda();
(b) Provide a JSON object summarising some database contents
The NodeJS code I have set up for the Alexa skill, processes DynamoDB data in order to provide the skill responses. I want to use the same codebase (i.e. the same Lambda function) to produce summaries for my own benefit. I don't want to copy-paste pieces of the Lambda function into a separate function. I would much rather have the same code do both tasks, in order to keep everything in step.
My problem is that the structure of the Lambda code for returning this JSON response to my request is as follows, from https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-create-api-as-simple-proxy-for-lambda.html:
'use strict';
console.log('Loading hello world function');
exports.handler = function(event, context, callback) {
let name = "you";
let city = 'World';
// etc ... more code ...
callback(null, response);
};
Both pieces of code assign a function to `exports.handler`
I think I want to achieve this effect:
if ( /* test whether being called by Alexa or by API Gateway */ )
{ /* Make the Alexa Response */ }
else
{ /* Construct the JSON data summary response */ }
From what I can make out, each Lambda function (in the sense of each Amazon Resource Number for a Lambda function) has to have only one entry file, i.e. I can't make Lambda start index_Alexa.js versus index_JSON.js.
I am open to any suggestion on how to get the same Lambda function (in the sense of the same JS file or package of files) do both things.
I question the usefulness of the approach somewhat, and the following has some potential for optimization remaining, but one way of accomplishing this is to declare exports.handler as a simple wrapper that invokes the correct previously-declared handler function based on a condition you can test in the request.
// set up handler for alexa
const skillBuilder = Alexa.SkillBuilders.standard();
const alexa_handler = skillBuilder
.addRequestHandlers(
LaunchRequest,
HelpIntent,
// .... various other intents
UnhandledIntent
)
.addErrorHandlers(ErrorHandler)
.lambda();
// set up handler for API Gateway
const api_gateway_handler = function(event, context, callback) {
let name = "you";
let city = 'World';
// etc ... more code ...
callback(null, response);
};
// for each invocation, choose which of the above to invoke
exports.handler = function(event, context, callback) {
if(/* some test that is true only for API Gateway */)
{
api_gateway_handler(event, context, callback);
}
else
{
alexa_handler(event, context, callback);
}
};
The line if(/* some test that is true only for API Gateway */) is something you'll need to work out, but I suspect something like this might work:
if(event.input && event.input.requestContext && event.input.requestContext.apiId)
The API Gateway docs suggest that this value would always be present.
Background
I am trying to convert a code snippet from good old Promises into something using Flutures and Sanctuary:
https://codesandbox.io/embed/q3z3p17rpj?codemirror=1
Problem
Now, usually, using Promises, I can uses a library like sinonjs to stub the promises, i.e. to fake their results, force to resolve, to reject, ect.
This is fundamental, as it helps one test several branch directions and make sure everything works as is supposed to.
With Flutures however, it is different. One cannot simply stub a Fluture and I didn't find any sinon-esque libraries that could help either.
Questions
How do you stub Flutures ?
Is there any specific recommendation to doing TDD with Flutures/Sanctuary?
I'm not sure, but those Flutures (this name! ... nevermind, API looks cool) are plain objects, just like promises. They only have more elaborate API and different behavior.
Moreover, you can easily create "mock" flutures with Future.of, Future.reject instead of doing some real API calls.
Yes, sinon contains sugar helpers like resolves, rejects but they are just wrappers that can be implemented with callsFake.
So, you can easily create stub that creates fluture like this.
someApi.someFun = sinon.stub().callsFake((arg) => {
assert.equals(arg, 'spam');
return Future.of('bar');
});
Then you can test it like any other API.
The only problem is "asynchronicity", but that can be solved like proposed below.
// with async/await
it('spams with async', async () => {
const result = await someApi.someFun('spam).promise();
assert.equals(result, 'bar');
});
// or leveraging mocha's ability to wait for returned thenables
it('spams', async () => {
return someApi.someFun('spam)
.fork(
(result) => { assert.equals(result, 'bar');},
(error) => { /* ???? */ }
)
.promise();
});
As Zbigniew suggested, Future.of and Future.reject are great candidates for mocking using plain old javascript or whatever tools or framework you like.
To answer part 2 of your question, any specific recommendations how to do TDD with Fluture. There is of course not the one true way it should be done. However I do recommend you invest a little time in readability and ease of writing tests if you plan on using Futures all across your application.
This applies to anything you frequently include in tests though, not just Futures.
The idea is that when you are skimming over test cases, you will see developer intention, rather than boilerplate to get your tests to do what you need them to.
In my case I use mocha & chai in the BDD style (given when then).
And for readability I created these helper functions.
const {expect} = require('chai');
exports.expectRejection = (f, onReject) =>
f.fork(
onReject,
value => expect.fail(
`Expected Future to reject, but was ` +
`resolved with value: ${value}`
)
);
exports.expectResolve = (f, onResolve) =>
f.fork(
error => expect.fail(
`Expected Future to resolve, but was ` +
`rejected with value: ${error}`
),
onResolve
);
As you can see, nothing magical going on, I simply fail the unexpected result and let you handle the expected path, to do more assertions with that.
Now some tests would look like this:
const Future = require('fluture');
const {expect} = require('chai');
const {expectRejection, expectResolve} = require('../util/futures');
describe('Resolving function', () => {
it('should resolve with the given value', done => {
// Given
const value = 42;
// When
const f = Future.of(value);
// Then
expectResolve(f, out => {
expect(out).to.equal(value);
done();
});
});
});
describe('Rejecting function', () => {
it('should reject with the given value', done => {
// Given
const value = 666;
// When
const f = Future.of(value);
// Then
expectRejection(f, out => {
expect(out).to.equal(value);
done();
});
});
});
And running should give one pass and one failure.
✓ Resolving function should resolve with the given value: 1ms
1) Rejecting function should reject with the given value
1 passing (6ms)
1 failing
1) Rejecting function
should reject with the given value:
AssertionError: Expected Future to reject, but was resolved with value: 666
Do keep in mind that this should be treated as asynchronous code. Which is why I always accept the done function as an argument in it() and call it at the end of my expected results. Alternatively you could change the helper functions to return a promise and let mocha handle that.
I'm using Mocha to test a Node.js API. I need to test a route that checks whether an update is available. The route accepts a version number of what's currently installed and compares it with an available version retrieved via an HTTP request.
To get the latest version available, a model method is called. Client.availableVersion() makes the HTTP request, does some simple manipulation and passes the available version to a callback function. Fairly straightforward stuff, but now I want to test my update() route and I need to be able to control what is returned by Client.availableVersion().
Having read the Mocha docs and the Sinon docs I was referred to...I'm stuck. It sounds like Sinon will handle what I need, but the implementation has me a little confused.
Here's a snippet from my routes method:
update: function( req, res, next ) {
var installedVersion = req.params.version;
client.availableVersion( function( err, availableVersion ) {
if( !err ) {
if( parseInt( installedVersion, 10 ) < parseInt( availableVersion, 10 ) ) {
// SEND UPDATE AVAILABLE RESPONSE
}
// SEND NO UPDATE AVAILABLE RESPONSE
}
else {
res.send( 500, err );
}
})
}
Any nudge in the right direction would be appreciated.
I would definitely go with stubs. http://sinonjs.org/docs/#stubs
I would use the stub.yields(...)
var next = sinon.spy()
var err = ... //Whatever object you want to pass as error or undefined
var availbleVersion = ... // A fake of the response the method is supposed to give
client.availableVersion = sinon.stub().yields(err, availableVersion);
myRoute.update(fakeReq, fakeRes, next);
//All you expectations