postCreate method calling multiple times in aikau - alfresco-share

We are adding some custom actions in Selected Items menu in Faceted search screen 5.1+aikau(100.0.1);
but when i load the page,In Custom widget postCreate method is calling twice and when i select 'Edit properties' then also onSelectedDocumentsAction function calling twice.
if i push one more extra item in selectedItemsActionsGroup then that many times(3 times) its calling.
var selectedItemsActionsGroup = widgetUtils.findObject(model.jsonModel.widgets,"id", "SELECTED_ITEMS_ACTIONS_GROUP");
selectedItemsActionsGroup.config.widgets.push(
{
id : "OnActionEditProperties",
name : "faceted-search/CustomBulkSelectItems/CustomBulkSelectItems",
config : {
label : "Edit",
iconImage : url.context
+ "/res/components/documentlibrary/actions/y-16.png",
type : "action-link",
permission : "",
asset : "",
href : "",
hasAspect : "",
notAspect : "",
publishTopic : "BULK_OPS",
publishPayload : {
action : "onActionEditProperties",
}
}
},
{
id : "OnActionManaged",
name : "faceted-search/CustomBulkSelectItems/CustomBulkSelectItems",
config : {
label : "Managed",
iconImage : url.context
+ "/res/components/documentlibrary/actions/x-16.png",
type : "action-link",
permission : "",
asset : "",
href : "",
hasAspect : "",
notAspect : "",
publishTopic : "BULK_OPS",
publishPayload : {
action : "OnActionManaged",
}
}
});
*****************
CustomBulkSelectItems.js
**********************
define(["dojo/_base/declare",
"alfresco/services/FormsRuntimeService",
"alfresco/core/UrlUtilsMixin",
"alfresco/enums/urlTypes",
"alfresco/core/Core",
"alfresco/menus/AlfFilteringMenuItem",
"alfresco/documentlibrary/_AlfDocumentListTopicMixin",
"alfresco/core/CoreXhr",
"dojo/_base/lang",
"dojo/dom-class",
"dojo/_base/array",
"service/constants/Default","alfresco/core/topics"],
function(declare,FormsRuntimeService, UrlUtilsMixin,urlTypes,AlfCore ,AlfFilteringMenuItem, _AlfDocumentListTopicMixin,AlfCoreXhr, lang, domClass,
array, AlfConstants,topics) {
return declare([FormsRuntimeService,UrlUtilsMixin,AlfCore,AlfFilteringMenuItem,_AlfDocumentListTopicMixin,AlfCoreXhr], {
i18nRequirements: [ {i18nFile: "./CustomBulkSeledtItems.properties"} ],
postCreate: function alfresco_documentlibrary_CustomBulkSelectItems__postCreate() {
console.log(" bulkops postCreate************.");
this.alfSubscribe("BULK_OPS", lang.hitch(this, this.onSelectedDocumentsAction));
this.inherited(arguments);
} ,
/**
* This function handles requests to perform actions on the currently selected documents. It takes the
* provided payload and updates it with the
*
* #instance
* #param {object} payload The payload containing the details of the action being requested
* #fires alfresco/core/topics#MULTIPLE_ITEM_ACTION_REQUEST
*/
onSelectedDocumentsAction: function alfresco_documentlibrary_CustomBulkSelectItems__onSelectedDocumentsAction(payload) {
console.log(" bulkops onSelectedDocumentsAction************.");
}
});
});

