mali
Version:
Minimalistic gRPC microservice framework
207 lines (188 loc) • 5.71 kB
JavaScript
const grpc = require('@grpc/grpc-js')
const CallType = require('@malijs/call-types')
const Metadata = require('./metadata')
/**
* Mali Response class that encapsulates the response of a call.
* Clients to not create this. Mali does it for us.
*/
class Response {
/**
* Creates a Mali Response instance
* @param {Object} call the grpc call instance
* @param {String} type the call type. one of `@malijs/call-types` enums.
*/
constructor (call, type) {
this.call = call
this.type = type
if (type === CallType.DUPLEX) {
this.res = call
}
}
/**
* Sets specific response header metadata field value
* @param {String|Object} field the metadata field name or object for metadata
* @param {*} [val] the value of the field
* @example <caption>Using string field name and value</caption>
* ctx.response.set('foo', 'bar')
* @example <caption>Using object</caption>
* ctx.response.set({
* foo: 'bar'
* })
*/
set (field, val) {
if (arguments.length === 2) {
if (!this.metadata) {
this.metadata = {}
}
this.metadata[field] = val
} else {
const md = field instanceof grpc.Metadata ? field.getMap() : field
if (typeof md === 'object') {
for (const key in md) {
this.set(key, md[key])
}
}
}
}
/**
* Gets the response header metadata value
* @param {String} field the field name
* @return {*} the metadata field value
* @example
* console.log(ctx.response.get('foo')) // 'bar'
*/
get (field) {
let val
if (this.metadata) {
val = this.metadata[field]
}
return val
}
/**
* Gets the response metadata as a `grpc.Metadata` object instance
* @return {Object} response metadata
*/
getMetadata () {
return Metadata.create(this.metadata)
}
/**
* Sends the response header metadata. Optionally (re)sets the header metadata as well.
* @param {Object} md optional header metadata object to set into the request before sending
* if there is existing metadata in the response it is cleared
* if param is not provided `sendMetadata` sends the existing metadata in the response
* @example <caption>Set and send</caption>
* ctx.response.sendMetadata({
* foo: 'bar'
* })
* @example <caption>Set and send later</caption>
* ctx.response.set('foo', 'bar')
* // ... later
* ctx.response.sendMetadata()
*/
sendMetadata (md) {
// if forcing send reset our metadata
if (md && (typeof md === 'object' || md instanceof grpc.Metadata)) {
this.metadata = null
this.set(md)
}
const data = this.getMetadata()
if (data) {
this.call.sendMetadata(data)
}
}
/**
* Gets the response status / trailer metadata value
* @param {String} field the field name
* @return {*} the metadata field value
* @example
* console.log(ctx.response.getStatus('bar')) // 'baz'
*/
getStatus (field) {
let val
if (this.status) {
val = this.status[field]
}
return val
}
/**
* Sets specific response status / trailer metadata field value
* @param {String|Object} field the metadata field name or object for metadata
* @param {*} val the value of the field
* @example <caption>Using string field name and value</caption>
* ctx.response.setStatus('foo', 'bar')
* @example <caption>Using object</caption>
* ctx.response.setStatus({
* foo: 'bar'
* })
*/
setStatus (field, val) {
if (arguments.length === 2) {
if (!this.status) {
this.status = {}
}
this.status[field] = val
} else {
const md = field instanceof grpc.Metadata ? field.getMap() : field
if (typeof md === 'object') {
for (const key in md) {
this.setStatus(key, md[key])
}
}
}
}
/**
* Gets the response status / trailer metadata as a `grpc.Metadata` object instance
* @return {Object} response status / trailer metadata
*/
getStatusMetadata () {
return Metadata.create(this.status, { addEmpty: false })
}
}
/**
* The internal gRPC call instance reference.
* @member {Object} call
* @memberof Response#
*/
/**
* The call's type. One of `@malijs/call-types` enums.
* This will match Request's type.
* @member {String} type
* @memberof Response#
* @example
* console.log(ctx.response.type) // 'unary'
*/
/**
* The call's response header metadata plain object if present.
* @member {Object} metadata
* @memberof Response#
* @example
* ctx.response.set('foo', 'bar')
* console.log(ctx.response.metadata) // { 'foo': 'bar' }
*/
/**
* The call's response trailer / status metadata plain object if present.
* @member {Object} status
* @memberof Response#
* @example
* ctx.response.setStatus('biz', 'baz')
* console.log(ctx.response.status) // { biz: 'baz' }
*/
/**
* The call's response actual payload object / stream.
* In case of `DUPLEX` call this is automatically set the actual `call` instance.
* @member {Object|Stream} res
* @memberof Response#
* @example <caption>UNARY or REQUEST STREAM calls</caption>
* ctx.response.res = { foo: 'bar' }
* @example <caption>RESPONSE STREAM calls</caption>
* ctx.response.res = createResponseStream()
* @example <caption>DUPLEX calls</caption>
* ctx.response.res.write({ foo: 'bar' })
* @example <caption>Custom Response Stream calls</caption>
* ctx.response.res = new Response({ object: true});
* ctx.response.res.push({ data: 'hello 1' });
* ctx.response.res.push({ data: 'hello 2' });
* ctx.response.res.push({ data: 'hello 3' });
* ctx.response.res.push(null);
*/
module.exports = Response