Redeliver existing form submission to new webhook - rest

I have set up a webhook between salesforce and Typeform and it's working fine. But Typeform has already filled form submissions. Now I want to deliver these responses to a new webhook is there a way to resync existing form submissions?

I dont think this is possible out of the box. You will need to fetch your responses via Typeform Responses API and feed them to your script or webhook.
It looks like the webhook payload is quite similar to the response returned by the API. You can write a script like this to feed all your existing responses from your typeform to a new webhook:
import fetch from 'node-fetch'
import crypto from 'crypto'
import { createClient } from '#typeform/api-client'
const token = process.env.TF_TOKEN // https://developer.typeform.com/get-started/personal-access-token/
const webhookSecret = process.env.SECRET
const uid = process.env.FORM_ID
const typeformAPI = createClient({ token })
const sleep = async (ms) => new Promise(res => setTimeout(res, ms))
// based on https://glitch.com/edit/#!/tf-webhook-receiver
const calculateSignature = (payload) => {
const hash = crypto
.createHmac('sha256', webhookSecret)
.update(payload)
.digest('base64')
return `sha256=${hash}`
}
const feedResponses = (before) => {
typeformAPI.responses.list({ uid, before }).then(async ({ items }) => {
if (items.length > 0) {
// process each response
for (let i=0; i<items.length; i+=1) {
const item = items[i]
const body = JSON.stringify({
"event_id": Date.now(),
"event_type": "form_response",
"form_response": item
})
const response = await fetch('/your-endpoint', {
method: 'POST',
headers: {
'Typeform-Signature': calculateSignature(body)
},
body,
})
const webhookResponse = await response.text()
console.log(webhookResponse)
await sleep(250) // rate-limit the requests
}
// continue with next page of responses
const { token } = items.at(-1)
feedResponses(token)
}
})
}
feedResponses()

Related

Cookie does not appear to be sent via fetch or hapi server is unable to receive cookie

So I have a simple backend server created with Hapi API and the frontend I'm using fetch. These are on different ports so I have CORs enabled and all the sweet stuff. I'm currently trying to set a refresh token in the browser using a http only cookie. As far as I can verify, the http only cookie is being set in the browser when login function is completed. I'm currently trying to send the http only cookie back to the server so I can set up the refresh token route and I can't seem to send or even verify that http token is sent back to the server.
Here's the server setting.
"use strict";
require("dotenv").config();
const Hapi = require("#hapi/hapi");
const Jwt = require("#hapi/jwt");
const routes = require("./routes/routes");
exports.init = async () => {
const server = Hapi.server({
port: 3000,
host: "localhost",
routes: {
cors: {
origin: ["*"],
credentials: true,
},
},
});
require("./models");
await server.register(Jwt);
server.auth.strategy("jwt", "jwt", {
keys: { key: process.env.SECRET_KEY, algorithms: ["HS256"] },
verify: { aud: false, iss: false, sub: false, exp: true },
validate: false,
});
server.state("refresh", {
ttl: 1000 * 60 * 60 * 24,
isSecure: true,
isHttpOnly: true,
encoding: "base64json",
clearInvalid: true,
strictHeader: true,
isSameSite: "None",
});
server.route(routes);
return server;
};
process.on("unhandledRejection", (err) => {
console.log(err);
process.exit(1);
});
Here's the login request and returns the http only cookie. This part works, the http cookie is returned and set.
const validateUserAndReturnToken = async (req, h) => {
const user = await User.findOne({
$or: [{ email: req.payload.username }, { username: req.payload.username }],
});
if (user) {
const match = await bcrypt.compare(req.payload.password, user.passwordHash);
if (match) {
const token = await createToken(match);
const refreshToken = await createRefreshToken(match);
h.state("refresh", refreshToken);
return { id_token: token, user: formatUser(user) };
} else {
throw boom.notAcceptable("Username and password did not match.");
}
} else {
throw boom.notAcceptable("Username or email was not found.");
}
};
Here's the fetch request I'm using to test sending a http cookie only back. I have credential: include so I don't know what is problem?
import type { DateInfo } from "#/stores/application";
const api = "http://localhost:3000/report";
let token = localStorage.getItem("user-token");
const headers = new Headers();
headers.append("Authorization", `Bearer ${token}`);
headers.append("Content-Type", "application/json");
export const getJobReport = async (dateFilter: DateInfo) => {
let response = await fetch(
`${api}/${dateFilter.startDate}/${dateFilter.endDate}`,
{
method: "GET",
headers,
credentials: "include",
}
);
return await response.json();
};
I have checked the application tab as well as the network request so I know set cookie is being sent and set on the browser. The problem is I can't seem to get the cookie back from the browser when fetch request is sent back to the server.
Here's the code I'm using to just check the existence of the cookie. According to Hapi Doc , req.state[cookie-name] which in this case is 'refresh' should have the cookie value. Refresh is returning undefined so I went up one level and check for req.state and gets an empty object {}.
route
{
method: "GET",
path: "/report/{startDate}/{endDate}",
options: {
auth: "jwt",
state: {
parse: true,
failAction: "error",
},
validate: {
params: Joi.object({
startDate: Joi.string(),
endDate: Joi.string(),
}),
},
},
handler: handlers.report.getJobApplicationReport,
},
handler
const getJobApplicationReport = async (req, h) => {
console.log("TEST", req.state);
const start = new Date(req.params.startDate);
const end = new Date(req.params.endDate);
try {
const applications = await Application.find({
dateApplied: { $gte: start, $lt: end },
});
// 'Applied', 'In Process', 'Rejected', 'Received Offer'
const total = applications.length;
let rejectedCount = 0;
let inProcessCount = 0;
applications.forEach((app) => {
if (app.status === "Rejected") {
rejectedCount++;
}
if (app.status === "In Process") {
inProcessCount++;
}
});
return {
total,
rejectedCount,
inProcessCount,
};
} catch (error) {
console.log(error);
throw boom.badRequest(error);
}
};
I've looked through all the Hapi documentation, fetch documentation and stackoverflow question/answers but can't seem to find a solution. I can't verify whether it's the fetch request that's not sending the http only cookie or the server setting that's not parsing it. Any help to determine the issue or solution would be greatly appreciated.
I've looked through all the Hapi documentation, fetch documentation and stackoverflow question/answers but can't seem to find a solution. I can't verify whether it's the fetch request that's not sending the http only cookie or the server setting that's not parsing it. Any help to determine the issue or solution would be greatly appreciated.

