thread in script based component not working - coldfusion

When I comment out the thread related code below, the submission to the slack API works as expected. However, when I attempt to process the submission within a thread command, the submission never goes through.
Am I doing something wrong? Running in Railo 4
// push to slack!
public function push(options = {}) {
var http = new http();
var data = {
"text": "Default message",
"channel": "activity"
};
structAppend(data, options);
// thread
// action="run" {
sleep(5000);
http.setMethod("post");
http.setUrl(variables.slackWebhookUrl);
http.addParam(
type = "formField",
name = "payload",
value = serializeJson(data)
);
http.send();
// }
}

Credit goes to Adam Cameron for pointing me in the right direction. As it turns out, scoping is quite tricky with cfthreads.
For the sake of brevity, I will just say that I will follow these rules when using threads in the future:
do not reference any variables from scopes outside the "thread block"
explicitly pass into the thread any "parent scope" data you need to reference
basically treat the cfthread as a cfmodule (passing data through attributes)
This is working code:
// push to slack!
public function push(options = {}) {
var data = {
"text": "Default message",
"channel": "activity"
};
structAppend(data, options);
thread
action="run"
data="#data#"
slackUrl="#variables.slackWebhookUrl#" {
var http = new http();
http.setMethod("post");
http.setUrl(attributes.slackUrl);
http.addParam(
type = "formField",
name = "payload",
value = serializeJson(attributes.data)
);
http.send();
}
}

Related

Dropzone.js + AWS S3 stalling queue

