I am working on a project for a Chrome Extension I am creating where it uses Google Sheets as a "database". I have the create and get part down. However, I am struggling with the update part. Here is my code so far:
let init = {
method: 'GET',
async: true,
headers: {
Authorization: 'Bearer ' + token,
'Content-Type': 'application/json'
},
'contentType': 'json'
};
fetch(
"https://sheets.googleapis.com/v4/spreadsheets/{spreadsheet_ID}/values/All Escalations!A5:Z10000001",
init)
.then((data) => {
return data.json();
}).then ((completedata) => {
const numRows = completedata.values ? completedata.values.length: 0
console.log(`${numRows} rows retrieved`)
let source = completedata.values;
const input = source.filter(function (row, index) {
row.unshift(index);
return row;
}).filter(function (iRow) {
return iRow[1] === arcaseiddis;
});
var index = (input[1]) ; //Saves the old index
let arjira2 = document.getElementById('jiracard').value
let ardatesubmitteddis2 = document.getElementById('datesubmitted').value
let arsubmittedbydis2 = document.getElementById('submittedbyaredit').value
let arclientiddis2 = document.getElementById('clientidaredit').value
let arcasenumberdis2 = document.getElementById('casenoaredit').value
let arnotesdis2 = document.getElementById('notesaredit').value
let arstatusdis2 = document.getElementById('statusaredit').value
let arissuedis2 = document.getElementById('casedesaredit').value
let arassignedtodis2 = document.getElementById('assignedtoaredit').value
let datearcompleted = document.getElementById('datecompletearedit').value
let arcaseiddis2 = e.target.parentElement.dataset.id
input[0]= arcaseiddis2; //Update the row with stuff
input[1] = arcasenumberdis2;
input[2]= arjira2;
input[3]= arstatusdis2;
input[4]= arissuedis2;
input[5]= arclientiddis2;
input[6]= ardatesubmitteddis2;
input[7]= arsubmittedbydis2;
input[8]= arassignedtodis2
input[9]= datearcompleted
input[10]= arnotesdis2
let values = [
[
input[0],
input[1],
input[2],
input[3],
input[4],
input[5],
input[6],
input[7],
input[8],
input[9],
input[10]
]
];
const resource = {
values
};
console.log(values)
I am able to console.log this out and it actually shows the updates I put in. However, when I run the update function it gives the following error:
{range: "All Escalations!Aundefined:Jundefined", values: {values: [,…]}}
range
:
"All Escalations!Aundefined:Jundefined"
values
:
{values: [,…]}
values
:
[,…]
0
:
["82389566743686", "4306203", "None", "SUBMITTED", "Testing", "Client", "2/8/2023", "John SMith",…]
0
:
"82389566743686"
1
:
"4306203"
2
:
"None"
3
:
"SUBMITTED"
4
:
"Testing"
5
:
"Client"
6
:
"2/8/2023"
7
:
"John Smith"
8
:
"Carey Jones"
9
:
""
10
:
"No Notes Yet"
This is the update function I am running:
var payload = {
"range": "All Escalations!A" + index + ":J" + index,
"values": resource
}
let init = {
method: 'PUT',
async: true,
headers: {
Authorization: 'Bearer ' + token,
},
body: JSON.stringify(payload)
};
fetch(
"https://sheets.googleapis.com/v4/spreadsheets/{SpreadsheetID}/values/All Escalations/?valueInputOption=USER_ENTERED",
init)
})
The code I've used so far is mainly coming from this article: google sheets api v4 update row where id equals value
Because this is a Chrome Extension I can't connect to the api via the URL like we used to in MV2. So I do it via the "fetch" method and it works for the get part. From looking over the above article, it looks like the person does the "get" to get that specific row. Then updates the specific row based on the ID. I appreciate any help I can get.
With some tinkering around I found the answer:
I credit Take A Minute with getting me in the right direction with his snippet. google sheets api v4 update row where id equals value. I am just starting out at this so any input on how to make this more efficient is welcome.
function updatearescalation(){
chrome.identity.getAuthToken({ 'interactive': true }, getToken);
function getToken(token) { // this seems to be the best way to get the token to be able to make these requests.
let initdarread = {
method: 'GET',
async: true,
headers: {
Authorization: 'Bearer ' + token,
'Content-Type': 'application/json'
},
'contentType': 'json'
};
fetch(
"https://sheets.googleapis.com/v4/spreadsheets/1uXGfr5OvJjW2Pb9YhkbLHED3cQcJI5YsvJKLedAHRKY/values/All Escalations!A5:Z10000001",
initdarread)
.then((data) => {
return data.json();
}).then ((completedata) => {
const numRows = completedata.data ? completedata.data.length: 0
console.log(`${numRows} rows retrieved`)
source = completedata.values;
const input = source.filter(function (row, index) {// when I use the source.map it returns a random row instead of the specific row I want.
row.unshift(index);
return row;
}).filter(function (iRow) {
return iRow[1] === arcaseiddis;
});
var index = parseInt(input[0]) + 5; //Saves the old index The +5 indicates where the actual data starts. This is important.
input[0].shift(); //According to post previously mentioned this removes the old index.
input[0] // This is where you update the rows with what you want.
let values = [
input[0], // This is where all the values you want updated go.
];
const resource = {
values
};
var payload = {
"range": "All Escalations!A" + index + ":K" ,
"values": resource
}
let range = "All Escalations!A" + index + ":K"
let initarupdate = {
method: 'PUT',
async: true,
headers: {
Authorization: 'Bearer ' + token,
},
body: JSON.stringify(payload)
};
fetch(
`https://sheets.googleapis.com/v4/spreadsheets/1uXGfr5OvJjW2Pb9YhkbLHED3cQcJI5YsvJKLedAHRKY/values/${range}/?valueInputOption=USER_ENTERED`,
initarupdate)
}) // I used a literal here to be able to use the Range I put in the payload.
}
}
Related
I am trying to work with an API (that provides data about movies in the media centers of german public broadcasting) but I can't get a single useful response.
I got an example code in javascript but since I dont know anything about that language I try to understand as much as I can and write an equivalent python program. Can someone tell me my mistake? The code should return a list of movies along with their duration and some similar information.
The original goes like:
`
const query = {
queries: [
{
fields: ['title', 'topic'],
query: 'sturm der liebe'
},
{
fields: ['channel'],
query: 'ard'
}
],
sortBy: 'timestamp',
sortOrder: 'desc',
future: false,
offset: 0,
size: 10,
duration_min: 20,
duration_max: 100
};
const queryString = JSON.stringify(query); // I think this turns query into a json file
const request = new XMLHttpRequest(); // here I start to loose the understanding
const requestURL = 'https://mediathekviewweb.de/api/query';
request.open("POST", requestURL);
request.addEventListener('load', function (event) {
let response;
try {
response = JSON.parse(request.responseText);
} catch (e) { }
if (request.status == 200 && typeof response != 'undefined') {
for (let i = 0; i < response.result.results.length; i++) {
let entry = response.result.results[i];
let row = $('<tr>');
row.append($('<td>').text(entry.channel));
row.append($('<td>').text(entry.topic));
row.append($('<td>').text(entry.title));
row.append($('<td>').text(entry.description));
row.append($('<td>').text(entry.url_video));
$('#mediathek > tbody').append(row);
}
$('#responseText').text(JSON.stringify(response, null, 2));
} else {
if (response) {
console.log(response.err);
$('#errorText').html(response.err.join('</br>'));
}
else {
$('#errorText').html(request.statusText + '</br>' + request.responseText);
}
}
});
request.send(queryString);
`
I started using requests:
`
import requests
import json
url = "https://mediathekviewweb.de/api/query"
answer = requests.post(url, json=queryString)
print(answer)
print(json.loads(answer.content))
`
Already in this minimal example answer returns "<Response [400]>" and the content is "{'result': None, 'err': ['Unexpected token o in JSON at position 1']}"
Can someone help me out? I didn't give a json, where does this error come from?
I get the same response when I try
`
import requests
import json
url = "https://mediathekviewweb.de/api/query"
query = {
"queries": [], // I want to search through all movies so this is left clear
"sortBy": "timestamp",
"sortOrder": "desc",
"future": False,
"offset": 0,
"size": 1000,
"duration_min": 90,
"content-type": "text/plain"
}
queryString = json.dumps(query) # hopefully this is equivalent to JSON.stringify()
answer = requests.post(url, json=queryString)
print(answer)
print(json.loads(answer.content))
`
I actually think this extra code isn't even used by the program.
I don't know if thats the right direction, but I think the javascript programm first opens a request and then in an other step inputs the data. I can't do that with the requests package as far as I know.
Don't use XMLHttpRequest. This adds to the confusion.
Here is the fetch option:
const query = {
queries: [
{
fields: ['title', 'topic'],
query: 'sturm der liebe',
},
{
fields: ['channel'],
query: 'ard',
},
],
sortBy: 'timestamp',
sortOrder: 'desc',
future: false,
offset: 0,
size: 10,
duration_min: 20,
duration_max: 100,
};
const requestURL = 'https://mediathekviewweb.de/api/query';
fetch(requestURL, { method: 'POST', body: JSON.stringify(query) })
.then((res) => res.json())
.then((res) => {
// success request logic
console.log('response', res);
})
.catch((error) => {
// error handling
console.error(error);
});
Using this node-ews package, I can send email, but I haven't been able to find a good example of how to read mails from the Inbox folder and get the email's text and attachments.
I've read the Microsoft documentation, such as this one: https://learn.microsoft.com/en-us/exchange/client-developer/exchange-web-services/how-to-work-with-exchange-mailbox-items-by-using-ews-in-exchange#get-an-item-by-using-the-ews-managed-api, but the examples are in C#, C++, or VB.
However, I would like to use Nodejs for this.
**I have found a best way extract every content using mailparser. see bellow
// At first Read emails from Inbox
const EWS = require('node-ews');
const simpleParser = require('mailparser').simpleParser;
// exchange server connection info
const ewsConfig = {
username: 'username',
password: 'password',
host: 'hostname'
};
const options = {
rejectUnauthorized: false,
strictSSL: false
};
// initialize node-ews
const ews = new EWS(ewsConfig, options);
var ewsFunction = 'FindItem';
var ewsArgs = {
'attributes': {
'Traversal': 'Shallow'
},
'ItemShape': {
't:BaseShape': 'Default'
},
'ParentFolderIds' : {
'DistinguishedFolderId': {
'attributes': {
'Id': 'inbox'
}
}
}
};
// Itreate over all the emails and store Id and ChangeKey.
ews.run(ewsFunction, ewsArgs, ewsSoapHeader)
.then(result => {
// Iterate over the result and extract Id and ChangeKey of the messages and pass those to GetItem function to read messages
})
// For reading individual messages returned by FindItem (using Id and ChangeKey)
var ewsFunction = 'GetItem';
var ewsArgs = {
'ItemShape': {
'BaseShape': 'Default',
'AdditionalProperties': {
'FieldURI': [
{ 'attributes': { 'FieldURI': 'item:MimeContent'}}
]
}
},
'ItemIds': {
'ItemId': {
'attributes': {
'Id': Id,
'ChangeKey': ChangeKey
}
}
}
};
await ews.run(ewsFunction, ewsArgs, ewsSoapHeader)
.then(result => {
// Iterate over the result and extract meesage
const {Message} = result.ResponseMessages.GetItemResponseMessage.Items
let mimeContent = Buffer.from(Message.MimeContent['$value'], 'base64').toString('binary');// decode mime content
simpleParser(mimeContent).then(async function (mail) {
console.log("mail")
console.log(mail.attachments)
console.log(mail.headers.get('message-id'))
console.log(mail.headers.get('references'))
console.log(mail.headers.get('in-reply-to'))
console.log({
// text: mail.text,
// html: mail.html ? mail.html.replace(/<meta([^>]+)>/g, "") : "",
from: (mail.from) ? mail.from.value.map(item => item.address) : [],
to: (mail.to) ? mail.to.value.map(item => item.address) : [],
cc: (mail.cc) ? mail.cc.value.map(item => item.address) : [],
bcc: (mail.bcc) ? mail.bcc.value.map(item => item.address) : [],
messageId: mail.messageId,
subject: mail.subject
})
}).catch((err) => {
console.log("err")
console.log(err)
})
})
Here you will get the full parsed mail contents with attachments. Happy Coding!!!
I have following Table / Array:
If I press the blue button, then all items with the same group as the record should change the Status (Gratis).
But now it just change the Value of the Record and all items above it. As an example, if I press the Button on Record No. 1 then itselft and all above (No. 0) get an change of the Status (Gratis).
Following code im using to go through the array and change the Status:
private _updateFreeStatus = (record: QuestionModel): void => {
fetch('api/Test/UpdateGratisStatus', {
headers: { 'Content-Type': 'application/json' },
method: 'PUT',
body: JSON.stringify({
'group': record.group,
'free': record.free,
})
});
this.state.question.map(item => {
if (item.group === record.group)
{
item.free = !record.free;
}
});
}
do not mutate the state
create a copy, and use setState
Use
const updatedQuestions = this.state.question.map(item => {
if (item.group === record.group) {
return {
...item,
free: !record.free
}
}
return item;
});
this.setState({question: updatedQuestions});
I created an array from a http response, and am attempting to iterate against an object list to create a second array of keys where the keys value is found in my http response array. But I noticed for some reason using find or includes, the if statement never evaluates to true. But if I replace my http array with an array of the exact same contents, it evaluates to true.
for (const property in partners) {
cy.log('PARTNER SKU LIST', partnerSkuList);
if (partnerSkuList.find((elem) => elem === partners[property])) {
cy.log('TRUE');
partnerNames.push(property);
}
}
cy.log('NAMES LIST', partnerNames);
Log output:
PARTNER SKU LIST, [3]
NAMES LIST, []
Defining variable as array with same value:
for (const property in partners) {
partnerSkuList = [3];
if (partnerSkuList.find((elem) => elem === partners[property])) {
cy.log('TRUE');
partnerNames.push(property);
}
}
cy.log('NAMES LIST', partnerNames);
Log output:
TRUE
NAMES LIST, [abc]
UPDATE:
Did some checking and my array is empty:
Given('the principal catalog identifier setting is set to partner_sku', () => {
// Get a list of item checkin methods for all partners
const partnerSkuList = [];
const partnerNames = [];
function partnerSettingsRequest(code, response) {
cy.request({
method: 'POST',
url: Cypress.env('adminApiUrl'),
headers: {
Authorization: `Bearer ${token}`,
'Content-type': 'application/json',
'x-company-app-name': 'admin:tests-ui',
},
body: {
query:
`query getSettingsByCode($code: String!) {
configurationByCode(code: $code) {
data {
value
partnerId
}
}
}`,
variables: {
code,
},
},
}).then(response);
}
const partnerSkuResponse = (res) => {
for (let i = 0; i < res.body.data.configurationByCode.length; i += 1) {
if (res.body.data.configurationByCode[i].data.value !== null
&& res.body.data.configurationByCode[i].data.partnerId !== null
&& res.body.data.configurationByCode[i].data.value.includes('partner_sku')
&& Object.values(partners).includes(res.body.data.configurationByCode[i].data.partnerId)) {
partnerSkuList.push(res.body.data.configurationByCode[i].data.partnerId);
}
}
return partnerSkuList;
};
partnerSettingsRequest('checkin_cid1', partnerSkuResponse);
for (const property in partners) {
if (partnerSkuList.length === 0) {
cy.log('ARRAY EMPTY');
}
cy.log('PARTNER SKU LIST', partnerSkuList);
if (partnerSkuList.includes(partners[property])) {
cy.log('ADDING PROPERTY');
partnerNames.push(property);
}
}
cy.log('NAMES LIST', partnerNames);
This was an async issue, just needed to wrap my code in a promise. So added an extra arg to my request function filter, theennn(pun intended):
}).then(response).then(filter);
const getPartnerNames = () => {
for (const property in partners) {
cy.log('PARTNER SKU LIST', partnerSkuList);
if (partnerSkuList.includes(partners[property])) {
cy.log('ADDING PROPERTY');
partnerNames.push(property);
}
}
};
partnerSettingsRequest('checkin_cid1', partnerSkuResponse, getPartnerNames);
I'm calling the Twitch API (should mention I'm doing this in React) to get information on a few channels. I'm getting the data back, but it gets added to the array in the wrong order, different on every reload. Since I have two make two different calls, this ends up leaving me with mismatched information. I'm assuming it's because some calls take longer than other, and array.map() is running regardless if the first call was done yet, I'm just not sure how to fix that.
Here is my script:
export default class TwitchApp extends React.Component {
state = {
games: [],
statuses: [],
names: [],
logos: [],
streams: ["nl_kripp", "ESL_SC2", "Day9tv",
"DisguisedToastHS" ]
};
findStreams = () => {
const channels = this.state.streams;
const statusUrls = channels.map((channel) => {
return 'https://api.twitch.tv/kraken/streams/' + channel;
})
const infoUrls = channels.map((channel) => {
return 'https://api.twitch.tv/kraken/channels/' + channel;
})
statusUrls.map((statusUrl)=> {
let url = statusUrl;
return $.ajax({
type: 'GET',
url: url,
headers: {
'Client-ID': 'rss7alkw8ebydtzisbdbnbhx15wn5a'
},
success: function(data) {
let game;
let status = data.stream != null ? "Offline" : "Online";
this.setState((prevState)=> ( { statuses: prevState.statuses.concat([status]) } ) );
status = '';
}.bind(this)
});
});
infoUrls.map((url)=> {
return $.ajax({
type: 'GET',
url: url,
headers: {
'Client-ID': 'rss7alkw8ebydtzisbdbnbhx15wn5a'
},
success: function(data) {
let name = data.display_name != null ? data.display_name : 'Error: Can\'t find channel';
let logo = data.logo != null ? data.logo : "https://dummyimage.com/50x50/ecf0e7/5c5457.jpg&text=0x3F";
let game = data.game != null ? data.game : "Offline";
//let status = data.status != null ? data.status: "Offline";
this.setState((prevState)=> ( { games: prevState.games.concat([game]), names: prevState.names.concat([name]), logos: prevState.logos.concat([logo]) } ) );
game = '';
logo = '';
name = '';
}.bind(this)
});
});
};
You have really many options here...
could either look into AsyncJs and it is the async.series(); you are looking for, that will make all call go into specific order.
You could also go for a promised based HTTP requester, like Axios in which you can chain all your requests.
But really, I would go with the 3rd option which is that you make an array of objects as your state like this:
state = {
info: {
"nl_kripp": {
game: '',
status: '',
name: '',
logo: '',
},
"ESL_SC2": {
game: '',
status: '',
name: '',
logo: '',
},
"Day9tv": {
game: '',
status: '',
name: '',
logo: '',
},
"DisguisedToastHS": {
game: '',
status: '',
name: '',
logo: '',
}
},
streams: ["nl_kripp", "ESL_SC2", "Day9tv", "DisguisedToastHS"]
};
and do something like this:
var streams = this.state.streams;
var fetchedArray = [];
fetchedArray.map(streams , stream => {
let url = 'https://api.twitch.tv/kraken/streams/' + stream;
return $.ajax({
type: 'GET',
url: url,
headers: {
'Client-ID': 'rss7alkw8ebydtzisbdbnbhx15wn5a'
},
success: function(data) {
var currentState = Object.assign({}, this.state.info[stream]);
currentState.status = data.stream === null ? 'Offline' : 'Online';
currentState.name = data.display_name;
currentState.logo = data.channel.logo;
currentState.game = data.game;
}.bind(this)
});
});
Now fetchedArray will hold the same information but stacked together and easily handle with javascript after, rather than unsorted arrays.