Axios sending url with params as string not object

i need to take url with params example:
https://domain.pl/ptpdf-gen?selected_posts=4871&advisor=magda,wojciech
But axios response is an object like:
{"https://domain.pl/ptpdf-gen?selected_posts":"4871","advisor":"magda,wojciech"}
How to send url as string via axios?
Optionally the request above could also be done as
axios.get('/user', {
params: {
selected_posts: 4871
advisor: ["magda", "Wojciech"]
},
paramsSerializer: params => {
return qs.stringify(params)
}
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
})
.then(function () {
// always executed
});
The qs is an external library,
https://www.npmjs.com/package/qs
var selected = 4871
var advisor = ["magda","wojciech"]
axios.post('https://domain.pl/ptpdf-gen', {selected, advisor })
So i made the url split like this, using URLSearchParams:
const currHref = window.location.search;
const urlParams = new URLSearchParams(window.location.search);
const myParam = urlParams.get('selected_posts');
const myParam2 = urlParams.get('advisor');
Then with axios.post i can send params:
axios.post("http://domain.pl/create", {myParam, myParam2})
On server i did handle params like:
const state = req.body;
const stateValues = Object.values(state);
And then i can concat url with stateValues[0] and stateValues[1];
let linkUrl = "https://domain.pl/ptpdf-gen?selected_posts=" + stateValues[0] + "&advisor=" + stateValues[1];
Works.

Add default data to Axios request

I am working on a project with React and a Rails API.
In each of my Axios requests, I want to pass a variable to my API.
Can I configure Axios to tell it to add a variable in the data when I try to POST, DELETE, PUT, PATCH…?
Example:
axios.post('url', { data: 'some_data' }).then(...)
→ API should receive:
data_of_request = { data: 'some_data', added_data_from_config_axios: 'some_variable' }
You can create your own function like this.
const sendPost = (url, data = {}, headers = {}) => {
var body = {...data, added_data_from_config_axios: 'some_variable' };
return axios.post(url, body, { headers });
}
And then, you use this function instead of axios
sendPost(url, { data: 'some_data' }).then(res => {
...
});
Finally I found a better answer.
I just used a built-in function of axios:
const added_data_axios = {
'add_data': '..some_data..'
};
const api = axios.create({
transformRequest: [(data) => {
return {...added_data_axios, ...data};
}, ...axios.defaults.transformRequest],
});