I'm trying to impliment a dropzone.js uploader to amazon S3 using the aws-sdk.js for the browser. But when I exceed the 'parallelUploads' maximum in the settings, the queue never completes. I'm using the approach in the following link:
amazon upload
relevant parts of my code:
var dz = new Dropzone("#DZContainer", {
acceptedFiles: "image/*,.jpg,.jpeg,.png,.gif",
autoQueue: true,
autoProcessQueue: true,
parallelUploads: 10,
clickable: [".uploadButton"],
accept: function(file, done){
let params = {
"Bucket": "upload-bucket",
"Key": getFullKey(file.name),
Body: file,
Region: "us-east-1,
ContentType: file.type
}
file.s3upload = AWS.S3.ManagedUpload(params);
if (typeof(done) === 'function') done();
},
canceled: function(file) {
if (file.s3upload) file.s3upload.abort();
},
init: function () {
this.on('removedfile', function (file) {
if (file.s3upload) file.s3upload.abort();
});
}
)
dz.uploadFiles = function (files) {
for (var j = 0; j < files.length; j++) {
var file = files[j];
dz.SendFile(file);
}
};
dz.SendFile = function(file) {
file.s3upload.send(function (err, data) {
if (err) {
console.err(err)
dz.emit("error", file, err.message);
} else {
dz.emit("complete", file);
}
});
if I drag in (or use the clickable) more than 10 files, the first 10 complete but it never processes the rest of the queue. What am I missing? All help is appreciated
EDIT: With a little more digging into Dropzone, it looks as though the file status is never getting set to complete. I see a function called _finished() in the dropzone code, but I'm having a hard time figuring out what specifically is supposed to trigger that function. I have tried dz.emit("complete", file) listed below as well as adding dz.emit("success",file) but my breakpoint at the first line of the _finished() function never triggers. Thus the file.status never gets set to completed.
Does anyone know when/what/how _finished() is supposed to be run?
As mentioned in the edit, I was able to track down where the .status was not properly getting set. This seemed to be in a private Dropzone function called _finished()
With further examination, I noticed that _finished() seemed to also be calling emit("complete", file) after setting file.status to Dropzone.SUCCESS and also emitting "success". It then checks if autoProcessQueue is set and if it is, returns the result of a processQueue() call.
I had a hard time figuring out what triggered this function as it was on an onload event that eventually realized was tied to an XHTTPRequest object used by the internal uploader (which is being overridden by the S3 uploader)
So I modified the function to emulate what the Dropzone._finished() was doing and it's behaving as expected:
dz.SendFile = function(file) {
file.s3upload.send(function (err, data) {
if (err) {
console.err(err)
dz.emit("error", file, err.message);
} else {
file.status = Dropzone.SUCCESS;
dz.emit("success", file, data, err);
dz.emit("complete", file);
if(dz.options.autoProcessQueue)
dz.processQueue()
}
});

Google Meet: How can i catch the url?

I need to open a new google meet room, and send it. I can't use standard "share" button in app. I need to catch the final url.
I can't catch that with curl (it's not a normal redirect).
My idea is that i need to open a request/link in background or in the same page, wait some second and catch the link, after i can release the page and user can enter.
Do you know something that can help me?
Edit:
Yes, i had miss to tell that i need to generate a room from a click and catch the url from code. Generally, i should to make this with Google Calendar API, but in this case i can't.
I use google Calendar API. I make a webApp for my organization, that from a form (with user information to send togheter meet link) make a google calendar event with google meet room (from google loggedin user account), catch the link and send it by a smsGateway.
function FormForMeet (Args) {
// get calendar name, if it already exists
var meetsCalendar = CalendarApp.getCalendarsByName ('CalendarName');
Logger.log (meetsCalendar.length)
if (meetsCalendar.length> = 1) {
// if some calendar be found, it catch the first, obviously here you can use some differet method. I had choose this because I don't expect to find more than 1
var calendar = meetsCalendar [0] .getId ();
}
else
{
// If miss, create new one.
var calendar = CalendarApp.createCalendar ('CalendarName', {summary: 'descrip Calendar',
// set a color of calendar label :D
color: CalendarApp.Color.PURPLE});
calendar = calendar.getId ();
}
// Call function to create meet
var LinkMeet = CreateConference_ (calendar);
// here you can use what you want for send Args + LinkMeet);
// if you want return link
return LinkMeet;
}
// Function to create Conference. You can obviously use the code up without make a new function.
function CreateConference_ (calendarId) {
// Custom of event, here I created the conferences according to my needs (now, with 1 h / conference)
var now = new Date ();
var start = new Date (now.getTime ()). toISOString ();
var end = new Date (now.getTime () + (1 * 60 * 60 * 1000)). toISOString ();
// generate random string to request
var rdmreqId = genStrg ();
var event = {
"end": {
"dateTime": end
},
"start": {
"dateTime": start
},
"summary": "conferenceName",
"conferenceData": {
"createRequest": {
"conferenceSolutionKey": {
"type": "hangoutsMeet"
},
"requestId": rdmreqId
}
}
};
// insert event in calendar
event = Calendar.Events.insert (event, calendarId, {
"conferenceDataVersion": 1});
// if use the function you can return the link to send
return event.hangoutLink
}
// random strind
function genStrg () {
var data = "something";
var text = "";
var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789! # # $% & <> * -";
for (var j = 2; j <= data.length; j ++) {
text = ""; // Reset text to empty string
for (var i = 0; i <possible.length; i ++) {
text + = possible.charAt (Math.floor (Math.random () * possible.length));
}
return text;
}
}
all google meet links look something like this:
https://meet.google.com/abc-defg-hij
You should be able to just copy and paste this link from your browser page. If someone enters this link, they will be taken to the meet lobby and they can enter at any time.
If you can't access this link for some reason, like if you're on mobile, you have to put your meet code (the abc-defg-hij) at the end of the aforementioned url.
edit: You actually can find the link if you're on mobile by going into your meeting lobby and scrolling down until you get to "joining information". Under that there should be the meeting link and the numbers to join by phone.

Google Classroom API : Unable to receive push notifications for courseworks and student submissions (COURSE_WORK_CHANGES)

We are seriously blocked. We have followed the documentation below (among many others) to set up the pub/sub pipelines, create service accounts, assign permissions and use the right scopes and feed types for registrations.
https://developers.google.com/classroom/guides/push-notifications
So programmatically we are able to do the following in .net using the API :
We can Create Courses
We can create registrations for a given courseid
We create/update courseworks for the course that we have created a registration.
All good so far,
BUT, we don't receive notifications for that created/update course work.
some code for clarity :
ServiceAccountCredential credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer("sa-something#precise-asset-259113.iam.gserviceaccount.com")
{
User = "impersonated user",
Scopes = new string[] { "https://www.googleapis.com/auth/classroom.coursework.students" ,
"https://www.googleapis.com/auth/classroom.courses",
"https://www.googleapis.com/auth/classroom.push-notifications" }}
.FromPrivateKey("My private key"));
//Authorize request
var result = credential.RequestAccessTokenAsync(CancellationToken.None).Result;
var service = new ClassroomService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
});
// We get courses
var courses = service.Courses.List().Execute();
// We get one course for registration
var course = courses.Courses.First();
// We create registration
var registration = service.Registrations.Create(new Google.Apis.Classroom.v1.Data.Registration()
{
Feed = new Google.Apis.Classroom.v1.Data.Feed()
{
FeedType = "COURSE_WORK_CHANGES",
CourseWorkChangesInfo = new Google.Apis.Classroom.v1.Data.CourseWorkChangesInfo()
{
CourseId = course.Id
},
},
CloudPubsubTopic = new Google.Apis.Classroom.v1.Data.CloudPubsubTopic()
{
TopicName = "projects/precise-asset-259113/topics/test"
},
});
//Successful response - We get a registrationID
var response = registration.Execute();
var courseWork = new CourseWork()
{
CourseId = course.Id,
Title = "Ver Test",
Description = "Calculus",
WorkType = "ASSIGNMENT",
MaxPoints = 20.0,
State = "PUBLISHED",
AlternateLink = "www.uni.com",
CreatorUserId = course.OwnerId,
CreationTime = DateTime.UtcNow,
DueTime = new TimeOfDay() { Hours = 5, Minutes = 10, Nanos = 10, Seconds = 10 },
DueDate = new Date() { Day = 3, Month = 12, Year = 2019 },
Assignment = new Assignment() { StudentWorkFolder = new DriveFolder() { AlternateLink = "Somewhere", Title = "My Calculus" } }
};
//Create course work for the course that we registered
var courseWorkResponse = service.Courses.CourseWork.Create(courseWork, course.Id).Execute();
SubscriberServiceApiClient subscriber = SubscriberServiceApiClient.Create();
SubscriptionName subscriptionName = new SubscriptionName("precise-asset-259113", "test");
PullResponse pullResponse = subscriber.Pull(
subscriptionName, returnImmediately: true, maxMessages: 20);
// Check for push notifications BUT ....NADA!!!
foreach (ReceivedMessage msg in pullResponse.ReceivedMessages)
{
string text = Encoding.UTF8.GetString(msg.Message.Data.ToArray());
Console.WriteLine($"Message {msg.Message.MessageId}: {text}");
}
Can you please assist?
Thanks
There are several things you need to change in order to ensure you get the notifications:
You need to create your subscription before you send the request that will generate the Pub/Sub message. Only messages published after the successful creation of a subscription are guaranteed to be received by subscribers for that subscription.
A single pull request with returnImmediately set to true is unlikely to return any messages, even if a message has been published. With this property set, if there are no messages immediately in memory of the server reached, the empty response is returned. You should always set returnImmediately to false. This still won't guarantee that messages are returned in a single request/response, even if there are messages available, but it will make it more likely.
Ideally, you would use the asynchronous client library, which opens a stream to the Cloud Pub/Sub service and receives messages as soon as they are available. If you are going to use the synchronous Pull method directly, then you need to keep many of them outstanding simultaneously in order to ensure delivery of messages with minimal latency. As soon as you receive a PullResponse for any of the outstanding requests, you should immediately open up another request to replace it. The goal of the asynchronous client library is to prevent one from having to take all of these step manually to ensure efficient delivery.

