Convert response after submitting a form to an API - json

I am sending the contents of a form, ie the name, upload fields etc to an api. After hitting the submit button, a new tab opens and I am getting a response:
{"success":false,"error":{"code":0,"message":"The given data failed to pass validation.","errors":{"something_id":["The something id field is required."]}}}
This (json?) doesn't make sense to a „normal“ user. So is it possible to get the response from the api before a new tab opens and display it in a way, so a user could understand? Like „Success – you can close the tab“ or „There was an error – you need to do this again“?
I don't know much about api and json, so it would be fine to learn if this could/would work?

here is a workaround:
First you need to load jquery on your page by adding this code within the tag or before the closing tag
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
Then give your form an ID (say my_form_id)
Then add this within your HTML
<script>
$(document).ready(function() {
// listen for when the form is submitted
$('#my_form_id').submit(function(event) {
// get the form data
var formData = $('#my_form_id').serialize();
// send the form to API using Ajax
$.ajax({
type : 'POST', // define the type of HTTP we want to use
url : 'your_full_api_url', // the url where you want to POST the data
data : formData, // your form data object
dataType : 'json', // what type of data do you expect from the API
encode : true
}).done(function(data) {
if(data.success==='true') {
//No error... all went well
alert('Your data was successfully submitted');
//You can do any other thing you want here
} else {
//get the error message and alert it
alert(data.message);
}
});
event.preventDefault();
});
});
</script>
Now what happens is that each time the form is submitted, the script is called and the form data is collected and submitted to your API URL using ajax.
The response is then parsed and checked if data was successfully submitted.
If not, it will use the browser alert function to print our the error message from your API.

Related

Delay in between two calls (POST and GET) in Google Apps script

