Problem sending POST body to the Firestore REST API - firebase

I want to create a new document in Firestore using the REST API.
Very good examples here using Axios to send the POST request with some fields:
https://www.jeansnyman.com/posts/google-firestore-rest-api-examples/
axios.post(
"https://firestore.googleapis.com/v1/projects/<PROJECTIDHERE>/databases/(default)/documents/<COLLECTIONNAME>",
{
fields: {
title: { stringValue: this.title },
category: { stringValue: this.category },
post: { stringValue: this.post },
summary: { stringValue: this.description },
published: { booleanValue: this.published },
created: { timestampValue: new Date() },
modified: { timestampValue: new Date() }
}
}
).then(res => { console.log("Post created") })
And an example here using Python Requests:
Using the Firestore REST API to Update a Document Field
(this is a PATCH request but the field formatting is the same as in a POST request)
import requests
import json
endpoint = "https://firestore.googleapis.com/v1/projects/[PROJECT_ID]/databases/(default)/documents/[COLLECTION]/[DOCUMENT_ID]?currentDocument.exists=true&updateMask.fieldPaths=[FIELD_1]"
body = {
"fields" : {
"[FIELD_1]" : {
"stringValue" : "random new value"
}
}
}
data = json.dumps(body)
headers = {"Authorization": "Bearer [AUTH_TOKEN]"}
print(requests.patch(endpoint, data=data, headers=headers).json())
I am using Google Apps Script UrlFetchApp.fetch to send my requests. I am able to use GET requests with no problems. For example, to get all the documents in a collection (in Google Apps Script):
function firestore_get_documents(){
var options = {
headers: { 'Authorization': 'Bearer ' + ScriptApp.getOAuthToken() },
method:'GET'
}
var response = UrlFetchApp.fetch('https://firestore.googleapis.com/v1/projects/<PROJECTIDHERE>/databases/(default)/documents/myCollection', options);
var parsed = JSON.parse(response.getContentText());
return parsed;
}
This works nicely. And changing 'method' to 'POST' creates a new document in myCollection as expected. Then I try to add a POST body with some fields (or just one field):
function firestore_create_new_document(){
var options = {
headers: { 'Authorization': 'Bearer ' + ScriptApp.getOAuthToken() },
method:'POST',
payload: {fields: { title: { stringValue: 'newTitle' } } }, // If you comment out this line, it works as expected
muteHttpExceptions:true
}
var response = UrlFetchApp.fetch('https://firestore.googleapis.com/v1/projects/<PROJECTIDHERE>/databases/(default)/documents/myCollection', options);
var contentText = response.getContentText();
var parsed = JSON.parse(response.getContentText());
return parsed;
}
I get the following errors:
code: 400 message: "Request contains an invalid argument."
status: "INVALID_ARGUMENT"
details[0][#type]: "type.googleapis.com/google.rpc.BadRequest"
details[0][fieldViolations][0][field]: "{title={stringValue=newTitle}}"
details[0][fieldViolations][0][description]: "Error expanding 'fields' parameter. Cannot find matching fields for path '{title={stringValue=newTitle}}'."
Documentation is available here:
https://firebase.google.com/docs/firestore/reference/rest/v1/projects.databases.documents/createDocument
https://firebase.google.com/docs/firestore/reference/rest/v1/projects.databases.documents#Document
The problem may be the formatting of my 'fields' object - I've tried several different formats from the documentation and examples
The problem may be that the fields don't exist yet? I think I should be able to create a new document with new fields
The problem may be with the way UrlFetchApp.fetch sends my JSON body. I have tried using payload = JSON.stringify(payload_object) and that doesn't work either.
I think UrlFetchApp is doing something slightly different than Axios or Python Requests - the body is getting sent differently, and not parsing as expected.

How about the following modification?
From:
var options = {
headers: { 'Authorization': 'Bearer ' + ScriptApp.getOAuthToken() },
method:'POST',
payload: {fields: { title: { stringValue: 'newTitle' } } }, // If you comment out this line, it works as expected
muteHttpExceptions:true
}
To:
var options = {
headers: { 'Authorization': 'Bearer ' + ScriptApp.getOAuthToken() },
method:'POST',
payload: JSON.stringify({fields: { title: { stringValue: 'newTitle' } } }),
contentType: "application/json",
muteHttpExceptions:true
}
When I tested above modified request, I could confirm that it worked. But if other error occurs, please tell me.
Reference:
Class UrlFetchApp

Related

How do I change the JSON stream I get back from Fetch using Deno?

I am new to Deno and have the following simple code...
const getCOP = async()=>{
const resp = await fetch("....", {
headers: {
accept: "application/json",
apiKey
},
});
return await resp.body;
}
let resp = {}
return new Response(resp.body, {
status: resp.status,
headers: {
"content-type": "application/json",
},
});
resp.body = await getCOP();
resp.status = 200;
It returns
{
"success": true,
"timestamp": 1675621083,
"base": "COP",
"date": "2023-02-05",
"rates": {
"EUR": 0.000199,
"GBP": 0.000179,
"USD": 0.000216
}
}
What I would like to do would be the equivalent of this in normal JS...
return {
rate : resp.body.rates,
copPerDollar : 1 / resp.body.rates.USD
}
Of course this doesn't seem to work because instead of an actual json obj it is a readable stream. How do I transform this stream into a json object and then restream it to the body of the sent request?

Run Azure Notebook By API (external)

I am trying to run the notebook from node, everything is working fine except the parameters are not accepted by the notebook instead it is sending the output based on default params. I am not getting where I am doing wrong.
Below is my call:
var job_payload = {
"run_name": runName,
"existing_cluster_id": 'cluster_id',
"notebook_task":
{
"notebook_path": notebookPath
},
"notebook_params": notebook_params //{'x':1,'y':2}
}
var url = "https://<location>.<azr_databricks>.net/api/2.0/jobs/runs/submit";
var options = {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer token'
},
body: JSON.stringify(job_payload),
};
My notebook:
import json
dbutils.widgets.text("x", '3', "firstParam")
dbutils.widgets.text("y", '4', "secondParam")
x=int(dbutils.widgets.get("x"))
y=int(dbutils.widgets.get("y"))
sum=x+y
class Output:
def __init__(self, val):
self.resultTest2 = val
p1 = Output(sum)
print(p1.resultTest2)
result=json.dumps(p1.__dict__)
#RETURNING THE OUTPUT
dbutils.notebook.exit(result)
I am sending x:1 and y:2 as param but instead of getting output 3 I am getting 7 which is default value.
As I am not getting much help from the documentation, please help:
Document URL: Microsoft link
I got the answer that where I was wrong from the below link :
StackOverflow Link
the job_payload will look like below:
var job_payload = {
"run_name": runName,
"existing_cluster_id": 'cluster_id',
"notebook_task":
{
"notebook_path": notebookPath,
"base_parameters":notebook_params //{'x':1,'y':2}
},
}
var url = "https://<location>.<azr_databricks>.net/api/2.0/jobs/runs/submit";
var options = {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer token'
},
body: JSON.stringify(job_payload),
};
Now, it is working fine.