Alexa ASK Lambda bug

I'm trying to make a skill where after the LaunchRequest, an initial welcome message is played in the function StartGame asking the user for their school, and then the user says their school in the SetSchool intent, and then the skill says a message. Right now there's a bug in the last part, and I don't know how to debug it.
The error:
My code:
/* eslint-disable func-names */
/* eslint-disable dot-notation */
/* eslint-disable new-cap */
/* eslint quote-props: ['error', 'consistent']*/
/**
* This sample demonstrates a simple skill built with the Amazon Alexa Skills
* nodejs skill development kit.
* This sample supports en-US lauguage.
* The Intent Schema, Custom Slots and Sample Utterances for this skill, as well
* as testing instructions are located at https://github.com/alexa/skill-sample-nodejs-trivia
**/
'use strict';
const Alexa = require('alexa-sdk');
const questions = require('./question');
const ANSWER_COUNT = 4; // The number of possible answers per trivia question.
const GAME_LENGTH = 10; // The number of questions per trivia game.
const GAME_STATES = {
TRIVIA: '_TRIVIAMODE', // Asking trivia questions.
START: '_STARTMODE', // Entry point, start the game.
HELP: '_HELPMODE', // The user is asking for help.
};
const APP_ID = undefined; // TODO replace with your app ID (OPTIONAL)
const languageString = {
'en': {
'translation': {
'QUESTIONS': questions['HS_QUESTIONS_EN_US'],
'GAME_NAME': 'Science Bowl',
'HELP_MESSAGE': 'I will ask you %s multiple choice questions. Respond with the number of the answer. ' +
'For example, say one, two, three, or four. To start a new game at any time, say, start game. ',
'REPEAT_QUESTION_MESSAGE': 'To repeat the last question, say, repeat. ',
'ASK_MESSAGE_START': 'Would you like to start playing?',
...
},
},
};
const newSessionHandlers = {
'LaunchRequest': function () {
this.handler.state = GAME_STATES.START;
this.emitWithState('StartGame', true);
},
'SetSchool': function() {
this.handler.state = GAME_STATES.START;
this.emitWithState('School', true);
},
'AMAZON.StartOverIntent': function () {
this.handler.state = GAME_STATES.START;
this.emitWithState('StartGame', true);
},
'AMAZON.HelpIntent': function () {
this.handler.state = GAME_STATES.HELP;
this.emitWithState('helpTheUser', true);
},
'Unhandled': function () {
const speechOutput = this.t('START_UNHANDLED');
this.emit(':ask', speechOutput, speechOutput);
},
};
...
const startStateHandlers = Alexa.CreateStateHandler(GAME_STATES.START, {
'StartGame': function (newGame) {
let speechOutput = newGame ? this.t('NEW_GAME_MESSAGE', this.t('GAME_NAME')) + this.t('WELCOME_MESSAGE', GAME_LENGTH.toString()) : '';
this.handler.state = GAME_STATES.START;
this.emit(':ask', speechOutput, speechOutput);
},
'School': function(newGame) {
this.handler.state = GAME_STATES.START;
this.response.speak('test');
this.emit(':responseReady');
}
});
exports.handler = function (event, context) {
const alexa = Alexa.handler(event, context);
alexa.appId = APP_ID;
// To enable string internationalization (i18n) features, set a resources object.
alexa.resources = languageString;
alexa.registerHandlers(newSessionHandlers, startStateHandlers, triviaStateHandlers, helpStateHandlers); // these were defined earlier
alexa.execute();
};
I excluded most of the code so it would fit here. I would like to try and debug it but I don't even know how to view the error messages. What do I do?
If you're hosting on AWS Lambda logs can be found in CloudWatch.
From the AWS console open cloudwatch, then click on the Logs link in the left hand menu. you should be able to find your Lambda service from there.
That said your problem seems to be an issue of Intent definition by state.
You've already set the state to START but the startStateHandlers doesn't have the SetSchool Intent defined.
To fix you'd either have to add a SetSchool intent definition to the startStateHandlers OR reset the state to one that does contain the SetSchool intent before emitting your response in the StartGame handler.

