Swift/Firebase: Can't send request data on a Firebase Cloud Function - javascript

I can't seem to send an identifier over a request body to Firebase from a Swift iOS app.
My javascript code works (I tested it in an API builder):
exports.numberOfVideos = functions.https.onRequest((request, response) => {
var attributionID = request.body.data.attributionID || "No attributionID found.";
functions.logger.info('[LOG]: request.body has a value of ' + JSON.stringify(request.body), {structuredData: true});
var db = admin.firestore();
db.collection("content").get().then(snapshot => {
var total = 0;
snapshot.forEach(doc => {
if (doc.data().attributionID == attributionID) {
total++;
}
// stuff = stuff.concat(newelement);
});
response.send('{"data": {"attributionID": "' + attributionID + '", "numberOfContents": "' + total + '"}}');
return "";
}).catch(reason => {
response.send(reason)
})
});
My Swift code can't see to convey the data:
static func getContentCount(for obj: Object, completion: #escaping (_ count: Int?) -> Void) {
let httpsCallable = functions.httpsCallable("numberOfVideos")
// httpsCallable.setValue("application/json", forKey: "Content-Type")
httpsCallable.call(["attributionID" : obj.id]) { result, error in
if let error = error as NSError? {
if error.domain == FunctionsErrorDomain {
let code = FunctionsErrorCode(rawValue: error.code)
let message = error.localizedDescription
let details = error.userInfo[FunctionsErrorDetailsKey]
print(error)
}
}
if let response = result?.data as? [String: [String: Any]] {
if let data = response["data"] {
if let countAsAString = data["numberOfContents"] as? String {
let count = Int(countAsAString)
if count != nil {
completion(count)
} else {
print("Data is valid but no count found: \(result?.data)")
completion(nil)
}
}
}
} else {
print("Data is invalid: \(result?.data)")
completion(nil)
}
}
}
When it worked from a REST API tester, here was the body:
POST /numberOfVideos HTTP/1.1
Host: *****************
Content-Type: application/json
Content-Length: 49
{"data":{"attributionID":"biNiaWVtmjUKoTQ1fTZu"}}
Any help would be appreciated!

Fixed it. I had to use an NSDictionary, not a Dictionary:
NSDictionary(dictionary: ["attributionID" : obj.id])

Related

It doesn't show me the data in the terminal when i send the post request twice

I am trying to create a middleware that receive a form-data and return the fieldname, contentType and the value. So when I send the firts post the data view in the terminal but if I send the same request again doesn't show me the data in the terminal.
And if a toggle the image, the data come show in the terminal
This is my code:
server:
const express = require("express");
const Upes = require("../upes");
const app = express();
const start = new Upes();
app.post("/", start.setup.bind(start), (req, res) => {
res.send("all right");
});
app.listen(3000, () => {
console.log("The server is active");
});
the index of my middleware:
const getData = require("./utils/getData");
const parseContentType = require("./utils/parseContentType");
class Upes {
setup(req, res, next) {
const contentType = parseContentType(req.headers["content-type"]);
if (!contentType) {
throw new Error("Malformed content-type");
}
const SUBTYPES = ["form-data", "x-www-form-urlencoded"];
if (!SUBTYPES.includes(contentType.subtype)) {
throw new Error(
"The subtypes does not match the following subtypes: " + SUBTYPES
);
}
getData(req, contentType.params.boundary, (data) => {
console.log(data);
});
next();
}
}
module.exports = Upes;
The function that receive the data and processes it:
function getData(req, boundary, callback) {
let chunk = "";
let data = [];
req.on("data", (buffer) => {
chunk += buffer.toString();
});
req.on("end", () => {
// Split the chunk in blocks
const blocks = getBlock(chunk, boundary);
blocks.forEach((block) => {
let [params, value] = block.split("\r\n\r\n");
params = params.split(";");
let fieldname = params[1].split("=")[1].replaceAll('"', "");
let contentType = () => {
const condition = params.length === 3;
if (condition) {
let type = params[2].split(":")[1].replace(" ", "");
return type;
}
return "text-plain";
};
const payload = {
fieldname: fieldname,
contentType: contentType(),
value: "", // value.replace("\r\n", "")
};
data.push(payload);
});
callback(data);
});
}
function getBlock(body, boundary) {
boundary = boundary.replaceAll("-", "");
return body.replaceAll("-", "").split(`${boundary}`).slice(1, -1);
}
module.exports = getData;
Send the same request 20 times
I don't know what happend, please can someone help me?