How to invoke iron ajax async after returning header in computed binding

Polymer 1.* and Firebase
How can do a async operation where this.$$('#ironAjax').generateRequest(); is call after return '{"Authorization": "Bearer ' + token + '"}';? I have to return the header value and then invoke the iron-ajax request.
<iron-ajax
url="[[HOST]][[LISTINGS]]?foo=foo"
id="ironAjax"
handle-as="json"
headers="[[setAuth()]]",
on-error="errorHandler"
loading="{{ironLoading}}"
last-response="{{listings}}"></iron-ajax>
pageActivated: function(activated) {
if (activated) {
this.setAuth();
}
},
setAuth: ()=> {
firebase.auth()
.onAuthStateChanged((token) => {
return '{"Authorization": "Bearer ' + token + '"}';
this.$$('#ironAjax').generateRequest(); // NEED TO CALL AFTER
// HEADER IS SET
})
I was able to handle the situation with a direct modification to the header. Hopefully this can help anyone else in the same situation:
Behavior:
<script>
AuthIronAjax = {
rawSetAuth: (user, ajax) => {
if (user) {
user.getIdToken().then((token) => {
ajax.headers['Authorization'] = 'Bearer ' + token;
ajax.generateRequest();
});
}
},
setAuth: function (ajax) {
firebase.auth()
.onAuthStateChanged((user)=> this.rawSetAuth(user, ajax));
}
};
</script>
Element it is used in. Note: iron-pages is being used with a implied host property binding activated:
observers: ['pageActivated(activated)'],
behaviors: [AuthIronAjax],
pageActivated: function(activated) {
if (activated) {
this.setAuth(this.$.ironAjax);
}
},

Angular2 - http call Code coverage

My components.ts is,
getHomePageData() : void{
this.homeservice.getHomePageData()
.subscribe(
data => {
//console.log("response status ################### "+data.status);
//console.log("getUserData response ************ \n"+JSON.stringify(data));
this.defaultFacilityId = data.response.defaultFacilityId;
this.defaultFacilityName = data.response.defaultFacilityName;
this.enterpriseId = data.response.enterpriseId;
this.enterpriseName = data.response.enterpriseName;
this.facilityList = data.response.facilityList;
this.userName = data.response.userName;
this.showDefaultPopoup();
},
error => {
console.error(error);
//this.errorMessage="Technical error - Contact Support team !" ;
}
);
}
So my component.spec.ts is ,
it('getHomePageData with SUCCESS - getHomePageData()', () => {
backend.connections.subscribe((connection: MockConnection) => {
//expect(connection.request.url).toEqual('http://localhost:8080/MSMTestWebApp/UDM/UdmService/Home/');
expect(connection.request.url).toEqual('http://192.168.61.158:9080/GetUserData');
expect(connection.request.method).toEqual(RequestMethod.Get);
expect(connection.request.headers.get('Content-Type')).toEqual('application/json');
let options = new ResponseOptions({
body:
{
"request": { "url": "/getUserData" },
"response": {
"defaultFacilityName":"3M Health Information Systems",
"enterpriseId":"11.0",
"enterpriseName":"HSA Enterprise",
"defaultFacilityId": "55303.0",
"userName":"Anand"
},
"error": ""
},
status : 200
});
connection.mockRespond(new Response(options));
});
backend.connections.subscribe((data) => {
//expect(data.response.facilityId).toEqual("55303.0");
//expect(subject.handleError).toHaveBeenCalled();
})
service.getHomePageData().subscribe((data) => {
//expect(videos.length).toBe(4);
expect(data.response.defaultFacilityId).toEqual("55303.0");
component.defaultFacilityId = data.response.defaultFacilityId;
component.defaultFacilityName = data.response.defaultFacilityName;
component.enterpriseId = data.response.enterpriseId;
component.enterpriseName = data.response.enterpriseName;
component.userName = data.response.userName;
console.log("$$$$$$$$$$$$$$$$**********$$$$$$$$$$$$$$$$$$$$$");
});
});
When i try to run test case. It got passed. But while I look into the code coverage, it doesn't cover the code shown in red below
Please help to get the full code coverage. Thanks.
In the test you've shown here you don't seem to be calling getHomePageData() from your component
Try building your test like this:
import { fakeAsync, tick } from '#angular/core/testing';
...
it('getHomePageData with SUCCESS - getHomePageData()', fakeAsync(() => {
backend.connections.subscribe((connection: MockConnection) => {
//expect(connection.request.url).toEqual('http://localhost:8080/MSMTestWebApp/UDM/UdmService/Home/');
expect(connection.request.url).toEqual('http://192.168.61.158:9080/GetUserData');
expect(connection.request.method).toEqual(RequestMethod.Get);
expect(connection.request.headers.get('Content-Type')).toEqual('application/json');
let options = new ResponseOptions({
body:
{
"request": { "url": "/getUserData" },
"response": {
"defaultFacilityName":"3M Health Information Systems",
"enterpriseId":"11.0",
"enterpriseName":"HSA Enterprise",
"defaultFacilityId": "55303.0",
"userName":"Anand"
},
"error": ""
},
status : 200
});
connection.mockRespond(new Response(options));
});
// If this function is not automatically called in the component initialisation
component.getHomePageData();
tick();
//you can call expects on your component's properties now
expect(component.defaultFacilityId).toEqual("55303.0");
});
FakeAsync allows you to write tests in a more linear style so you no longer have to subscribe to the service function to write your expectations.
In a FakeAsync test function you can call tick() after a call where an asynchronous operation takes place to simulate a passage of time and then continue with the flow of your code.
You can read more about this here: https://angular.io/docs/ts/latest/testing/#!#fake-async
EDIT - Error Case
To test the error logic you can call mockError or set up an error response using mockRespond on your connection:
it('getHomePageData with ERROR- getHomePageData()', fakeAsync(() => {
backend.connections.subscribe((connection: MockConnection) => {
if (connection.request.url === 'http://192.168.61.158:9080/GetUserData') {
// mockError option
connection.mockError(new Error('Some error'));
// mockRespond option
connection.mockRespond(new Response(new ResponseOptions({
status: 404,
statusText: 'URL not Found',
})));
}
component.getHomePageData();
tick();
//you can call expects now
expect(connection.request.url).toEqual('http://192.168.61.158:9080/GetUserData');
expect(connection.request.method).toEqual(RequestMethod.Get);
expect(connection.request.headers.get('Content-Type')).toEqual('application/json');
expect('you can test your error logic here');
});
What we're doing inside the subscription is making sure that anytime the GetUserData endpoint is called within this test method it will return an error.
Because we test errors and successes separately in the success test there's no need to add the error related settings in the request options.
Are you using JSON data? Then you should probably use map() before using .subscribe().
.map((res:Response) => res.json())
Try organizing your code like this:
ngOnInit() {
this.getHomePageData();
}
getHomePageData() {
this.http.get('your.json')
.map((res:Response) => res.json())
.subscribe(
data => {
this.YourData = data
},
err => console.error(err),
() => console.log('ok')
);
}
Hope it helps,
Cheers

how to get respose of data webscript in share webscript js file

I have one data webscript at alfresco side which return json response.
i want this json response in share webscript to display that json data on share.
following is the my code written in getLocation.get.js file # share.
var result1 = new Array();
var connector = remote.connect("alfresco");
var data = connector.get("/com/portfolio/ds/getlocation");
// create json object from data
if(data.status == 200){
var result = jsonUtils.toJSONString(eval(data.response));
model.docprop = result ;
}else{
model.docprop = "Failed";
}
Following is the output from alfresco side
{
"subgroups": [
{
"name": "grp_pf_india_user" ,
"label": "INDIA"
},
{
"name": "grp_pf_israil_user" ,
"label": "ISRAIL"
},
{
"name": "grp_pf_usa_user" ,
"label": "USA"
}
]
}
use this code to call repo webscripts from share side by using the concept or RMI. (Alfresco.constants.PROXY_URI) = (http://host:port/share/proxy/alfresco/)
var xurl=Alfresco.constants.PROXY_URI+"HR-webscripts/createHRDocument/"+JSON.stringify(o);
//alert(xurl);
var request = $.ajax({
url: xurl ,
type: "POST",
//data: { "groupname" : o},
beforeSend : function(xhr){
/*
Alfresco.util.Ajax & alfresco/core/CoreXhr – will automatically take the token from the cookie and add it as a request header for every request.
Alfresco.forms.Form – will automatically take the token from the cookie and add it as a url parameter to when submitting an multipart/form-data request.
(When submitting a form as JSON the Alfresco.util.Ajax will be used internally)
*/
if (Alfresco.util.CSRFPolicy && Alfresco.util.CSRFPolicy.isFilterEnabled()){
xhr.setRequestHeader(Alfresco.util.CSRFPolicy.getHeader(), Alfresco.util.CSRFPolicy.getToken() );
}
},
dataType: "html"
});
request.done(function(msg) {
//alert( "Request OK: " + msg );
$("#res").html( msg );
});
request.fail(function(jqXHR, textStatus) {
alert( "Request failed: " + textStatus );
});

Resources