Loopback strange behaviour

I am talking about loopback push component. I am trying to intercept the "create" method of "Installation" model. My code looks like this -
server/boot/installationex.js
module.exports = function (app) {
var Installation = app.models.Installation;
var create = Installation.create;
Installation.create = function (data, cb) {
//reinitializing old implementation
this.create = create;
console.log("Received data: "+JSON.stringify(data));
if (!data || !data.imei) {
console.log("No data or imei was provided, creating new");
this.create(data, cb);
return;
}
//saving 'this' reference
var that = this;
//search by imei filter
var filter = {where: {imei: data.imei}};
this.findOne(filter, function (err, result) {
if (err) {
console.log("Error occurred while looking for installation by IMEI");
cb(err);
return;
}
if (!result) {
console.log("No installation found by IMEI, will create a new installation");
that.create(data, cb);
return;
}
console.log("Found existing installation with id: " + JSON.stringify(result));
result.deviceToken = result.gpsLocation = result.osVersion = result.vendor = result.phoneNumbers = null;
if (data.deviceToken) {
result.deviceToken = data.deviceToken;
}
if (data.gpsLocation) {
result.gpsLocation = data.gpsLocation;
}
if (data.osVersion) {
result.osVersion = data.osVersion;
}
if (data.vendor) {
//result.vendor=data.vendor;
result.vendor = 'jahid';
}
if (data.phoneNumbers) {
result.phoneNumbers = data.phoneNumbers;
}
that.upsert(result, cb);
});
}
}
Unfortunately this code is invoked only once, I mean the first time. After that this code is never invoked. I became sure by looking at the log. It only prints the log first time. After that it does not print any log.
Any idea why this glue code is only invoked once? My intention is to intercept all create method invocation for Installation model. And check if there is already an entry for supplied "IMEI", if so then reuse that. Otherwise create new.
Thanks in advance.
Best regards,
Jahid
What I would start here with is:
instead of implementing your own intercepting mechanism use Model Hooks
check out findOrCreate() method
boot scripts are only run once during application startup. if you want a function that triggers every time a function is called, use a remote hook or model hook. probably something along the lines of:
...
Installation.beforeRemote('create', ...
...
see http://docs.strongloop.com/display/LB/Adding+logic+to+models for more info