My first request on StackOverflow! I really hope you can help me!
I want to create a process to automatize a data report from a system to a sheet; I thought I could use their API, apps script, and export the data on google sheets.
To do so, I need to run two calls on the API:
A POST call, which runs the report within the system (it requires a date range as body).
In return, I will get an ID that is associated with the data generated and it expires after some time.
A GET call, which is a URL that contains the ID generated in the first call and created with a concatenation.
The first call works fine; I get in return the ID successfully.
My problem is when I run the second call, I don’t get any data in return, and I don’t understand what’s the issue, I can see the URL is concatenated correctly because if I copy the URL from the log and I test it on another apps script or on Postman, it works perfectly fine!
Could someone help me in case I am doing something wrong?
Here’s the code:
function callEvents() {
var API_KEY = "xxx";
var data = { 'Start Date': '2021-05-03', 'End Date': '2021-06-03' }
var options = {
'method' : 'post',
'contentType': 'application/json',
'payload' : JSON.stringify(data) };
//This is the first call
var urlEncoded = encodeURI('https://website/api/dataviewresult/
etc/json/?api_key=' + API_KEY);
var url = UrlFetchApp.fetch(urlEncoded, options);
var result = JSON.parse(url.getContentText());
Logger.log(url.getContentText());
//here I retrieve the ID to use in the second call
var ipdataview = (result["contents"]["id"]);
Logger.log(ipdataview);
//here is the concatenation and the second call
var urlEncoded2 = encodeURI('https://website/api/dataviewresult/etc/json/'+ipdataview+'/?api_key=' + API_KEY);
Logger.log(urlEncoded2);
var response = UrlFetchApp.fetch(urlEncoded2);
Logger.log(response.getContentText());
I found out the solution, I was doing the second call (Get) too soon from the first one, so the data was not being picked up.
I used utilities.sleep() for about 2 seconds and it worked perfectly.
Thank you for everyone helping!
S

jquery.submit function with event

We are writing a form with google recaptcha v3. The form needs to get the token before actually submitted. A colleague wrote this code and it works, the form submits without any problem. But I'm confused on why it would work? Why isn't it caught in an infinite loop when .submit() function is being called recursively?
jQuery.fn.extend({
grecaptcha: function (options) {
this.submit(function (e) {
e.preventDefault();
var key = options["recaptcha_site_key"];
var acdata = options["action_data"];
var ele = this;
grecaptcha.execute(key, { action: acdata }).then(function (token) {
$("<input>")
.attr({
type: "hidden",
name: "g-recaptcha-response",
value: token,
}).appendTo($(ele));
ele.submit();
});
});
},
});
$("#formID").grecaptcha(option);
Are there any other better approaches to request a token before submitting?
Per :
https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/submit
This method is similar, but not identical to, activating a form's submit . When invoking this method directly, however:
No submit event is raised. In particular, the form's onsubmit event handler is not run.
Your code sample is not calling the jQuery method to trigger a submit event on the form. That would, in fact, result in a loop. Try wrapping the ele variable jQuery. $(ele).submit () should result in a loop. By not wrapping the reference to this (e.currentTarget) in a jQuery object, and instead calling the DOM submit function, you are submitting the form without triggering an event or running the handler.
Makes sense?

How to have One URL which sends to one-of-three Google Forms URLs?

I have a need to get equal populations in each of three surveys. The three surveys are identical except for one change - it contains different pictures.
I would like to distribute a single URL to my survey respondents.
I would like to count the number of previous responses I have, and add one.
I would like to redirect the session to one of three (Google Forms) URLs based upon the calculation
(Responses.Count + 1) MOD 3.
I think I need a Google Apps script to do this?
Here is some pseudocode:
var form0 = FormApp.openByUrl(
'htttps://docs.google.com/forms/d/e/2342f23f1mg/viewform'
);
var form1 = FormApp.openByUrl(
'htttps://docs.google.com/forms/d/e/23422333g/viewform'
);
var form2 = FormApp.openByUrl(
'htttps://docs.google.com/forms/d/e/2342wfeijqeovig/viewform'
);
var form0Responses = form0.getResponses();
var form1Responses = form1.getResponses();
var form2Responses = form2.getResponses();
var whichURL = (
form0Responses.length +
form1Responses.length +
form2Responses.length + 1
) % 3; // modulo three
// var goToForm = switch ( whichURL ) blah blah;
// redirect to goToForm;
// How do I redirect now?
Thanks!
Maybe there's a simpler solution possible but I don't think I know of it :)
The common link that you give out could be a link to a "proxy" page that doesn't contain anything but just redirects users to the correct page. Or it could be a link to the actual page with a necessary form embedded. Let's look at the options.
0) Publish your code as web app
In either case you'll need to have your code published as a web app. In GAS it's way simpler than it sounds, you'll find all the info here: https://developers.google.com/apps-script/guides/web
Make sure you set that Anyone, even anonymous can access the app and it always runs as you (if you choose User accessing the app, they'll have to go through authentication process which is not what you need here).
1) Redirect via GAS web app.
Your code in this case would look like so:
// I changed your function a bit so that it could be easier to work with
// in case the number of your forms changes later on
function getForm() {
var forms = [
{
// I'm using openById instead of openByUrl 'cause I've run into issues with
// the latter
form: FormApp.openById('1')
},
{
form: FormApp.openById('2')
},
{
form: FormApp.openById('3')
}
];
var whichURL = 0;
for (var i in forms) {
forms[i].responses = forms[i].form.getResponses().length;
whichURL += forms[i].responses;
}
whichURL++;
// we're returning the actual URL to which we should redirect the visitors
return forms[whichURL % forms.length].form.getPublishedUrl();
}
// doGet is Google's reserved name for functions that
// take care of http get requests to your web app
function doGet() {
// we're creating an html template from which getForm function is called
// as the template is evaluated, the returned result
// of the function is inserted into it
// window.open function is a client-side function
// that will open the URL passed to it as attribute
return HtmlService.createTemplate('<script>window.open("<?= getForm() ?>", "_top");</script>').evaluate();
}
So, after you've published your app, you'll get the link opening which the doGet function will run — and you're going to be redirected to your form.
The thing here is that the URL that you're getting this way is not rather beautiful, sth like https://script.google.com/macros/s/1234567890abcdefghijklmnopqrstuvwxyz/exec and it will also show a message at the top of the page "The app wasn't developed by Google" during those 1-2 seconds before redirect happens.
2) Embed your form into another webpage
The idea here is different: instead of giving your users a "proxy" link, you'll provide them with a page that'll ask a Google script for a correct form link and will display that form in the page in an iframe.
So, there are a couple of steps:
2.1) Change your doGet function (getForm will stay the same):
function doGet() {
return ContentService.createTextOutput(getForm());
}
In this case doGet will not return an html to render by browser but just a link to your form.
Sidenote: after changing the code you'll need to publish a new version of your code for the changes to take effect.
2.2) Create a Google site at sites.google.com
2.3) Insert an "Embed" block into your page, with the following code:
<script>
function reqListener(response) {
// change height and width as needed
document.body.insertAdjacentHTML('beforeend', '<iframe src="' + response.target.response + '" width="400" height="400"></iframe>');
}
var oReq = new XMLHttpRequest();
oReq.addEventListener("load", reqListener);
oReq.open("GET", "INSERT-YOUR-SCRIPT-URL");
oReq.send();
</script>
What it does: javascript code sends an http request to your script, gets the form URL and passes it on into the callback function, reqListener which in turn inserts it into document body within an iframe element.
The good thing is that your URL which will be much more user-friendly (you could use this approach on your own site, too).
As a result, you'll have sth like this:

Dynamically Fill in form from drop down selection

I have the Following Form:
How do i do the following:
When I chose a user from the select user drop down menu, After selecting the user I want to dynamically fill in the data below ? How do I do this without refreshing the page or loading another page?
I am using ASP.NET MVC
I would have to get the User ID from the Select User and then get the appropriate roles from the model
You could use AJAX. Subscribe to the .change event of the DropDown, retrieve the selected value, perform an AJAX call to a controller action sending the selected value which will return as JSON the corresponding list. Then in the success callback of this AJAX call add the necessary information to the lists.
Something along the lines of:
$(function() {
$('#id_of_your_users_ddl').change(function() {
var selectedValue = $(this).val();
var url = $(this).data('url'); // this assumes that you have appended a data-url attribute to your dropdown
$.post(url, { userId: selectedValue }, function(result) {
// result will be a JSON list returned by your controller action
// that you could use here to update your roles lists
});
});
});

How to access body of post request in app script using doPost(request)

I need to access the raw body of a post request in Google App Script. I see there is something like
function doPost(request) {
request.contentLength
}
that actually returns the right length of the raw content of the request body. So I thought there must be something to get the complete body, e.g. as a String.
I am not looking to access form field parameters that might be transferred using post.
I know this question is old, but a lot of things have changed and Google has made this available now. You can easily access the body of a POST request from doPost(e) using:
e.postData.contents
If you want to parse incoming JSON, use this:
JSON.parse(e.postData.contents)
From the Release Notes of May 9, 2013, use request.postData.getDataAsString():
function doPost(request) {
var jsonString = request.postData.getDataAsString();
var jsonData = JSON.parse(jsonString);
sheet.appendRow([ 'Data1:' , jsonData.Data1 ]); // Just an example
}
You can access the raw body of a post by creating a Blob from the parameter. You can then call various methods on the Blob object such as getBytes() or getDataAsString(). The functions are listed here.
function doPost(event) {
var contentBlob = event.parameter.thefile;
var contentAsString = contentBlob.getDataAsString();
var bytes = contentBlob.getBytes();
// do something with the contents ...
}
I don't think it is currently possible to access the POST body, say to implement a REST service. I submitted an enhancement request for this; if you stumbled upon this one, feel free to star it to vote on that issue.
This should also work.
function doPost(e) {
if(typeof e !== 'undefined')
Logger.log(e.parameter);
}
The simplest way to find out all that you have access to is to print out the request object.
function doPost(request) {
Logger.log(request);
}
And then do a POST to your script's URL to see the log content
Depending on the nature of what you are trying do and what control you have over the origination of the post request, you may be able to integrate with the Drive API. If the post data can be sent to the user's Drive account as a file (containing raw JSON data for instance), your script could then locate that file, load it's content, do whatever you need with it (put it in a spreadsheet for example), and optionally trash the Drive file. This assumes, of course, that you have control over how the post request is sent. A workflow might involve:
Client sends post request with data to user's Drive account (tag or name file to be easily identified later)
Upon completion of Drive File creation, client sends a request to your script with parameter "method=consumeData" or such
Your script checks the user's Drive account using the DocList service and retrieves any files uploaded for your script
I haven't tried this yet, but should work!
This should work.
const doPost = (request) => {
const { postData: { contents, type } = {} } = request;
if (type === 'application/json') {
//...
}
else {
// console.error("Data is well not formatted. Check if that's JSON")
}
return ContentService.createTextOutput(JSON.stringify({ id: 1 })).setMimeType(ContentService.MimeType.JSON);
};