Nodejs TypeError: Cannot read properties of undefined (reading 'refresh')

I need your help, it turns out that I am trying to use the Hubstaff api. I am working on nodejs to make the connection, I followed the documentation (official hubstaff api documentation) and use the methods they give as implementation examples (example of implementation nodejs).
But I get the following error:
I don't know why this happens, and I can't find more examples of how I can implement this api. The openid-client lib is used to make the connection through the token and a state follow-up is carried out to refresh the token.
To be honest, I'm not understanding how to implement it. Can someone who has already used this API give me a little explanation? I attach the code
hubstaffConnect.util
const {
Issuer,
TokenSet
} = require('openid-client');
const fs = require('fs');
const jose = require('jose');
// constants
const ISSUER_EXPIRE_DURATION = 7 * 24 * 60 * 60; // 1 week
const ACCESS_TOKEN_EXPIRATION_FUZZ = 30; // 30 seconds
const ISSUER_DISCOVERY_URL = 'https://account.hubstaff.com';
// API URl with trailing slash
const API_BASE_URL = 'https://api.hubstaff.com/';
let state = {
api_base_url: API_BASE_URL,
issuer_url: ISSUER_DISCOVERY_URL,
issuer: {}, // The issuer discovered configuration
issuer_expires_at: 0,
token: {},
};
let client;
function loadState() {
return fs.readFileSync('./configState.json', 'utf8');
}
function saveState() {
fs.writeFileSync('./configState.json', JSON.stringify(state, null, 2), 'utf8');
console.log('State saved');
}
function unixTimeNow() {
return Date.now() / 1000;
}
async function checkToken() {
if (!state.token.access_token || state.token.expires_at < (unixTimeNow() + ACCESS_TOKEN_EXPIRATION_FUZZ)) {
console.log('Refresh token');
state.token = await client.refresh(state.token);
console.log('Token refreshed');
saveState();
}
}
async function initialize() {
console.log('API Hubstaff API');
let data = loadState();
data = JSON.parse(data);
if (data.issuer) {
state.issuer = new Issuer(data.issuer);
state.issuer_expires_at = data.issuer_expires_at;
}
if (data.token) {
state.token = new TokenSet(data.token);
}
if (data.issuer_url) {
state.issuer_url = data.issuer_url;
}
if (data.api_base_url) {
state.api_base_url = data.api_base_url;
}
if (!state.issuer_expires_at || state.issuer_expires_at < unixTimeNow()) {
console.log('Discovering');
state.issuer = await Issuer.discover(state.issuer_url);
state.issuer_expires_at = unixTimeNow() + ISSUER_EXPIRE_DURATION;
console.log(state.issuer);
}
client = new state.issuer.Client({
// For personal access token we can use PAT/PAT.
// This is only needed because the library requires a client_id where as the API endpoint does not require it
client_id: 'PAT',
client_secret: 'PAT',
});
saveState();
console.log('API Hubstaff initialized');
}
async function request(url, options) {
await checkToken();
let fullUrl = state.api_base_url + url;
return client.requestResource(fullUrl, state.token, options);
}
function tokenDetails() {
let ret = {};
if (state.token.access_token) {
ret.access_token = jose.JWT.decode(state.token.access_token);
}
if (state.token.refresh_token) {
ret.refresh_token = jose.JWT.decode(state.token.refresh_token);
}
return ret;
}
module.exports = {
initialize,
checkToken,
request,
tokenDetails
};
controller
const usersGet = async(req, res = response) => {
const response = await api.request('v2/organizations', {
method: 'GET',
json: true,
});
const body = JSON.parse(response.body);
res.render('organizations', {
title: 'Organization list',
organizations: body.organizations || []
});
};

