I want to call multiple APIs and get the result of all the APIs in a single list.
I have thought for looping and then get the APIs call one by one but struct in between.
Can anyone help me in this because after getting the result in form of a list .I have to use that list to draw a pie chart.
Future<List<int>> status() async {
var data = new http.Client();
String basicAuth =
'Basic ' + base64Encode(utf8.encode('$username:$password'));
List<String> itemsIds = ['new', 'open', 'stalled', 'resolved'];
return Future.wait<>(['new', 'open', 'stalled', 'resolved'].map((item) =>
data.get(Uri.encodeFull("sampleapi'and(status='$item')/" + '/next'),headers:{'authorization': basicAuth}.then((response) {
})
)
);
}
Related
I am trying to add pagination to my Zapier trigger.
The API I am using for the trigger supports pagination, but not using a page number in the traditional sense (ie. page 1,2,3,...). Instead, the API response includes a key (ie. "q1w2e3r4") which should be passed as a parameter to the next request to get the next page of results.
From looking at the docs, I can use {{bundle.meta.page}} (which defaults to 0 unless otherwise set).
I am trying to set {{bundle.meta.page}} in the code editor, with an example shown below:
const options = {
url: 'company_xyz.com/api/widgets',
method: 'GET',
...,
params: {
...,
'pagination_key': bundle.meta.page,
}
}
return z.request(options)
.then((response) => {
response.throwForStatus();
const json_response = response.json;
widgets = json_response.widgets
...
bundle.meta.page = json_response["next_pagination_key"]
return widgets;
});
The problem is that when Zapier tries to retrieve the next page, bundle.meta.page will be 1 instead of the value of "next_pagination_key" from the result of the previous request.
There are docs on cursor-based pagination in the CLI docs.
The relevant block is:
const performWithAsync = async (z, bundle) => {
let cursor;
if (bundle.meta.page) {
cursor = await z.cursor.get(); // string | null
}
const response = await z.request(
'https://5ae7ad3547436a00143e104d.mockapi.io/api/recipes',
{
// if cursor is null, it's sent as an empty query
// param and should be ignored by the server
params: { cursor: cursor }
}
);
// we successfully got page 1, should store the cursor in case the user wants page 2
await z.cursor.set(response.nextPage);
return response.items;
};
This should work in the Zapier Visual Builder, but you might need to use the CLI instead. You can export your integration using the zapier convert CLI command (docs).
Google Sheets document can contain some sheets. First is default and '0'. Generally for any sheet there is address like this:
https://docs.google.com/spreadsheets/d/(spreadsheetId)/edit#gid=(sheetId)
with both spreadsheetId and sheetId.
But in API documentation there is no mention of how to use sheetId. I can only read and edit default sheet for given spreadsheetId.
If in request from code presented in exemplary link I added sheetId property I got error:
{
message: 'Invalid JSON payload received. Unknown name "sheetId": Cannot bind query parameter. Field \'sheetId\' could not be found in request message.',
domain: 'global',
reason: 'badRequest'
}
How to get access to other sheets than default in Google Sheets API and read or update fields in them?
Sheet name is the easiest way to access a specific sheet. As written here, range parameter can include sheet names like,
Sheet1!A1
If you must use a sheet id instead of sheet name, You can use any of the alternate end points which uses dataFilter, like spreadsheets.values.batchUpdateByDataFilter instead of spreadsheets.values.batchUpdate. You can then use sheetId in request body at data.dataFilter.gridRange.sheetId. An example of using such a filter with sheetId is provided by another answer here by ztrat4dkyle.
However, developer metadata is the preferred method of permanently associating objects(sheets/ranges/columns) to variables, where user modifications are expected on such objects.
Essentially we need to use dataFilters to target a specific sheet by ID.
#TheMaster pointed me in the right direction but I found the answers confusing so I just want to share my working example for Node.js.
Here's how to get the value of cell B2 from a sheet that has ID 0123456789
const getValueFromCellB2 = async () => {
const SPREADSHEET_ID = 'INSERT_SPREADSHEET_ID';
const SHEET_ID = 0123456789;
// TODO: replace above values with real IDs.
const google = await googleConnection();
const sheetData = await google.spreadsheets.values
.batchGetByDataFilter({
spreadsheetId: SPREADSHEET_ID,
resource: {
dataFilters: [
{
gridRange: {
sheetId: SHEET_ID,
startRowIndex: 1,
endRowIndex: 2,
startColumnIndex: 1,
endColumnIndex: 2,
},
},
],
},
})
.then((res) => res.data.valueRanges[0].valueRange.values);
return sheetData[0][0];
}
// There are many ways to auth with Google... Here's one:
const googleConnection = async () => {
const auth = await google.auth.getClient({
keyFilename: path.join(__dirname, '../../secrets.json'),
scopes: 'https://www.googleapis.com/auth/spreadsheets',
});
return google.sheets({version: 'v4', auth});
}
To simply read data we're using batchGetByDataFilter where dataFilters is an array of separate filter objects. The gridRange filter (one of many) allows us to specify a sheetId and range of cells to return.
Here is my working example for "rename sheet in spreadsheet by sheetId" function.
You can use other methods from Google Spreadsheets API Docs in the same way. Hope it will be helpful for somebody
<?php
function getClient() //standard auth function for google sheets API
{
$clientConfigPath = __DIR__ . '/google_credentials/client_secret.json';
$client = new Google_Client();
$client->setApplicationName('Google Sheets API PHP Quickstart');
$client->setScopes(Google_Service_Sheets::SPREADSHEETS);
$client->setAuthConfig($clientConfigPath);
$client->setAccessType('offline');
// Load previously authorized credentials from a file.
$credentialsPath = (__DIR__ . '/google_credentials/credentials.json');
if (file_exists($credentialsPath)) {
$accessToken = json_decode(file_get_contents($credentialsPath), true);
} else {
// Request authorization from the user.
$authUrl = $client->createAuthUrl();
printf("Open the following link in your browser:\n%s\n", $authUrl);
print 'Enter verification code: ';
$authCode = trim(fgets(STDIN));
// Exchange authorization code for an access token.
$accessToken = $client->fetchAccessTokenWithAuthCode($authCode);
// Store the credentials to disk.
if (!file_exists(dirname($credentialsPath))) {
mkdir(dirname($credentialsPath), 0700, true);
}
file_put_contents($credentialsPath, json_encode($accessToken));
printf("Credentials saved to %s\n", $credentialsPath);
}
$client->setAccessToken($accessToken);
// Refresh the token if it's expired.
if ($client->isAccessTokenExpired()) {
$client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
file_put_contents($credentialsPath, json_encode($client->getAccessToken()));
}
return $client;
}
function renameSheet(string $sheetId, string $newTitle, string $spreadsheetId)
{
// Get the API client and construct the service object.
$client = getClient();
$service = new Google_Service_Sheets($client);
$requests = [
new Google_Service_Sheets_Request([
'updateSheetProperties' => [
'properties' => [
'sheetId' => $sheetId,
'title' => $newTitle,
],
'fields' => 'title'
]
])
];
$batchUpdateRequest = new Google_Service_Sheets_BatchUpdateSpreadsheetRequest([
'requests' => $requests
]);
return $service->spreadsheets->batchUpdate($spreadsheetId, $batchUpdateRequest);
}
UPDATE
If you want to get sheet title by sheetId, you can use following function
function getSpreadsheetInfo($spreadsheetId)
{
$client = getClient();
$service = new Google_Service_Sheets($client);
$response = $service->spreadsheets->get($spreadsheetId);
return $response;
}
function getSheets($spreadsheetId)
{
$spreadsheet_info = getSpreadsheetInfo($spreadsheetId);
$sheets_info = [];
foreach ($spreadsheet_info as $item) {
$sheet_id = $item['properties']['sheetId'];
$sheet_title = $item['properties']['title'];
$sheets_info[$sheet_id] = $sheet_title;
}
return $sheets_info;
}
$sheets_info_array = getSheets($YOUR_SPREADSHEET_ID_HERE);
$sheets_info_array will be equal
array (
"sheet_id1(int)" => 'sheet_title1',
"sheet_id2(int)" => 'sheet_title3',
)
so you can get $your_sheet_id's title as $sheets_info_array[$your_sheet_id]
The initial blank empty tab that is always present when a new Google Sheet is created always has sheetId 0 assigned to it.
Subsequently created sheetIds are randomized ten digit numbers. Only the first tab has sheetId 0. Even if you rename a sheet, it's ID remains constant. IDs are never reused - they remain unique within a given sheet.
Using the Google Drive API, access to a Google Sheet is instantiated using the sheet's Google Drive file ID.
Once you have instantiated access to the particular Google Sheet file, you can then reference each tab within the sheet tab and manipulate information, format, etc within a tab of the sheet, by using the 'sheetId' nomenclature.
Here is a PHP example of renaming a Google Sheet's tab name using sheetId 0.
<?php
/*
* Google Sheets API V4 / Drive API V3, rename existing sheet tab example
*
*/
$fileID = '/* pass your Google Sheet Google Drive file ID here */';
$client = new Google_Client();
$client->useApplicationDefaultCredentials(); // the JSON service account key location as defined in $_SERVER
$client->setApplicationName('API Name');
$client->addScope(Google_Service_Drive::DRIVE);
$client->setAccessType('offline');
$client->setSubject('API Instance Subject');
$sheet = new Google_Service_Sheets($client);
$sheetList = $sheet->spreadsheets->get($fileID);
/*
* iterate through all Google Sheet tabs in this sheet
*/
$homeFlag = FALSE;
foreach($sheetList->getSheets() as $sheetRecord) {
/*
* if match, save $sheetTabID from Google Sheet tab
*/
if ($sheetRecord['properties']['sheetId'] == 0) {
$sheetTabID = $sheetRecord['properties']['sheetId'];
$sheetTabTitle = $sheetRecord['properties']['title'];
$homeFlag = TRUE;
}
}
/*
* if $homeFlag is TRUE, you found your desired tab, so rename tab in Google Sheet
*/
if ($homeFlag) {
$newTabName = 'NotTabZero';
$sheetRenameTab = new Google_Service_Sheets_BatchUpdateSpreadsheetRequest(array('requests' => array('updateSheetProperties' => array('properties' => array('sheetId' => $sheetTabID, 'title' => $newTabName), 'fields' => 'title'))));
$sheetResult = $sheet->spreadsheets->batchUpdate($sheetID,$sheetRenameTab);
}
?>
More simplier answer is to use the A1 Notation to get what sheet and rows you want
const res = await sheets.spreadsheets.values.get({
spreadsheetId: "placeholder_id_value",
range: "Sheet2!A:A", # This will grab all data out of sheet 2 from column A
})
reference
I have been working on a requirement, i.e. when a bug is created/inprogress in TFS post a HTTP call to Slack (third party collaboration tool).
When a bug is closed post one more HTTP call to Slack.
I had implemented TFS server side plugin, unfortunately we don't have complete access to TFS and cannot implement. So, planning to implement Webapi and host it (say in Docker container) and whenever bug created / closed event happens in TFS it should post HTTP call.
I have created a simple console app with a method and it's working fine.
any sample code or thoughts to convert it to web api?
if I host, can it monitor TFS events and posts some HTTP calls?
public class GetWI
{
static void Main(string[] args)
{
GetWI ex = new GetWI();
ex.GetWorkItemsByWiql();
}
public void GetWorkItemsByWiql()
{
string _personalAccessToken = "xxxx";
string _credentials = Convert.ToBase64String(System.Text.ASCIIEncoding.ASCII.GetBytes(string.Format("{0}:{1}", "", _personalAccessToken)));
//this is needed because we want to create a project scoped query
string project = "Agileportfolio";
//create wiql object
var wiql = new
{
query = "Select [State], [Title] " +
"From WorkItems " +
"Where [Work Item Type] = 'Bug' " +
"And [System.TeamProject] = '" + project + "' " +
"And [System.State] = 'New' " +
"Order By [State] Asc, [Changed Date] Desc"
};
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("https://test.visualstudio.com");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", _credentials);
//serialize the wiql object into a json string
var postValue = new StringContent(JsonConvert.SerializeObject(wiql), Encoding.UTF8, "application/json"); //mediaType needs to be application/json for a post call
var method = new HttpMethod("POST");
var httpRequestMessage = new HttpRequestMessage(method, "https://abrahamdhanyaraj.visualstudio.com/_apis/wit/wiql?api-version=2.2") { Content = postValue };
var httpResponseMessage = client.SendAsync(httpRequestMessage).Result;
if (httpResponseMessage.IsSuccessStatusCode)
{
WorkItemQueryResult workItemQueryResult = httpResponseMessage.Content.ReadAsAsync<WorkItemQueryResult>().Result;
//now that we have a bunch of work items, build a list of id's so we can get details
var builder = new System.Text.StringBuilder();
foreach (var item in workItemQueryResult.WorkItems)
{
builder.Append(item.Id.ToString()).Append(",");
}
//clean up string of id's
string ids = builder.ToString().TrimEnd(new char[] { ',' });
HttpResponseMessage getWorkItemsHttpResponse = client.GetAsync("_apis/wit/workitems?ids=" + ids + "&fields=System.Id,System.Title,System.State&asOf=" + workItemQueryResult.AsOf + "&api-version=2.2").Result;
if (getWorkItemsHttpResponse.IsSuccessStatusCode)
{
var result = getWorkItemsHttpResponse.Content.ReadAsStringAsync().Result;
//Read title
}
}
// Create Channel
string name = "xyzz3";
var payload = new
{
token = "xoxp-291239704800-292962676087-297314229698-a80e720d98e443c8afb0c4cb2c09e745",
name = "xyzz3",
};
var serializedPayload = JsonConvert.SerializeObject(payload);
var response = client.PostAsync("https://slack.com/api/channels.create" + "?token=test&name=" + name + "&pretty=1",
new StringContent(serializedPayload, Encoding.UTF8, "application/json")).Result;
if (response.IsSuccessStatusCode)
{
dynamic content = JsonConvert.DeserializeObject(
response.Content.ReadAsStringAsync()
.Result);
}
}
}
I use wcf service to listen events from TFS. You may find my project here: https://github.com/ashamrai/tfevents
For wcf service:
Update your ServiceName.svc file and add:
Factory="System.ServiceModel.Activation.WebServiceHostFactory"
Create web method to use json:
[OperationContract]
[WebInvoke(Method = "POST", RequestFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Bare)]
void WorkItemChangedEvent(Stream EventData);
Convert stream with Newtonsoft.Json to get information about event and work item:
StreamReader _reader = new StreamReader(pEventData, Encoding.UTF8);
string _eventStr = _reader.ReadToEnd();
WorkItemEventCore _wieventcorre = JsonConvert.DeserializeObject(_eventStr);
Then you have to create the subscription with url "http://host:port/service.svc/webmethod": https://learn.microsoft.com/en-us/vsts/service-hooks/services/webhooks
Instead of using a query and manually polling Visual Studio Team Services (VSTS), you can use a concept called WebHooks. You configure a WebHook in VSTS to listen for events and send these to a public endpoint. One event type is for Work Items. The endpoint can be any type of public endpoint, for example an Azure Function.
If the only thing you want to do is post the events to Slack, it's even easier because that's a standard integration point: Slack with VSTS.
This is much easier then using a server side plugin or writing your own Web API.
I am trying to do use the post method for a simple suitescript program, i am very new to this.
In Netsuite i have written a suitescript as follows.
function restPost()
{
var i = nlapiLoadRecord('department', 115);
var memo = nlapisetfieldvalue('custrecord225', ' ');// this is a customfield, which i want to populate the memo field, using rest client in firefox
var recordId = nlapiSubmitRecord(i);
}
i have created a script record and uploaded this suitescript and even copied the external URL to paste it in restclient.
In Restclient(firefox plugin), pasted the external URL, i have given the method as post, header authorization given, content-type: application/json, and in body i put in {"memo":"mynamehere"};
In this the error i get is
message": "missing ) after argument list
I even tried it by writting other suitescript programs the errors i get is as follows:
Unexpected token in object literal (null$lib#3) Empty JSON string
Invalid data format. You should return TEXT.
I am kinda new to the programming world, so any help would be really good.
I think you are trying to create a RESTlet for POST method. Following is the sample code for POST method -
function createRecord(datain)
{
var err = new Object();
// Validate if mandatory record type is set in the request
if (!datain.recordtype)
{
err.status = "failed";
err.message= "missing recordtype";
return err;
}
var record = nlapiCreateRecord(datain.recordtype);
for (var fieldname in datain)
{
if (datain.hasOwnProperty(fieldname))
{
if (fieldname != 'recordtype' && fieldname != 'id')
{
var value = datain[fieldname];
if (value && typeof value != 'object') // ignore other type of parameters
{
record.setFieldValue(fieldname, value);
}
}
}
}
var recordId = nlapiSubmitRecord(record);
nlapiLogExecution('DEBUG','id='+recordId);
var nlobj = nlapiLoadRecord(datain.recordtype,recordId);
return nlobj;
}
So after deploying this RESTlet you can call this POST method by passing following sample JSON payload -
{"recordtype":"customer","entityid":"John Doe","companyname":"ABCTools Inc","subsidiary":"1","email":"jdoe#email.com"}
For Authorization you have to pass request headers as follows -
var headers = {
"Authorization": "NLAuth nlauth_account=" + cred.account + ", nlauth_email=" + cred.email +
", nlauth_signature= " + cred.password + ", nlauth_role=" + cred.role,
"Content-Type": "application/json"};
I can understand your requirement and the answer posted by Parsun & NetSuite-Expert is good. You can follow that code. That is a generic code that can accept any master record without child records. For Example Customer Without Contact or Addressbook.
I would like to suggest a small change in the code and i have given it in my solution.
Changes Below
var isExistRec = isExistingRecord(objDataIn);
var record = (isExistRec) ? nlapiLoadRecord(objDataIn.recordtype, objDataIn.internalid, {
recordmode: 'dynamic'
}) : nlapiCreateRecord(objDataIn.recordtype);
//Check for Record is Existing in Netsuite or Not using a custom function
function isExistingRecord(objDataIn) {
if (objDataIn.internalid != null && objDataIn.internalid != '' && objDataIn.internalid.trim().length > 0)
return true;
else
return false;
}
So whenever you pass JSON data to the REStlet, keep in mind you have
to pass the internalid, recordtype as mandatory values.
Thanks
Frederick
I believe you will want to return something from your function. An empty object should do fine, or something like {success : true}.
Welcome to Netsuite Suitescripting #Vin :)
I strongly recommend to go through SuiteScript API Overview & SuiteScript API - Alphabetized Index in NS help Center, which is the only and most obvious place to learn the basics of Suitescripting.
nlapiLoadRecord(type, id, initializeValues)
Loads an existing record from the system and returns an nlobjRecord object containing all the field data for that record. You can then extract the desired information from the loaded record using the methods available on the returned record object. This API is a core API. It is available in both client and server contexts.
function restPost(dataIn) {
var record = nlapiLoadRecord('department', 115); // returns nlobjRecord
record.setFieldValue('custrecord225', dataIn.memo); // set the value in custom field
var recordId = nlapiSubmitRecord(record);
return recordId;
}
I have a list of people (first and last name) who I want to follow, but I don't want to Google or search them via Twitter separately. What is the best way to get the Twitter handles? Some GitHub repos or tutorials are also fine.
Twitter offers a "User Search" API request.
If you want to search for a user named "Ada Lovelace" you will need to send an OAuth'd request to
https://api.twitter.com/1.1/users/search.json?q=Ada%20Lovelace
You will get back a list of results. There may be many people who share the same first and last name.
As for how to do it, that rather depends on the programming language you want to use.
If you just want a clickable link, use https://twitter.com/search?q=Terence%20Eden
So firstly this question is off-topic but I will try write an answer for you. You could use the twitter api for this but that might be a little overkill if you just want to do this for you.
I made you an API
I made an API just for you using KimonoLabs. You can use this and just make a script that loops through your list and requests this api every time, then return a list of the results. Here is the API endpoint:
https://www.kimonolabs.com/api/duwxgie4?apikey=D6UKiTtKU93kv0YJj8i3kFBAbsIjdSTC&q=PERSON%20NAME
The &q= is the paramater for the person's name. To seperate the first and last name use %20, like so: Robert%20Keus
The documentation for this api is here:
https://www.kimonolabs.com/apis/duwxgie4
Let me know if you need any help,
Luca
Latest answer # 2016
First Solution: I wrote following node.js script. You need access_token and id of pulicly published google doc spreadsheet. For testing purpose I have provided sample spreadsheet's link and its id in following code.
var Twit = require('twit'),
async = require('async');
var T = new Twit({
consumer_key: 'xxxxxxxx',
consumer_secret: 'xxxxxxxxxxxxxxxxxxxxxxxxxx',
access_token: 'xxxxxxxxxxx',
access_token_secret: 'xxxxxxxxxxxxxxxxxxxxxxxxx',
timeout_ms: 60*1000 // optional HTTP request timeout to apply to all requests.
});
//https://docs.google.com/spreadsheets/d/1n7DxgJTTHZ9w3xwiHokUhXMLkBwpP5c9ZLFmsYFDCic/edit?usp=sharing
var GoogleSpreadsheet = require("google-spreadsheet"),
_ = require('underscore');
var sheetId = req.params.sheet || "1n7DxgJTTHZ9w3xwiHokUhXMLkBwpP5c9ZLFmsYFDCic",
sheet = new GoogleSpreadsheet(sheetId);
async.waterfall([
function (cb) {
sheet.getRows(1, {}, function (err, rows) {
if (err) { res.send(err); return;};
var names = [];
_.each(rows, function (row) {
names.push(row.first + " " + row.last);
});
cb(null, names);
});
},
function (names, callback1) {
async.map(names, function(name, cb){
T.get('users/search', { q: name, page: 1 }, function (err, data, response) {
if(data.length)
cb(null, {screen_name: data[0].screen_name, name:data[0].name});
else
cb(null, {screen_name: "no_data_retrieved", name: name});
});
}, function (err, results) {
callback1(null, results);
});
},
function (users, callback) {
console.log(users); //**YOU GET ALL DESIRED DATA HERE**
}
], function (err, result) {
//handle in memory data
});
Second Solution: Clone node-cheat twitter-screen-names, run npm install and shoot node server, Now get all twitter usernames as json in browser.
Happy Helping!