How to build a simple app for Google Home with Dialogflow that returns a random item from an entities list?

I am just starting to use Dialogflow to build some simple apps for my Google Home and I am having trouble creating an app that would simply returns a random name with a sentence.
For example: I say "give us a challenge": I want the app to return something like $random_name should do 10 push ups.
Is this possible to achieve?
Thank you!
As the comment above, you need to use your fulfillment to determine the name and reply it to the user. Simply, you can use like the code below to return the name from fixed name's array:
"use strict";
process.env.DEBUG = "actions-on-google:*";
const App = require("actions-on-google").DialogflowApp;
const request = require("request");
const nameListFromConst = [
"name1", "name2", "name3", "name4", "name5",
"name6", "name7", "name8", "name9", "name10"
];
exports.foobar = (req, res) => {
const app = new App({request: req, response: res});
const inputWelcome = app => {
const index = Math.floor(Math.random() * 10);
const name = nameListFromConst[index];
app.ask(name);
};
const actionMap = new Map();
actionMap.set("input.welcome", inputWelcome);
app.handleRequest(actionMap);
};
But, it seems that you want to determine the name from entities you registered into your agent of Dialogflow. If true, you can retrieve your entities with Dialogflow API dynamically in the fulfillment code like the following:
exports.foobar = (req, res) => {
const app = new App({request: req, response: res});
const inputWelcome = app => {
const options = {
url: "https://api.dialogflow.com/v1/entities/{YOUR_ENTITY_ID}",
method: "GET",
headers: {
"Authorization": "Bearer {YOUR_DEVELOPER_ACCESS_TOKEN}",
"Content-type": "application/json; charset=UTF-8"
},
json: true
};
request(options, (error, response, body) => {
if (error) {
console.log(error);
app.ask("Error occurred.");
} else {
const nameListFromEntity = body.entries.map(x => {
return x.value;
});
const index = Math.floor(Math.random() * 10);
const name = nameListFromEntity[index];
app.ask(name);
}
});
};
const actionMap = new Map();
actionMap.set("input.welcome", inputWelcome);
app.handleRequest(actionMap);
};

How to translate superagent to axios?

I have some upload working for superagent. It involves posting to an api for cloudinary. My question is how do I do the same thing with axios. I'm not sure what superagent.attach and superagent.field relate to in axios.
Basically when I make the post request I need to attach all these fields to the request or else I get bad request and I want to do this in axios not superagent as I am switching over to axios.
Here are all the params:
const image = files[0];
const cloudName = 'tbaustin';
const url = `https://api.cloudinary.com/v1_1/${cloudName}/image/upload`;
const timestamp = Date.now()/1000;
const uploadPreset = 'cnh7rzwp';
const paramsStr = `timestamp=${timestamp}&upload_preset=${uploadPreset}ORor-6scjYwQGpNBvMW2HGMkc8k`;
const signature = sha1(paramsStr);
const params = {
'api_key': '177287448318217',
'timestamp': timestamp,
'upload_preset': uploadPreset,
'signature': signature
}
Here is the superagent post request:
let uploadRequest = superagent.post(url)
uploadRequest.attach('file', image);
Object.keys(params).forEach((key) => {
uploadRequest.field(key, params[key]);
});
uploadRequest.end((err, res) => {
if(err) {
alert(err);
return
}
You would need to use FromData as follows:
var url = `https://api.cloudinary.com/v1_1/${cloudName}/upload`;
var fd = new FormData();
fd.append("upload_preset", unsignedUploadPreset);
fd.append("tags", "browser_upload"); // Optional - add tag for image admin in Cloudinary
fd.append("signature", signature);
fd.append("file", file);
const config = {
headers: { "X-Requested-With": "XMLHttpRequest" },
onUploadProgress: function(progressEvent) {
// Do something with the native progress event
}
};
axios.post(url, fd, config)
.then(function (res) {
// File uploaded successfully
console.log(res.data);
})
.catch(function (err) {
console.error('err', err);
});
See full example here