error firebase functions [Promises must be handled appropriately] on deploy

I was written a code previous week and it deploys without any error on firebase server. but now I cannot deploy it again on another account in orders to I don't change my code!
one of my friends tell me this in about new update of firebase but I don't find any solution for this!
it shows these errors
Promises must be handled appropriately
and
block is empty
the first error pointed to my first line and the second one pointed to end 'catch' block :
import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin';
admin.initializeApp();
// export const helloWorld = functions.https.onRequest((request, response) => {
// console.log("sadegh");
// response.send("Hello from Firebase1!");
// });
//
export const sendChatNotification = functions
.firestore.document('rooms/{roomId}/messages/{messageId}')
.onCreate((snap, context) => {
const roomId = context.params.roomId;
const messageId = context.params.messageId;
const newValue = snap.data();
const receiverId = newValue.receiverId;
const text = newValue.text;
const type = newValue.type;
const senderName = newValue.senderName;
var p = admin.firestore().collection("users").doc(receiverId).get();
p.then(snapshot2 => {
const data2 = snapshot2.data();
const firebaseNotificationToken = data2.firebaseNotificationToken;
// const name = data2.name;
if (type == 'voiceCall' || type == 'videoCall' || type == 'hangUp') {
const channelId = newValue.channelId;
const senderId = newValue.senderId;
const status = newValue.status;
console.log("type: " + type + " /status: " + status)
let message = {
data: {
type: type,
senderId: senderId,
senderName: senderName,
receiverId: receiverId,
status: status,
channelId: channelId,
roomId: roomId
},
token: firebaseNotificationToken
};
sendMessage(message)
if (status == "canceled") {
let message1 = {
notification: {
title: '☎ Missed voice call ',
body: senderName
},
token: firebaseNotificationToken
};
sendMessage(message1)
} else if ((type == 'voiceCall' || type == 'videoCall') && status = '') {
let message1 = {
notification: {
title: '☎ ' + senderName + ' is calling you',
body: 'tap to answer...'
},
token: firebaseNotificationToken
};
sendMessage(message1)
}
} else {
let message = {
notification: {
title: '📃 ' + senderName,
body: text
},
token: firebaseNotificationToken
};
sendMessage(message)
}
return "";
}).catch((e) => {
console.log('error: ' + e);
return null;
});
// return "";
// }).catch(e=>{console.log('error: '+e)});
return "sadegh";
});
function sendMessage(message) {
admin.messaging().send(message)
.then((response) => {
// Response is a message ID string.
console.log('Successfully sent message:', response);
})
.catch((error) => {
console.log('Error sending message:', error);
});
}
Your code is a bit messy and it is not really easy to understand it without dedicating a long time.
However, here is below a piece of code that should work and that cover one case of your Business Logic. Note how the promises returned by the asynchronous tasks are returned.
export const sendChatNotification = functions.firestore
.document('rooms/{roomId}/messages/{messageId}')
.onCreate((snap, context) => {
const roomId = context.params.roomId;
const messageId = context.params.messageId;
const newValue = snap.data();
const receiverId = newValue.receiverId;
const text = newValue.text;
const type = newValue.type;
const senderName = newValue.senderName;
var p = admin
.firestore()
.collection('users')
.doc(receiverId)
.get();
return p.then(snapshot2 => { // <- HERE, the promise is returned
const data2 = snapshot2.data();
const firebaseNotificationToken = data2.firebaseNotificationToken;
if (type == 'voiceCall' || type == 'videoCall' || type == 'hangUp') {
const channelId = newValue.channelId;
const senderId = newValue.senderId;
const status = newValue.status;
console.log('type: ' + type + ' /status: ' + status);
let message = {
data: {
type: type,
senderId: senderId,
senderName: senderName,
receiverId: receiverId,
status: status,
channelId: channelId,
roomId: roomId
},
token: firebaseNotificationToken
};
return admin.messaging().send(message); // <- HERE, the promise is returned
}
});
});
I would suggest you watch the 3 videos about "JavaScript Promises" from the Firebase video series: https://firebase.google.com/docs/functions/video-series/
The problem is you commented the return in your catch block
As your Firebase .get() function must return a promise, in your code, if it fails, it won't return a promise and it will hang there.
either use return null or return something to be handled by the calling app