I think you're calling same widget faceted-search/CustomBulkSelectItems/CustomBulkSelectItems twice and publishTopic also called twice from the get.js file.
After change the widget in the get.js it is looking fine to me.
Share your thoughts.
I changed your something like below and the output is like that.
var selectedItemsActionsGroup = widgetUtils.findObject(model.jsonModel.widgets,"id", "SELECTED_ITEMS_ACTIONS_GROUP");
selectedItemsActionsGroup.config.widgets.push(
{
id : "OnActionEditProperties",
name : "faceted-search/CustomBulkSelectItems/CustomBulkSelectItems",
config : {
label : "Edit",
iconImage : url.context
+ "/res/components/documentlibrary/actions/y-16.png",
type : "action-link",
permission : "",
asset : "",
href : "",
hasAspect : "",
notAspect : "",
publishTopic : "BULK_OPS",
publishPayload : {
action : "onActionEditProperties",
}
}
},
{
id : "OnActionManaged",
name : "faceted-search/CustomBulkSelectItems/CustomBulkSelect",
config : {
label : "Managed",
iconImage : url.context
+ "/res/components/documentlibrary/actions/x-16.png",
type : "action-link",
permission : "",
asset : "",
href : "",
hasAspect : "",
notAspect : "",
publishTopic : "BULK_OPS_1",
publishPayload : {
action : "OnActionManaged",
}
}
});
CustomBulkSelectItems.js
define(["dojo/_base/declare",
"alfresco/services/FormsRuntimeService",
"alfresco/core/UrlUtilsMixin",
"alfresco/enums/urlTypes",
"alfresco/core/Core",
"alfresco/menus/AlfFilteringMenuItem",
"alfresco/documentlibrary/_AlfDocumentListTopicMixin",
"alfresco/core/CoreXhr",
"dojo/_base/lang",
"dojo/dom-class",
"dojo/_base/array",
"service/constants/Default","alfresco/core/topics"],
function(declare,FormsRuntimeService, UrlUtilsMixin,urlTypes,AlfCore ,AlfFilteringMenuItem, _AlfDocumentListTopicMixin,AlfCoreXhr, lang, domClass,
array, AlfConstants,topics) {
return declare([FormsRuntimeService,UrlUtilsMixin,AlfCore,AlfFilteringMenuItem,_AlfDocumentListTopicMixin,AlfCoreXhr], {
i18nRequirements: [ {i18nFile: "./CustomBulkSeledtItems.properties"} ],
postCreate: function alfresco_documentlibrary_CustomBulkSelectItems__postCreate() {
console.log(" bulkops postCreate************.");
this.alfSubscribe("BULK_OPS", lang.hitch(this, this.onSelectedDocumentsAction));
this.inherited(arguments);
} ,
/**
* This function handles requests to perform actions on the currently selected documents. It takes the
* provided payload and updates it with the
*
* #instance
* #param {object} payload The payload containing the details of the action being requested
* #fires alfresco/core/topics#MULTIPLE_ITEM_ACTION_REQUEST
*/
onSelectedDocumentsAction: function alfresco_documentlibrary_CustomBulkSelectItems__onSelectedDocumentsAction(payload) {
console.log(" bulkops onSelectedDocumentsAction************.");
}
});
});
CustomBulkSelect.js
define(["dojo/_base/declare",
"alfresco/services/FormsRuntimeService",
"alfresco/core/UrlUtilsMixin",
"alfresco/enums/urlTypes",
"alfresco/core/Core",
"alfresco/menus/AlfFilteringMenuItem",
"alfresco/documentlibrary/_AlfDocumentListTopicMixin",
"alfresco/core/CoreXhr",
"dojo/_base/lang",
"dojo/dom-class",
"dojo/_base/array",
"service/constants/Default","alfresco/core/topics"],
function(declare,FormsRuntimeService, UrlUtilsMixin,urlTypes,AlfCore ,AlfFilteringMenuItem, _AlfDocumentListTopicMixin,AlfCoreXhr, lang, domClass,
array, AlfConstants,topics) {
return declare([FormsRuntimeService,UrlUtilsMixin,AlfCore,AlfFilteringMenuItem,_AlfDocumentListTopicMixin,AlfCoreXhr], {
i18nRequirements: [ {i18nFile: "./CustomBulkSeledtItems.properties"} ],
postCreate: function alfresco_documentlibrary_CustomBulkSelectItems__postCreate() {
console.log(" CustomBulkSeledt bulkops postCreate************.");
this.alfSubscribe("BULK_OPS_1", lang.hitch(this, this.onSelectedDocumentsAction));
this.inherited(arguments);
} ,
/**
* This function handles requests to perform actions on the currently selected documents. It takes the
* provided payload and updates it with the
*
* #instance
* #param {object} payload The payload containing the details of the action being requested
* #fires alfresco/core/topics#MULTIPLE_ITEM_ACTION_REQUEST
*/
onSelectedDocumentsAction: function alfresco_documentlibrary_CustomBulkSelectItems__onSelectedDocumentsAction(payload) {
console.log(" CustomBulkSeledt bulkops onSelectedDocumentsAction************.");
}
});
});
Updated info
Please look at the AlfFilteringMenuItem.js and
postCreate: function alfresco_menus_AlfFilteringMenuItem____postCreate() {
this.filterTopic && this.alfSubscribe(this.filterTopic, lang.hitch(this, this.filter));
this.inherited(arguments);
},
and
filter: function alfresco_menus_AlfFilteringMenuItem__filter(payload) {
this.alfLog("warn", "No implementation of filtering extension point", payload);
}
There is no specific implemenation is found. If you enable debug logs, you should be able to the warn messages.
I looked at the aikau logs and you can see the default behaviour also having muliple requests( each request for each widger) similar to yours. The only difference is , filter method and publish (lang.hitch) stuffs. Hope you're clear now.

