[WIP]
Refer : https://grpc.io/docs/languages/node/basics/
Todo
Done
Blueprint
About it.
Features
- record and playback gRPC endpoints
- custom matchers and templates
- sessoin support
- run as docker image, cli command or as node module.
Kinds of gRPC API supported
- single request- single response
- single request - streaming response
- streaming request - single response
- streaming request - streaming response
Installation
-
Run as node module
npx miraje
npx miraje -port 3000 -host 0.0.0.0 -config ./config -protos ./protos
npx miraje -port 3000 -host 0.0.0.0 -config ./config -protos ./protos -recording -remotehost: 0.0.0.0 remoteport: 8080
Default values :
-
config : ./config
-
protos : ./protos
-
port : 5055
-
host : 0.0.0.0
-
Run as docker image
docker run -p 3000:80 -v /path/to/config:/config saxolab/miraje -host 0.0.0.0 -protos ./protos
docker run -p 3000:80 -v /path/to/config:/config saxolab/miraje -host 0.0.0.0 -recording -remotehost: 0.0.0.0 remoteport: 8080 -protos ./protos
-
Install as node module and use api in node tests
npm install --save miraje
Run in player mode:
const app = require('miraze/app');
const parameters = {
host : "0.0.0.0",
port : "3000",
configPath : `${process.cwd()}/config`,
protosPath : `${process.cwd()}/protos`,
};
app.run(parameters);
Run in recorder mode
const app = require('miraze/app');
const parameters = {
host : "0.0.0.0",
port : "50054",
configPath : `${process.cwd()}/tests/fixtures/config`,
protosPath : `${process.cwd()}/tests/fixtures/protos`,
recording: true,
remoteHost : "localhost",
remotePort : "3000"
};
app.run(parameters);
Sample strucuture of config folder
stub/
- config/
- mappings.yaml
- greet/
- default.js.yaml
- rohit.js.yaml
- virat.js.yaml
- prices/
- defualt.js.yaml
- common/
- message/js.yml
Configurations
Configuration file shoud be placed in the config
dir and named as config.yaml
Example of a cofiguration file :
sessionEnabled : true
keywordSuffix : '@'
trimStreaming : 10
Name | default | |
---|
expressionSymbols | ["{{", "}}"] | e.g {{request.body.name}} |
sessionEnabled | true | |
keywordSuffix | /^@/ | ends with @ |
trimStreaming | 10 | number for responses for a streaming server to keep in mappings file. Will repeast the responses unless configured otherwise per request basis. |
Defining Mappings
To define a stubbed grpc endpoint, first update the config/mappings.yaml
in config directory
helloworld.greet.Greeter.SayHello : [
"greet/rohit.js.yaml",
"greet/virat.js.yaml",
"greet/default.js.yaml",
]
prices.streaming.Pricing.Subscribe : [
"prices/211-Stock.js.yaml",
]
Remember that the responses are applied in the order declared in mappings file. So if "greet/rohit.js.yaml"
matches the request, the response will be returned based on this file.
Define responses
A simple unary request :
request@ : {
name: "rohit"
}
response@ : {
message : "Hello Rohit"
}
For a streaming response :
request@ : {
uic: 211,
assetType: "Stock"
}
response@ : {
"stream@" : [
{quote: "quote:one"},
{quote: "quote:two"},
{quote: "quote:three"}
]
}
Using matchers and templates
For some endpoint, it is must to have part of response based on request body. In such case we can use the template :
request@ : {
name: "any@"
}
response@ : {
message : "Glad to meet you {{request.body.name}}"
}
Including templates
If your response is extremly complex and you need to parameterize certaion parts of it, you can use partial templates. E.g.
request@: {
uic: 211,
assetType: "Stock"
}
reply@: {
stream@: [
{ message: "this is your first message"},
{ message: "this is your second message"},
{
include@: "shared/message-template.js.yaml",
param@: {username: "{{request.body.name}}"}
},
],
repeat@: false
}
Scripting in templates
You can add custom script to handle complex scnarios :
request@ : {
name : "any@"
lastName: "Singh"
js@: `
return request.matches && request.body.age > 18
`
}
You can even write your own code to create responses dynamically using javascript :
request@: {
stream@: [
{name: "first-user"},
{name: "second-user"},
]
}
@reply: {
stream@: {
js@: "
endpoint.calls = endpoint.calls || 0;
endpoint.calls++;
scope.calls = scope.calls || 0;
scope.calls++;
var message = `
hello : ${request.body.name},
total calls to this endpoint : ${endpoint.calls},
total replies by this rule : ${scope.calls},
sequence in stream : ${stream.$index}`;
return {message};
"
}
}
Extensions
You can write your own javascript code to add custom logic to templates. Create a directory config/ext
and simply put you javascript file there.
Create custom matchers
To create custom matcher, create a file ``config/ext /asset-types.js` as :
const matchers = require('miraje/matchers');
const validAssetTypes = ['Stock', 'CfdOnFutures', 'FxSpot'];
module.exports = {
appliesTo : (str) => str === 'assetTypes@',
matches : (value) => validAssetTypes.includes(value)
}
Sessions
In case you wan't to build a stateful stub (not recommened), you can use sessions. To enable sessions make sure thatsessionEnabled: true
is set inconfig/config.yaml
Then you can use sessions in your matchers or templates :
request@: {
name: 'rohit'
}
@reply: {
stream@: {
response: {
message: 'You called me {{session[request.name]}} times.'
}
js@: '
session[request.name] = session[request.name] || 0;
session[request.name] += session[request.name];
'
}
}
install locally to test :
npm link
Roadmap