Can't figure out why my app.get is being run twice?

I have a app.get which inside of it is quite a bit of logic. Which everything works great aside from some of the logic being called twice for some reason. I have noticed when I was saving something to by db that it would save two rows.
So I put a console.log in that area and sure enough it was logging it twice.
Any reason why this is happening?
app.get('/shopify/callback', (req, res) => {
const { shop, hmac, code, state } = req.query;
const stateCookie = cookie.parse(req.headers.cookie).state;
if (state !== stateCookie) {
return res.status(403).send('Request origin cannot be verified');
}
if (shop && hmac && code) {
// DONE: Validate request is from Shopify
const map = Object.assign({}, req.query);
delete map['signature'];
delete map['hmac'];
const message = querystring.stringify(map);
const providedHmac = Buffer.from(hmac, 'utf-8');
const generatedHash = Buffer.from(
crypto
.createHmac('sha256', config.oauth.client_secret)
.update(message)
.digest('hex'),
'utf-8'
);
let hashEquals = false;
try {
hashEquals = crypto.timingSafeEqual(generatedHash, providedHmac)
} catch (e) {
hashEquals = false;
};
if (!hashEquals) {
return res.status(400).send('HMAC validation failed');
}
// DONE: Exchange temporary code for a permanent access token
const accessTokenRequestUrl = 'https://' + shop + '/admin/oauth/access_token';
const accessTokenPayload = {
client_id: config.oauth.api_key,
client_secret: config.oauth.client_secret,
code,
};
request.post(accessTokenRequestUrl, { json: accessTokenPayload })
.then((accessTokenResponse) => {
const accessToken = accessTokenResponse.access_token;
// DONE: Use access token to make API call to 'shop' endpoint
const shopRequestUrl = 'https://' + shop + '/admin/shop.json';
const shopRequestHeaders = {
'X-Shopify-Access-Token': accessToken,
}
request.get(shopRequestUrl, { headers: shopRequestHeaders })
.then((shopResponse) => {
const response = JSON.parse(shopResponse);
const shopData = response.shop;
console.log('BEING CALLED TWICE...')
res.render('pages/brand_signup',{
shop: shopData.name
})
})
.catch((error) => {
res.status(error.statusCode).send(error.error.error_description);
});
})
.catch((error) => {
res.status(error.statusCode).send(error.error.error_description);
});
} else {
res.status(400).send('Required parameters missing');
}
});

How do I make this angular2 service somewhat synchronous?