Related

api_platform produces Error "no handler found for uri [/index/_doc/_search] and method [POST]"

When trying to implement elasticsearch (v7.9.3) via the fos_elastica-bundle (v6.0.0) into my Symfony (v5.3.10) - App with api_platform (v2.6.6), I keep on getting this error:
"{"error":"no handler found for uri [//posts/_doc/_search] and method [POST]"}",
My api_platform.yaml reads:
api_platform:
[...]
elasticsearch:
hosts: [ '%env(ELASTICSEARCH_URL)%' ]
mapping:
App\Document\Post:
index: posts
and my fos_elastica.yaml:
fos_elastica:
clients:
default: { url: '%env(ELASTICSEARCH_URL)%' }
indexes:
posts:
properties:
id:
"type": "keyword"
source: ~
title: ~
description: ~
body: ~
children: ~
tags: ~
originalContent: ~
persistence:
driver: mongodb
model: App\Document\Post
By debugging the fos-elastica Bundle, I found out that the Elastica-Connector correctly triggers a [POST]-Request to "/posts/_doc/_search" with this request body:
{"sort":[{"id":{"order":"asc"}}],"query":{"match_all":{}},"size":30,"from":0}
If I use the Kibana Dev Tools Console and trigger an identical request
POST /posts/_doc/_search
{"sort":[{"id":{"order":"asc"}}],"query":{"match_all":{}},"size":30,"from":60}
I do get results from elasticsearch as expected:
#! Deprecation: [types removal] Specifying types in search requests is deprecated.
{
"took" : 12,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 3082,
"relation" : "eq"
},
"max_score" : null,
"hits" : [
{
"_index" : "posts",
"_type" : "_doc",
[...]
Apart from the deprecation notice, everything seems fine.
Does anyone have an idea why the api_platform integration of the fos_elastica-bundle does not work as expected and keeps on returning the "no handler found"-error message?
I have now helped myself by creating a custom ApiResource - filter
#[ApiFilter(FulltextFilter::class, arguments: ['index' => 'post'], properties: ['body','description','tag'])]
My custom filter implements ApiPlatform\Core\Bridge\Doctrine\MongoDbOdm\Filter\FilterInterface, directly communicates with the ElasticSearch server, sends a query to search the specified index (posts) and adds another match()-directive to the aggregationBuilder with a set of IDs matching the original search:
<?php
declare(strict_types=1);
namespace App\Filter;
use ApiPlatform\Core\Bridge\Doctrine\MongoDbOdm\Filter\FilterInterface;
use Doctrine\ODM\MongoDB\Aggregation\Builder;
use Elastica\Result;
use Elastica\Client;
use Elastica\Query;
use Symfony\Component\PropertyInfo\Type;
/**
* Filter the collection by given properties.
*
*/
final class FulltextFilter implements FilterInterface
{
protected $index = '';
protected $properties = [];
protected $client;
protected $searchParameterName;
protected $maxResultsParameterName;
const DEFAULT_MAX_RESULTS = 200;
public function __construct(Client $client, string $index = '', string $maxResultsParameterName = 'amount', string $searchParameterName = 'query', array $properties = []) {
$this->index = $index;
$this->properties = $properties;
$this->client = $client;
$this->searchParameterName = $searchParameterName;
$this->maxResultsParameterName = $maxResultsParameterName;
}
public function getFilteredIds($searchterm, $index = null, $properties = null, $maxResults = null) {
$matches = [];
if (is_null($properties)) {
$properties = array_keys($this->properties);
}
foreach ($properties as $propertyName) {
array_push($matches, ['match'=>[$propertyName => $searchterm]]);
}
$queryObject = ['query' => ['bool' => ['should' => $matches]]];
$queryObject['size'] = (int) $maxResults >0 ? (int) $maxResults : self::DEFAULT_MAX_RESULTS;
$query = new Query();
$response = $this->client->getIndex($index ?? $this->index)
->search($query->setRawQuery($queryObject))
->getResults();
return array_map(function(Result $result) {return $result->getHit()['_source']['id'];}, $response);
}
public function apply(Builder $aggregationBuilder, string $resourceClass, string $operationName = null, array &$context = [])
{
$maxResults = $context['filters'][$this->maxResultsParameterName] ?? null;
$searchterm = $context['filters'][$this->searchParameterName] ?? false;
if ($searchterm !== false) {
$aggregationBuilder->match()->field('id')->in($this->getFilteredIds($searchterm, null, null, $maxResults));
}
}
public function getDescription(string $resourceClass): array
{
return [];
}
}
This solution might not be as elegant as using the ElasticSearch-Connector natively provided by api_platform, but it is fairly performant and it works.
However, if someone comes up with a solution to fix the depicted ES-Connector issue with api_platform, please feel free to share it.
The problem is that, FOS Elastica requires an ES URL with an ending slash. But Api Platform requires a URL without ending slash.
We usually define the URL in .env file and then recall it in config files.
To solve the problem, we could define the URL in .env without endling slash and add the slash to the FOS Elastica config.
# .env
###> friendsofsymfony/elastica-bundle ###
ELASTICSEARCH_URL=http://localhost:9200
###< friendsofsymfony/elastica-bundle ###
# config/packages/api_platform.yaml
api_platform:
elasticsearch:
enabled: true
hosts: [ '%env(ELASTICSEARCH_URL)%' ]
# config/packages/fos_elastica.yaml
fos_elastica:
clients:
default: { url: '%env(ELASTICSEARCH_URL)%/' }

Flutter Firebase Cloud Functions deleting all items as soon as they are written

I'm trying to use the deleteOldItems Cloud Functions code for my project, when I use the code and simply change the 2 hours to 4 it deletes all my nodes as soon as they are written. Here is my Firebase functions code...
exports.deleteOldItems = functions.database.ref('messages/{pushId}').onWrite(async (change) => {
var ref = change.after.ref.parent; // reference to the items
var now = Date.now();
var cutoff = now - 4 * 60 * 60 * 1000;
var oldItemsQuery = ref.orderByChild('timestamp').endAt(cutoff);
return oldItemsQuery.once('value', function(snapshot) {
// create a map with all children that need to be removed
var updates = {};
snapshot.forEach(function(child) {
updates[child.key] = null
});
// execute all updates in one go and return the result to end the function
return ref.update(updates);
});
});
And here is how I'm saving my timestamp in Flutter to
database/messages
with child 'timestamp'..
var time = DateTime.now().millisecondsSinceEpoch;
Here is my JSON # 'messages'
"messages" : {
"-M3e5Glv5JEaz12hDwBB" : {
"body" : "Hello",
"name" : "Charles",
"photo" : "https://media1.giphy.com/media/7wDEaTbCupuRq/giphy-downsized.gif",
"timeStamp" : 1585549220985,
"user" : "4eb8920a-e407-4488-bce4-c6f64f7b0891"
},
"-M3e5Jfx2cnbtMTNLPev" : {
"body" : "Example",
"name" : "Charles",
"photo" : "https://media1.giphy.com/media/7wDEaTbCupuRq/giphy-downsized.gif",
"timeStamp" : 1585549232893,
"user" : "4eb8920a-e407-4488-bce4-c6f64f7b0891"
},
"-M3e6oYk6_zTe6IAs0Fg" : {
"bodyGif" : "https://media3.giphy.com/media/8xomIW1DRelmo/giphy.gif",
"name" : "Charles",
"photo" : "https://media1.giphy.com/media/7wDEaTbCupuRq/giphy-downsized.gif",
"timestamp" : 1585549625584,
"user" : "4eb8920a-e407-4488-bce4-c6f64f7b0891"
},
"-M3hR_DrGcKhM8zpFbYA" : {
"body" : "Again",
"name" : "Charles",
"photo" : "https://media1.giphy.com/media/7wDEaTbCupuRq/giphy-downsized.gif",
"timeStamp" : 1585605399479,
"user" : "4eb8920a-e407-4488-bce4-c6f64f7b0891"
}
and my database security rules are as follows for 'messages'...
"messages": {
".indexOn": ["timestamp"]
}
How can I get the nodes to be removed as expected?

How to do filter by keyword in Firebase?

I have a Firebase database structure like this
{
"5ZhJG1NACDdG9WoNZWrBoYGkIpD3" : {
"Company" : {
"5ZhJG1NACDdG9WoNZWrBoYGkIpD3" : {
"authTime" : 1532061957,
"companyName" : "Scopic Software",
"contactName" : "Hoang Scopic",
"email" : "hoang.trinh#scopicsoftware.com",
"firebaseID" : "5ZhJG1NACDdG9WoNZWrBoYGkIpD3",
"isFirstLogin" : false,
"phoneNumber" : "1234567890",
"schoolName" : "MIT",
"teachers" : {
"aOpjnzHpDiZ7uwQQqJoinGvM9ZD3" : "0"
}
}
}
},
"AhZc9B02goOtZ6qBNhz9W0K6Esg2" : {
"Subscription" : {
"-LHlQ4OhijzzFY5HZOT4" : {
"firebaseID" : "-LHlQ4OhijzzFY5HZOT4",
"period" : {
"endAt" : "1533194625",
"startAt" : "1531985025"
},
"status" : "trial"
}
},
"Teacher" : {
"AhZc9B02goOtZ6qBNhz9W0K6Esg2" : {
"authTime" : 1532061932,
"email" : "hoang.trinhj#gmail.com",
"firebaseID" : "AhZc9B02goOtZ6qBNhz9W0K6Esg2",
"isFirstLogin" : false,
"name" : "Hoang Trinh",
"schoolName" : "HUST",
"subscriptions" : {
"-LHlQ4OhijzzFY5HZOT4" : "0"
}
}
}
},
"aOpjnzHpDiZ7uwQQqJoinGvM9ZD3" : {
"Subscription" : {
"-LHlWnpNZazBC5lpXLi0" : {
"firebaseID" : "-LHlWnpNZazBC5lpXLi0",
"period" : {
"endAt" : "1533196388",
"startAt" : "1531986788"
},
"status" : "trial"
}
},
"Teacher" : {
"aOpjnzHpDiZ7uwQQqJoinGvM9ZD3" : {
"Company" : {
"5ZhJG1NACDdG9WoNZWrBoYGkIpD3" : "0"
},
"authTime" : 1532060884,
"email" : "piavghoang#gmail.com",
"firebaseID" : "aOpjnzHpDiZ7uwQQqJoinGvM9ZD3",
"isFirstLogin" : false,
"subscriptions" : {
"-LHlWnpNZazBC5lpXLi0" : "0"
}
}
}
},
"ooR32SjABdOYEkWX6dzy4fE5Kym1" : {
"Admin" : {
"email" : "admin#test.com",
"firstName" : "Hoang",
"lastName" : "Trinh",
"password" : "123456xx"
},
"isAdmin" : true
}
}
This is data in an existing system, so I can't change its structure.
Now I need to write an API to list these records filtered by "email" attribute there (it's nested inside a key).
How can I do a search for this email?
There is one solution that I thought about. It's just getting the whole json back and process data in code (not using Firebase database query functions).
But I can't do that, because AFTER filtering, I need to do paging also.
In order to make the pagination, I need to use query functions like "sortByChild" or "limitToLast", so I have to use these query functions with filtering.
Thank you.
EDIT:
My current implementation for pagination:
getUsers: async (page, perPage, searchTerm) => {
const db = fbAdmin.db();
let dbRef = await db.ref();
if (searchTerm) {
// TODO: Add filter logic here
}
let totalItems = await dbRef.once('value');
let totalItemsNumber = await Object.keys(totalItems.toJSON()).length;
// Calculate for pagination
let lastItemId;
if ((page - 1) * perPage + 1 > totalItemsNumber) {
lastItemId = null;
} else {
let lastItems = await dbRef.orderByKey().limitToLast((page - 1) * perPage + 1).once('value');
lastItemId = Object.keys(lastItems.toJSON())[0];
}
// Do the pagination
let snap;
let data;
if (lastItemId) {
snap = await dbRef.orderByKey().endAt(lastItemId).limitToLast(perPage).once('value');
data = snap.toJSON();
} else {
data = {};
}
let users = Object.keys(data).map(key => {
if (data[key][KEY]) { // Check if it's an admin
return {
role: TYPE_ADMIN,
email: data[key][KEY].email,
name: data[key][KEY].firstName + " " + data[key][KEY].lastName
}
} else if (data[key][Company.KEY]) { // Check if it's a company member
return {
role: TYPE_COMPANY_MEMBER,
email: data[key][Company.KEY][key].email,
name: data[key][Company.KEY][key].name,
schoolName: data[key][Company.KEY][key].schoolName
}
} else if (data[key][Teacher.KEY]) { // Check if it's a teacher
if (data[key][Teacher.KEY][key][Company.KEY]) { // Check if it's a company teacher
return {
role: TYPE_COMPANY_TEACHER,
email: data[key][Teacher.KEY][key].email,
name: data[key][Teacher.KEY][key].name,
schoolName: data[key][Teacher.KEY][key].schoolName,
subscription: data[key][Subscription.KEY]
}
} else { // Check if it's an individual teacher
return {
role: TYPE_INDIVIDUAL_TEACHER,
email: data[key][Teacher.KEY][key].email,
name: data[key][Teacher.KEY][key].name,
schoolName: data[key][Teacher.KEY][key].schoolName,
subscription: data[key][Subscription.KEY]
}
}
} else {
return null;
}
});
return users || null;
},

firebase realtime database query

I have the following structure:
{
"Campaign" : {
"-KtghP_NMOFrjN_RrI6f" : {
"Projects" : {
"-Kz5g4j8dKgxtqQsPfN1" : {
"createByName" : "Michal",
"profileId" : "-KtlDwI3Bq4Bi7R23kya"
},
"-KzGCaLblTxzu4Nje15Z" : {
"createByName" : "Roy",
"profileId" : "-Kxx_egu9h4GOrxqM1nB"
}
}
},
"-KyjE0HPNSg27Kpurq8l" : {
"Projects" : {
"-KzBUZBsI947HckV296O" : {
"createByName" : "Roy",
"profileId" : "-Kxx_egu9h4GOrxqM1nB"
}
}
}
},
"UserProfile" : {
"-Kxx_egu9h4GOrxqM1nB" : {
"MyProjects" : {
"-KzGC3Yn4bAAorcwDhUT" : {
"CampaignId" : "-KyjE0HPNSg27Kpurq8l",
"projectId" : "-KzBUZBsI947HckV296O"
},
"-KzGCaeTqMm_g1Jq6u6i" : {
"CampaignId" : "-KtghP_NMOFrjN_RrI6f",
"projectId" : "-KzGCaLblTxzu4Nje15Z"
}
},
"firstName" : "roy"
}
}
}
i have the profile id.
I need to get the different projects of the user, under the different Campaigns..
I want to get the list of MyProjects by the userid, and then iterate (with the keys i got from MyProjects) over the campaigns -(key)-> projects -(key)-> profileId and compare them...
(hope it was clear enough...)
HOW DO I DO THAT?
Ok, after some more research, i went a different way.
it seems like i don't need "MyProjects".
var userId = "-Kxx_egu9h4GOrxqM1nB";
var campaignRef = this.db.app.database().ref('Campaign'); //root
var projectsRef = campaignRef.child('Projects');
projectsRef.orderByChild('profileId').equalTo(userId).once("value", (snap) => {
console.log(snap.val());
});
but still i get NULL..
(tried "child_added" and .on in different combinations - nothing...)
After logging in, and pulling the Campaign object, you can try running the following code:
// user is an object from the userProfile. You can also just directly access the object and get what you need. Really, it's up to you. I'll assume that you also stored the user ID (uid) here too.
var user = {},
campaigns = {},
uid = {},
myProjects = [];
function getProjects() {
for (var i in campaigns) {
for (var k in campaigns[i]) {
// Make sure that the profileId of the project and the uid match, then push object into myProjects array
campaigns[i][k].profileId === uid ? myProjects.push(campaigns[i][k]) : 'nada';
}
}
}
In the myProjects array will you find your projects.

Lambda nodeJS 4.3 not finishing/executing success callback

Despite running the log statement immediately above it, my call to callback(null) isn't working. Even tried wrapping it in a try catch block but got nothing.
For reference, here's the full function:
var Firebase = require('firebase');
var request = require('request');
//noinspection AnonymousFunctionJS
/**
*
* #param event - from Lambda
* #param context - from Lambda
* #param callback - from Lambda
*/
exports.handler = function (event, context, callback) {
var AUTOPILOT_API_KEY = getKey(event.stage, 'AUTOPILOT_API_KEY');
var AUTOPILOT_JOURNEY_ID = getKey(event.stage, 'AUTOPILOT_JOURNEY_ID');
activate();
function activate () {
console.log('event:', event);
if (validPayload(event, context)) {
addDefaultPresets(event.uid);
addToAutopilot(event.user, event.uid);
}
}
/**
* checks that the necessary payload has been received
* if YES: returns true and allows process to continue
* if NO: throws context.fail with useful error message(s)
* operating under custom error code naming convention of
* http code + 3 digit ULM error code
* #param event - from Lambda
* #param context - from Lambda
* #returns {boolean} - whether the payload contains the required data
*/
function validPayload (event, context) {
return true; // REDACTED FOR BREVITY
}
/**
* Adds the user to Autopilot as a contact and adds them
* to the journey with trigger id 0001
* #param {Object} user
* #param {string} uid generate by Firebase as unique identifier of registered user
*/
function addToAutopilot (user, uid) {
// REDACTED FOR BREVITY
request({
method: 'POST',
url: 'https://api2.autopilothq.com/v1/trigger/' + AUTOPILOT_JOURNEY_ID + '/contact',
headers: {
'autopilotapikey': AUTOPILOT_API_KEY,
'Content-Type': 'application/json'
},
body: JSON.stringify(payload)
}, function (error, response, body) {
//noinspection MagicNumberJS
if (response.statusCode !== 200) {
errorResponse.status = response.statusCode;
errorResponse.error = {
errorMessage: error,
user: event.user,
response: response,
body: body
};
console.log('should throw ERROR callback');
context.fail(JSON.stringify(errorResponse));
} else {
console.log('should throw SUCCESS callback');
console.log(JSON.stringify({
status: response.statusCode,
message: "User successfully added to Autopilot account & journey"
}));
callback(null);
}
console.log('Finished addToAutopilot()');
});
}
/**
* Adds a collection of presets the the account of the new user
* #param uid {String} - Firebase UID
*/
function addDefaultPresets (uid) {
// REDACTED FOR BREVITY
var presets = ref.child('users').child(uid).child('presets');
console.log('Starting addDefaultPresets()');
activate();
function activate () {
console.info('activating...');
// for each field
fields.forEach(function (field) {
// iterate over each preset
presetData[field].forEach(function (label) {
// and add to firebase db via addDefaultPreset() if unique
presetIsUnique(field, label);
})
});
console.log('Finished addDefaultPresets()');
}
function presetIsUnique (field, label) {
presets.child(field).orderByChild('label')
.equalTo(label)
.once('value', function (snapshot) {
var val = snapshot.val();
if (!val) {
addDefaultPreset(field, label);
} else {
console.error('already exists', field, label);
}
});
}
function addDefaultPreset (field, label) {
//noinspection MagicNumberJS
presets.child(field).push().set({
creator: 'default',
dateAdded: Math.floor(Date.now() / 1000),
label: label
}, setCallback);
}
function setCallback (err) {
if (err) {
console.error(err);
} else {
console.info('added preset');
}
}
}
function getKey (stage, keyId) {
var keys = {
AUTOPILOT_API_KEY: {
staging: 'dev123',
prod: 'prod123'
},
AUTOPILOT_JOURNEY_ID: {
staging: 'XXX',
product: 'XXXX'
},
FIREBASE_URL: {
staging: 'https://staging.firebaseio.com/',
prod: 'https://prod.firebaseio.com/'
}
};
if (stage === 'prod') {
return keys[keyId][stage];
} else {
return keys[keyId]['staging'];
}
}
};
It seems like firebase is keeping something in the event loop. By default, lambda waits until the event loop is empty before terminating. You can set context.callbackWaitsForEmptyEventLoop = false to tell lambda to terminate the function soon after the callback is called, even if there is still items in the event loop.
After hours and hours of debugging here is something that worked for me:
var firebase = require('firebase');
var app = firebase.initializeApp(config);
...
...
...
app.delete().then(function(){
var response = {
statusCode: 200,
body: JSON.stringify({
message: 'Success'
}),
};
callback(null, response);
});
You can read more about delete in the official documentation:
Make the given App unusable and free the resources of all associated
services.
Hope this is helpful!

Resources