Related
I am trying to generate a credential using the didkit-wasm library with the following code, but getting error with prepareIssueCredential method: key expansion failed.
Any idea what i could be doing wrong
const did = `did:pkh:tz:` + userData.account.address;
const credential = {
'#context': [
'https://www.w3.org/2018/credentials/v1',
{
alias: 'https://schema.org/name',
description: 'https://schema.org/description',
website: 'https://schema.org/url',
logo: 'https://schema.org/logo',
BasicProfile: 'https://tzprofiles.com/BasicProfile',
},
],
id: 'urn:uuid:' + uuid(),
issuer: 'did:pkh:tz:tz1ZDSnw...',
issuanceDate: new Date().toISOString(),
type: ['VerifiableCredential', 'Company Credential'],
credentialSubject: {
id: did,
name: company.name,
description: company.description,
url: company.url,
},
};
let credentialString = JSON.stringify(credential);
const proofOptions = {
verificationMethod: did + '#TezosMethod2021', //subject's did
proofPurpose: 'assertionMethod',
};
const publicKeyJwkString = await JWKFromTezos(
'edpkuGHxcJDq9....' //issuer's public key
);
let prepStr = await prepareIssueCredential(
credentialString,
JSON.stringify(proofOptions),
publicKeyJwkString
);
So, sounds like this question was asked before, but actually this one is different.
The problem is that Geocoding API is modifying the given address.
address = '750,West Broadway,Vancouver,V5Z 1H2,British Columbia,Canada';
https://maps.googleapis.com/maps/api/geocode/json?address=address&key=API_KEY
1.
Response from Geocoding Static API url:
{
formatted_address: '750 W Broadway, Vancouver, BC V5Z 1J4, Canada',
place_id: 'ChIJ_RRP_8NzhlQRVS9i9yqSAKY'
}
2.
Response from Geocoder Class with the same address:
{
formatted_address: '750 W Broadway, Vancouver, BC V5Z 1H2, Canada',
place_id: 'ChIJxQEI2cJzhlQRf44wwGpQBNQ'
}
Different place_id and Post Code.
This creates a problem when using the place id with other services like Directions, StreetView.
const getGoogleMapAndStreetView = async (placeId) => {
const geocoder = new map.Geocoder();
const streetViewService = new map.StreetViewService();
const directionsService = new map.DirectionsService();
const geoResult = await geocoder.geocode(
{
placeId: placeId,
},
(geoResult, status) => {
if (status === "OK") {
return geoResult;
}
}
);
const origin = { placeId: geoResult.results[0].place_id };
const directionsResult = await directionsService.route(
{
origin: origin,
destination: origin,
travelMode: map.TravelMode.DRIVING,
},
(directionsResult, status) => {
if (status === "OK") {
return directionsResult;
}
}
);
const panoResult = await streetViewService.getPanorama(
{
location: directionsResult.routes[0].legs[0].start_location,
source: map.StreetViewSource.OUTDOOR,
radius: 50,
},
(panoResult, status) => {
if (status === "OK") {
return panoResult;
}
}
);
const newHeading = map.geometry.spherical.computeHeading(
new map.LatLng(panoResult.data.location.latLng),
new map.LatLng(geoResult.results[0].geometry.location)
);
return {
lat: geoResult.results[0].geometry.location.lat(),
lng: geoResult.results[0].geometry.location.lng(),
heading: newHeading,
pano: panoResult.data.location.pano,
};
};
I have a weird scenario here.
The following line in my Pulumi typescript code always fails the first time:
const redisCache = new gcp.redis.Instance("my-redis-cache", {
name: "my-metadata-cache",
tier: "BASIC",
memorySizeGb: 1,
authorizedNetwork: pulumi.interpolate`projects/someprojectid/global/networks/default`,
connectMode: "PRIVATE_SERVICE_ACCESS",
redisVersion: "REDIS_6_X",
displayName: "My Metadata Cache",
project: someprojectid,
}, defaultResourceOptions);
**
error: 1 error occurred:
* Error waiting to create Instance: Error waiting for Creating Instance: Error code 13, message: an internal error has occurred
**
Strangely, when I again run pulumi up, it succeeds. Has anyone else faced this before? Any clues?
Ok this turned out to be a case of working with a beast of a code. Once I started isolating the issue, things became clearer. For those who stumble across this one, here is a full working code.
import * as pulumi from "#pulumi/pulumi";
import * as gcp from "#pulumi/gcp";
export interface CacheComponentResourceArgs {
projectId : pulumi.Input<string>;
projectNumber: pulumi.Input<string>;
}
export class CacheComponentResource extends pulumi.ComponentResource {
constructor(name: string, resourceArgs: CacheComponentResourceArgs, opts?: pulumi.ResourceOptions) {
const inputs: pulumi.Inputs = {
options: opts,
};
super("ekahaa:abstracta:Cache", name, inputs, opts);
const serviceNetworkingAccessService = new gcp.projects.Service("service-nw-" + name , {
disableDependentServices: true,
project: resourceArgs.projectId,
service: "servicenetworking.googleapis.com",
}, {
parent : this
});
const redisService = new gcp.projects.Service("redis-service-" + name, {
disableDependentServices: true,
project: resourceArgs.projectId,
service: "redis.googleapis.com",
}, {
parent : this
});
const defaultGlobalAddress = new gcp.compute.GlobalAddress("default-ip-range-" + name, {
name: "default-ip-range",
purpose: "VPC_PEERING",
prefixLength: 16,
project: resourceArgs.projectId,
addressType: "INTERNAL",
network: pulumi.interpolate`projects/${resourceArgs.projectId}/global/networks/default`
}, {
parent : this,
dependsOn: [ redisService]
});
const privateServiceConnection = new gcp.servicenetworking.Connection("servicenetworking-" + name, {
service: "servicenetworking.googleapis.com",
network: pulumi.interpolate`projects/${resourceArgs.projectId}/global/networks/default`,
reservedPeeringRanges: [defaultGlobalAddress.name],
}, {
parent : this,
dependsOn: [ defaultGlobalAddress]
});
const iamBindingRedis2 = new gcp.projects.IAMBinding("iamredis2-" + name, {
members: [
pulumi.interpolate`serviceAccount:service-${resourceArgs.projectNumber}#service-networking.iam.gserviceaccount.com`
],
role: "roles/servicenetworking.serviceAgent",
project: resourceArgs.projectId
}, {
parent : this,
dependsOn: [privateServiceConnection]
});
const redisCache = new gcp.redis.Instance(name, {
name: name,
tier: "BASIC",
memorySizeGb: 1,
authorizedNetwork: pulumi.interpolate`projects/${resourceArgs.projectId}/global/networks/default`,
connectMode: "PRIVATE_SERVICE_ACCESS",
redisVersion: "REDIS_6_X",
displayName: "Abstracta Metadata Cache",
project: resourceArgs.projectId,
}, {
parent : this,
dependsOn : [redisService,serviceNetworkingAccessService,iamBindingRedis2]
});
this.registerOutputs({
redisCache : redisCache
});
}
}
let suffix = "20211018-002";
let org_name = `org-redis-demo-${suffix}`;
let projectId = `redis-demo-${suffix}` ;
const myGcpProject = new gcp.organizations.Project('ab-' + org_name, {
orgId: gcpOrgId,
projectId: projectId,
billingAccount: billingAccountId,
name: 'ab-' + org_name,
});
const myGcpProjectIAM = new gcp.projects.IAMBinding("iam-001", {
members: [
"user:vikram.vasudevan#ekahaa.com",
],
role: "roles/owner",
project: myGcpProject.projectId
});
const cacheComponentResource = new CacheComponentResource("my-cache", {
projectId : myGcpProject.projectId,
projectNumber : myGcpProject.number
}, {
dependsOn : [myGcpProjectIAM]
});
I want to get Logs from a subscription filter and then put the logs in a s3 bucket and sent them to ES.
Similar like in the diagram here:
https://aws.amazon.com/solutions/implementations/centralized-logging/
When I am using this function:
/*
For processing data sent to Firehose by Cloudwatch Logs subscription filters.
Cloudwatch Logs sends to Firehose records that look like this:
{
"messageType": "DATA_MESSAGE",
"owner": "123456789012",
"logGroup": "log_group_name",
"logStream": "log_stream_name",
"subscriptionFilters": [
"subscription_filter_name"
],
"logEvents": [
{
"id": "01234567890123456789012345678901234567890123456789012345",
"timestamp": 1510109208016,
"message": "log message 1"
},
{
"id": "01234567890123456789012345678901234567890123456789012345",
"timestamp": 1510109208017,
"message": "log message 2"
}
...
]
}
The data is additionally compressed with GZIP.
The code below will:
1) Gunzip the data
2) Parse the json
3) Set the result to ProcessingFailed for any record whose messageType is not DATA_MESSAGE, thus redirecting them to the
processing error output. Such records do not contain any log events. You can modify the code to set the result to
Dropped instead to get rid of these records completely.
4) For records whose messageType is DATA_MESSAGE, extract the individual log events from the logEvents field, and pass
each one to the transformLogEvent method. You can modify the transformLogEvent method to perform custom
transformations on the log events.
5) Concatenate the result from (4) together and set the result as the data of the record returned to Firehose. Note that
this step will not add any delimiters. Delimiters should be appended by the logic within the transformLogEvent
method.
6) Any additional records which exceed 6MB will be re-ingested back into Firehose.
*/
const zlib = require('zlib');
const AWS = require('aws-sdk');
/**
* logEvent has this format:
*
* {
* "id": "01234567890123456789012345678901234567890123456789012345",
* "timestamp": 1510109208016,
* "message": "log message 1"
* }
*
* The default implementation below just extracts the message and appends a newline to it.
*
* The result must be returned in a Promise.
*/
function transformLogEvent(logEvent: any) {
return Promise.resolve(`${logEvent.message}\n`);
}
function putRecordsToFirehoseStream(streamName: any, records: any, client: any, resolve: any, reject: any, attemptsMade: any, maxAttempts: any) {
client.putRecordBatch({
DeliveryStreamName: streamName,
Records: records,
}, (err: any, data: any) => {
const codes = [];
let failed = [];
let errMsg = err;
if (err) {
failed = records;
} else {
for (let i = 0; i < data.RequestResponses.length; i++) {
const code = data.RequestResponses[i].ErrorCode;
if (code) {
codes.push(code);
failed.push(records[i]);
}
}
errMsg = `Individual error codes: ${codes}`;
}
if (failed.length > 0) {
if (attemptsMade + 1 < maxAttempts) {
console.log('Some records failed while calling PutRecordBatch, retrying. %s', errMsg);
putRecordsToFirehoseStream(streamName, failed, client, resolve, reject, attemptsMade + 1, maxAttempts);
} else {
reject(`Could not put records after ${maxAttempts} attempts. ${errMsg}`);
}
} else {
resolve('');
}
});
}
function putRecordsToKinesisStream(streamName: any, records: any, client: any, resolve: any, reject: any, attemptsMade: any, maxAttempts: any) {
client.putRecords({
StreamName: streamName,
Records: records,
}, (err: any, data: any) => {
const codes = [];
let failed = [];
let errMsg = err;
if (err) {
failed = records;
} else {
for (let i = 0; i < data.Records.length; i++) {
const code = data.Records[i].ErrorCode;
if (code) {
codes.push(code);
failed.push(records[i]);
}
}
errMsg = `Individual error codes: ${codes}`;
}
if (failed.length > 0) {
if (attemptsMade + 1 < maxAttempts) {
console.log('Some records failed while calling PutRecords, retrying. %s', errMsg);
putRecordsToKinesisStream(streamName, failed, client, resolve, reject, attemptsMade + 1, maxAttempts);
} else {
reject(`Could not put records after ${maxAttempts} attempts. ${errMsg}`);
}
} else {
resolve('');
}
});
}
function createReingestionRecord(isSas: any, originalRecord: any) {
if (isSas) {
return {
Data: Buffer.from(originalRecord.data, 'base64'),
PartitionKey: originalRecord.kinesisRecordMetadata.partitionKey,
};
} else {
return {
Data: Buffer.from(originalRecord.data, 'base64'),
};
}
}
function getReingestionRecord(isSas: any, reIngestionRecord: any) {
if (isSas) {
return {
Data: reIngestionRecord.Data,
PartitionKey: reIngestionRecord.PartitionKey,
};
} else {
return {
Data: reIngestionRecord.Data,
};
}
}
exports.handler = (event: any, context: any, callback: any) => {
Promise.all(event.records.map(function (r: any) {
const buffer = Buffer.from(r.data, 'base64');
let decompressed;
try {
decompressed = zlib.unzipSync(buffer);
} catch (e) {
return Promise.resolve({
recordId: r.recordId,
result: 'ProcessingFailed',
});
}
const data = JSON.parse(decompressed);
// CONTROL_MESSAGE are sent by CWL to check if the subscription is reachable.
// They do not contain actual data.
if (data.messageType === 'CONTROL_MESSAGE') {
return Promise.resolve({
recordId: r.recordId,
result: 'Dropped',
});
} else if (data.messageType === 'DATA_MESSAGE') {
const promises = data.logEvents.map(transformLogEvent);
return Promise.all(promises)
.then(transformed => {
const payload: any = transformed.reduce(function (a: any, v: any) {
return a + v;
});
const encoded = Buffer.from(payload).toString();
return {
recordId: r.recordId,
result: 'Ok',
data: encoded,
};
});
} else {
return Promise.resolve({
recordId: r.recordId,
result: 'ProcessingFailed',
});
}
})).then(recs => {
const isSas = Object.prototype.hasOwnProperty.call(event, 'sourceKinesisStreamArn');
const streamARN = isSas ? event.sourceKinesisStreamArn : event.deliveryStreamArn;
const region = streamARN.split(':')[3];
const streamName = streamARN.split('/')[1];
const result: any = { records: recs };
let recordsToReingest = [];
const putRecordBatches: any = [];
let totalRecordsToBeReingested = 0;
const inputDataByRecId: any = {};
event.records.forEach(function (r: any) { inputDataByRecId[r.recordId] = createReingestionRecord(isSas, r) });
let projectedSize = recs.filter(function (rec: any) { return rec.result === 'Ok' })
.map(function (r: any) { return r.recordId.length + r.data.length })
.reduce((a, b) => a + b, 0);
// 6000000 instead of 6291456 to leave ample headroom for the stuff we didn't account for
for (let idx = 0; idx < event.records.length && projectedSize > 6000000; idx++) {
const rec: any = result.records[idx];
if (rec.result === 'Ok') {
totalRecordsToBeReingested++;
recordsToReingest.push(getReingestionRecord(isSas, inputDataByRecId[rec.recordId]));
projectedSize -= rec.data.length;
delete rec.data;
result.records[idx].result = 'Dropped';
// split out the record batches into multiple groups, 500 records at max per group
if (recordsToReingest.length === 500) {
putRecordBatches.push(recordsToReingest);
recordsToReingest = [];
}
}
}
if (recordsToReingest.length > 0) {
// add the last batch
putRecordBatches.push(recordsToReingest);
}
if (putRecordBatches.length > 0) {
new Promise((resolve, reject) => {
let recordsReingestedSoFar = 0;
for (let idx = 0; idx < putRecordBatches.length; idx++) {
const recordBatch = putRecordBatches[idx];
if (isSas) {
const client = new AWS.Kinesis({ region: region });
putRecordsToKinesisStream(streamName, recordBatch, client, resolve, reject, 0, 20);
} else {
const client = new AWS.Firehose({ region: region });
putRecordsToFirehoseStream(streamName, recordBatch, client, resolve, reject, 0, 20);
}
recordsReingestedSoFar += recordBatch.length;
console.log('Reingested %s/%s records out of %s in to %s stream', recordsReingestedSoFar, totalRecordsToBeReingested, event.records.length, streamName);
}}).then(
() => {
console.log('Reingested all %s records out of %s in to %s stream', totalRecordsToBeReingested, event.records.length, streamName);
callback(null, result);
},
failed => {
console.log('Failed to reingest records. %s', failed);
callback(failed, null);
});
} else {
console.log('No records needed to be reingested.');
callback(null, result);
}
}).catch(ex => {
console.log('Error: ', ex);
callback(ex, null);
});
};
But I am getting a Lambda.FunctionError:
Check your function and make sure the output is in required format. In addition to that, make sure the processed records contain valid result status of Dropped, Ok, or ProcessingFailed
Does anybody know, which function is suitable, to receive logs from the Cloudwatch subscription filter, sending them to S3 and ES?
My code for the FirehoseDeliveryStream looks like:
const firehoseDeliveryStream = new CfnDeliveryStream(this, "FirehoseDeliveryStream", {
deliveryStreamType: "DirectPut",
elasticsearchDestinationConfiguration: {
domainArn: elasticsearchDomain.domainArn,
roleArn: firehoseDeliveryRole.roleArn,
indexName: "test",
s3Configuration: {
bucketArn: this.logsBucket.bucketArn,
roleArn: firehoseDeliveryRole.roleArn,
cloudWatchLoggingOptions: {
enabled: true,
logGroupName: firehoseloggroup.logGroupName,
logStreamName: logstream.logStreamName
},
},
s3BackupMode: "AllDocuments",
cloudWatchLoggingOptions: {
enabled: true,
logGroupName: firehoseloggroup.logGroupName,
logStreamName: logstream.logStreamName
},
processingConfiguration: {
enabled: true,
processors: [{
type: "Lambda",
parameters: [{
parameterName: "LambdaArn",
parameterValue: handler.functionArn,
}],
}],
},
},
});
I have a CloudWatch log-group-1, kinesis firehose, lambda, S3.
log-group-1 sends logs to kinesis firehose (using subscription filter). Kinesis firehose triggers lambda to process the logs. Lambda returns the logs back to kinesis firehose and kinesis firehose saves transformed logs to S3.
Lambda gets the following input:
{
"invocationId": "000ac99...",
"deliveryStreamArn": "arn:aws:firehose:eu-central-1:123456789123:deliverystream/delivery-09",
"region": "eu-central-1",
"records": [
{
"recordId": "496199814216613477...",
"approximateArrivalTimestamp": 1625854080200,
"data": "H4sIAAAAAAAAADWOwQrCM......"
},
{
"recordId": "4961998142166134...",
"approximateArrivalTimestamp": 1625854100311,
"data": "H4sIAAAAAAAAADVPy07DMB......"
}
]
}
To return the transformed message you must change the records list. See example:
"records": [
{
"recordId": "you better take it from the input",
"result": "can be Ok, Dropped, ProcessingFailed",
"data": "must be an encoded base-64 string"
}
]
I attached a code written in Javascipt. It is enough just to copy-paste it to lambda.
const node_gzip_1 = require("node-gzip");
async function handler(event) {
console.log('event: ' + JSON.stringify(event, undefined, 3));
let result = [];
// Iterate through records list
const records = event.records;
for (let ii = 0; ii < records.length; ii++) {
const record = records[ii];
const recordId = record.recordId;
// Transform record data to a human readable string
const data = record.data;
const decodedData = Buffer.from(data, 'base64');
const ungziped = await node_gzip_1.ungzip(decodedData);
console.log('ungziped: ' + ungziped);
// Parse record data to JSON
const dataJson = JSON.parse(ungziped.toString());
// Get a list of log events and iterate through each element
const logEventsList = dataJson.logEvents;
logEventsList.forEach((logEventValue) => {
// Get the message which was saved in CloudWatch
const messageString = logEventValue.message;
// Create the transformed result
const transformedResultJson = {
someRandomNumber: Math.random(), // Some random variable I decided to put in the result
message: messageString + '-my-custom-change' // Edit the message
};
// Final data must be encoded to base 64
const messageBase64 = Buffer.from(JSON.stringify(transformedResultJson) + '\n').toString('base64'); // Adding a new line to transformed result is optional. It just make reading the S3 easier
console.log('messageBase64: ' + messageBase64);
// Save transformed result
result.push({
recordId: recordId,
result: 'Ok',
data: messageBase64
});
});
}
// Replace initial records list with the transformed list
event.records = result;
console.log('new event: ' + JSON.stringify(event, undefined, 2));
// Returned value will go back to kinesis firehose, then S3
return event;
}
exports.handler = handler;
Lambda return value is:
{
"invocationId": "000ac99...",
"deliveryStreamArn": "arn:aws:firehose:eu-central-1:123456789123:deliverystream/delivery-09",
"region": "eu-central-1",
"records": [
{
"recordId": "496199814216613477...",
"result": "Ok",
"data": "eyJzb21lUmF..."
},
{
"recordId": "4961998142166134...",
"result": "Ok",
"data": "eyJzb21lUmFuZG9..."
}
]
}
You can also use a lambda blueprint kinesis-firehose-syslog-to-json.
Also see:
https://docs.amazonaws.cn/en_us/firehose/latest/dev/data-transformation.html
Kinesis Firehose putting JSON objects in S3 without seperator comma
I want to extend my api using loopback . I have read the documentation
'use strict';
module.exports = function(Meetups,pusher) {
Meetups.status = function(cb) {
var currentDate = new Date();
var currentHour = currentDate.getHours();
var OPEN_HOUR = 6;
var CLOSE_HOUR = 20;
console.log('Current hour is %d', currentHour);
var response;
if (currentHour >= OPEN_HOUR && currentHour < CLOSE_HOUR) {
response = 'We are open yeah!!! for business.';
} else {
response = 'Sorry, we are closed. Open daily from 6am to 8pm.';
}
cb(null, response);
};
Meetups.remoteMethod(
'status', {
http: {
path: '/status',
verb: 'get'
},
returns: {
arg: 'status',
type: 'string'
}
}
);
Meetups.pusher = function(cb) {
if (2>1) {
response = 'sending something';
} else {
response = 'mont blanc';
}
cb(null, response);
};
Meetups.remoteMethod(
'pusher', {
http: {
path: '/pusher',
verb: 'get'
},
returns: {
arg: 'pusher',
type: 'string'
}
}
);
};
First, I added /status route and it worked fine. But, when i tried to add /pusher . It just didnt work. I am getting an error
{
"error": {
"statusCode": 500,
"name": "ReferenceError",
"message": "response is not defined",
"stack": "ReferenceError: response is not defined\n at Function.Meetups.pusher (/Users/ankursharma/Documents/projects/meetupz/common/models/meetups.js:34:20)\n at SharedMethod.invoke (/Users/ankursharma/Documents/projects/meetupz/node_modules/strong-remoting/lib/shared-method.js:270:25)\n at HttpContext.invoke (/Users/ankursharma/Documents/projects/meetupz/node_modules/strong-remoting/lib/http-context.js:297:12)\n at phaseInvoke (/Users/ankursharma/Documents/projects/meetupz/node_modules/strong-remoting/lib/remote-objects.js:677:9)\n at runHandler (/Users/ankursharma/Documents/projects/meetupz/node_modules/strong-remoting/node_modules/loopback-phase/lib/phase.js:135:5)\n at iterate (/Users/ankursharma/Documents/projects/meetupz/node_modules/strong-remoting/node_modules/loopback-phase/node_modules/async/lib/async.js:146:13)\n at Object.async.eachSeries (/Users/ankursharma/Documents/projects/meetupz/node_modules/strong-remoting/node_modules/loopback-phase/node_modules/async/lib/async.js:162:9)\n at runHandlers (/Users/ankursharma/Documents/projects/meetupz/node_modules/strong-remoting/node_modules/loopback-phase/lib/phase.js:144:13)\n at iterate (/Users/ankursharma/Documents/projects/meetupz/node_modules/strong-remoting/node_modules/loopback-phase/node_modules/async/lib/async.js:146:13)\n at /Users/ankursharma/Documents/projects/meetupz/node_modules/strong-remoting/node_modules/loopback-phase/node_modules/async/lib/async.js:157:25\n at /Users/ankursharma/Documents/projects/meetupz/node_modules/strong-remoting/node_modules/loopback-phase/node_modules/async/lib/async.js:154:25\n at execStack (/Users/ankursharma/Documents/projects/meetupz/node_modules/strong-remoting/lib/remote-objects.js:522:7)\n at RemoteObjects.execHooks (/Users/ankursharma/Documents/projects/meetupz/node_modules/strong-remoting/lib/remote-objects.js:526:10)\n at phaseBeforeInvoke (/Users/ankursharma/Documents/projects/meetupz/node_modules/strong-remoting/lib/remote-objects.js:673:10)\n at runHandler (/Users/ankursharma/Documents/projects/meetupz/node_modules/strong-remoting/node_modules/loopback-phase/lib/phase.js:135:5)\n at iterate (/Users/ankursharma/Documents/projects/meetupz/node_modules/strong-remoting/node_modules/loopback-phase/node_modules/async/lib/async.js:146:13)"
}
}
I am pretty sure, its a very small mistake. I am beginner in loopback and trying to implement loopback in my project.
In the example they define response as a local variable to that remote method, you did not. Secondly, (Meetups,pusher) you do not need to export pusher here. You are adding to Meetups.
You have to declare response in your pusher remote method.
An alternative way without declaring response is, Simply returning the value.
Example:
Meetups.pusher = function(cb) {
if (2>1) {
return 'sending something';
} else {
return 'mont blanc';
}
};
Define the variable and return the variable or you can directly call the cb in if and else like
Meetups.pusher = function(cb) {
if (2>1) {
cb(null,'sending something');
} else {
cb(null, 'mont blanc');
}
};