I have an angular2 service and I want it to do the following:
Ftp to remote server
Find a file read some lines from it
Build a 'results' json object and return to the calling component
So - actually I have steps 1 / 2 working - but of course its all 'async'. So what is happening is in my component I am doing this call to the service where this.ftp is the instance of my service:
this.servers = this.ftp.lookForServers();
Now this correctly calls the lookForServers method of my FTP service , which looks like this:
lookForServers(){
var servers = [];
var whereAreWe = 0;
var possibles = ["/path/to/servers/"];
for(var i=0;i<possibles.length;i++){
whereAreWe = i;
this.c.list(possibles[i],false,(err,list)=>{
for(var p=0;p<list.length;p++){
console.log(list[p]);
var server_version = this.grabLog(possibles[whereAreWe]+list[p].name);
servers.push({
name: list[p].name,
path: possibles[whereAreWe]+list[p].name,
version: server_version
});
}
});
}
return servers;
}
Now - the this.grabLog(possibles[whereAreWe]+list[p].name); function call ends up making further calls to the this.c - the FTP client, which of course is async, so this method returns almost immediately - whilst the callbacks continue to run. Those callbacks download a file, and then another callback function processes this file - again line by line, asynchronously picking out various details i want to store.
By the end of this chain - I have all my details in the final :
lineReader.on('close', () => { function - but of course my `this.ftp.lookForServers();` function call has long gone....and the component is none the wiser.
So how can I let this work happen asynchronously, and still pass back to the component my results JSON object once the work is complete? This is probably quite a simple question about how do I make a service call a component callback...?
You don't need it to run syncronously. You should make lookForServers (and the other function it's using) use observables, then subscribe to the result like this:
this.ftp.lookForServers().subscribe((data) => { this.servers = data });
Here are the implementations:
const Client = require('ftp');
const fs = require('fs');
const readline = require('readline');
import { NextObserver } from 'rxjs/Observer';
import { Observable } from 'rxjs/Rx';
interface server {
name: string;
path: string;
version: string;
java_version: string;
}
export class FTPClient {
username: string;
password: string;
host: string;
port: number;
c: any;
constructor() {
}
init(username, password, host, port) {
console.log("initiating FTP connection to:" + host + "on port:" + port);
this.username = username;
this.password = password;
this.host = host;
this.port = port;
this.c = new Client();
console.log("Client created");
}
connect() {
console.log("About to start connection");
this.c.on('ready', () => {
this.c.list((err: any, list: any) => {
if (err) throw err;
console.dir(list);
this.c.end();
});
});
// connect to localhost:21 as anonymous
var connectProps = {
host : this.host,
port : this.port,
user : this.username,
password : this.password
};
console.log("Connecting now...");
this.c.connect(connectProps);
}
public lookForServers(name: string): Observable<any[]> {
return Observable.create((observer: NextObserver <any[]>) => {
let servers = [];
let whereAreWe = 0;
let possibles = [ "/path/to/servers/" ];
for (var i = 0; i < possibles.length; i++) {
whereAreWe = i;
this.c.list(possibles[ i ], false, (err: any, list: any) => {
for (var p = 0; p < list.length; p++) {
this.grabMessagesLog(possibles[ whereAreWe ] + list[ p ].name)
.subscribe((data: any) => {
let server_version = data;
servers.push({
name : list[ p ].name,
path : possibles[ whereAreWe ] + list[ p ].name,
version : server_version
});
observer.next(servers);
observer.complete();
}
);
}
});
}
});
}
grabMessagesLog(path): Observable<any> {
return Observable.create((observer: NextObserver <any>) => {
let result = '';
let unix = Math.round(+new Date() / 1000);
this.c.binary(function(err) {
console.log(err);
});
this.c.get(path + "/logs/messages.log", (err, stream) => {
if (err) throw err;
stream.once('close', () => {
this.c.end();
this.getServerMetadataFromMessagesLog(unix + "_messages.log")
.subscribe((data) => {
stream.pipe(fs.createWriteStream(unix + "_messages.log"));
observer.next(data);
observer.complete();
});
});
});
});
}
getServerMetadataFromMessagesLog(path): Observable<any> {
return Observable.create((observer: NextObserver <any>) => {
let lineReader = readline.createInterface({
input : fs.createReadStream(path)
});
let server_version = "";
let java_version = "";
let line_no = 0;
lineReader.on('line', function(line) {
line_no++;
console.log("line number is:" + line_no);
if (line.includes("STUFF") && line.includes("FLAG2") && line_no == 2) {
var first = line.split("FLAG2")[ 1 ];
var last = first.split(" (")[ 0 ];
var version = "FLAG2" + last;
this.server_version = version;
console.log("version is:" + version);
}
if (line.includes("java.version =")) {
var javav = line.split("java.version =")[ 1 ];
this.java_version = javav;
lineReader.close();
}
console.log('Line from file:', line);
});
lineReader.on('close', () => {
var res = {
version : server_version,
java_version : java_version
};
alert("RES IS:" + JSON.stringify(res));
observer.next(res);
observer.complete();
});
});
}
}
Try using a recursive function with the $timeout function of Angular
function recursiveWait(server_version){
if(server_version != null){
return;
}
$timeout(function(){recursiveWait()}, 500);
}
And place it here:
console.log(list[p]);
var server_version = this.grabLog(possibles[whereAreWe]+list[p].name);
recursiveWait(server_version);
servers.push({
name: list[p].name,
This will ask the var if it's != null If it's equal it will call the function again in 500ms, if it's not it will return and exit the function, letting the code continue.

Categories