How do I get sorted results from the Google Photos search API? - api

I'm using the search API for Google Photos documented here. I'd like the results in the response to be sorted from newest to oldest, but by default, the results are sorted from oldest to newest. Is there a way to reverse the sorting?

I believe your goal as follows.
You want to sort the result values from the method of "Method: mediaItems.search".
You want to sort the values from oldest to newest.
Issue and workaround:
Unfortunately, in the current stage, it seems that there is no parameter for sorting the returned values for the the method of "Method: mediaItems.search" in Google Photos API. Also, it seems that such parameter is not existing in the method of "mediaItems.list".
By the way, it was found that when albumId is used in the request body for the method of "Method: mediaItems.search", the returned values are sorted as the ascending order. If you use the albumn ID, I think that your goal can be achieve by this.
On the other hand, when albumId is NOT used in the request body, the returned values are sorted as the descending order. And also, it seems that when filteres is used in the request body, the returned values are sorted as the descending order.
From your question, I thought that in your situation, albumId might be not used. So in this case, as the current workaround, how about sorting the values using a script after the values are retrieved? In this answer, I would like to propose to use the Web Apps created by Google Apps Script as a wrapper API.
Usage:
1. Create new project of Google Apps Script.
Sample script of Web Apps is a Google Apps Script. So please create a project of Google Apps Script.
If you want to directly create it, please access to https://script.new/. In this case, if you are not logged in Google, the log in screen is opened. So please log in to Google. By this, the script editor of Google Apps Script is opened.
2. Linking Cloud Platform Project to Google Apps Script Project.
About this, you can see the detail flow at here.
And also, please enable Google Photos API at API console.
3. Add scope.
In this case, please addt the scope of https://www.googleapis.com/auth/photoslibrary to the manifest file (appsscript.json).
4. Script.
Please copy and paste the following script (Google Apps Script) to the script editor. This script is for the Web Apps. This Web Apps is used as an API.
function doGet(e) {
const key = "sampleKey"; // This is used for using this Web Apps.
try {
if (e.parameter.key != key) throw new Error("Invalid key.");
const albumId = e.parameter.albumId;
const filters = e.parameter.filters;
const sort = e.parameter.sort;
const headers = {"Authorization": "Bearer " + ScriptApp.getOAuthToken()};
const url = "https://photoslibrary.googleapis.com/v1/mediaItems:search";
let mediaItems = [];
let pageToken = "";
const metadata = {pageSize: 100, pageToken: pageToken};
if (albumId) metadata.albumId = albumId;
if (filters) metadata.filters = JSON.parse(filters);
do {
const params = {
method: "post",
headers: headers,
contentType: "application/json",
payload: JSON.stringify(metadata),
}
const res = UrlFetchApp.fetch(url, params);
const obj = JSON.parse(res.getContentText());
mediaItems = mediaItems.concat(obj.mediaItems);
pageToken = obj.nextPageToken || "";
} while (pageToken);
if (mediaItems.length > 0) {
if (sort && sort == "ascending") {
mediaItems.sort((a, b) => new Date(a.mediaMetadata.creationTime) < new Date(b.mediaMetadata.creationTime) ? -1 : 1);
}
return ContentService.createTextOutput(JSON.stringify({values: mediaItems}));
}
return ContentService.createTextOutput(JSON.stringify({error: "No values."}));
} catch(err) {
return ContentService.createTextOutput(JSON.stringify({error: err.message}));
}
}
5. Deploy Web Apps.
The detail information can be seen at the official document.
On the script editor, at the top right of the script editor, please click "click Deploy" -> "New deployment".
Please click "Select type" -> "Web App".
Please input the information about the Web App in the fields under "Deployment configuration".
Please select "Me" for "Execute as".
This is the important of this workaround.
Please select "Anyone" for "Who has access".
In this case, the user is not required to use the access token. So please use this as a test case.
When you want to use the access token, please set it to Anyone with Google account or Only myself. By this, the user can access to the Web Apps using the access token. When you use the access token, please include the scope of https://www.googleapis.com/auth/drive.readonly or https://www.googleapis.com/auth/drive.
Please click "Deploy" button.
When "The Web App requires you to authorize access to your data" is shown, please click "Authorize access".
Automatically open a dialog box of "Authorization required".
Select own account.
Click "Advanced" at "This app isn't verified".
Click "Go to ### project name ###(unsafe)"
Click "Allow" button.
Copy the URL of Web App. It's like https://script.google.com/macros/s/###/exec.
When you modified the Google Apps Script, please redeploy as new version. By this, the modified script is reflected to Web Apps. Please be careful this.
6. Testing.
As the test of this Web Apps, I would like to propose to use the following curl command. Please replace https://script.google.com/macros/s/###/exec with your Web Apps URL.
Simple use:
In this curl command, the result value is returned as the ascending order of oldest to newest.
$ curl -GL -d "key=sampleKey" -d "sort=ascending" https://script.google.com/macros/s/###/exec
Use albumId:
When you want to use the album ID, please use the following curl command.
$ curl -GL -d "albumId=###" -d "key=sampleKey" -d "sort=ascending" https://script.google.com/macros/s/###/exec
In this case, even when -d "sort=ascending" is not used, the result value is returned as the ascending order of oldest to newest.
Use filters:
When you want to use the filters, please use the following curl command.
$ curl -GL -d 'filters={"dateFilter":{"ranges":[{"startDate":{"year":2020},"endDate":{"year":2021}}]}}' -d "key=sampleKey" -d "sort=ascending" https://script.google.com/macros/s/###/exec
In this command, the values of 2020 - 2021 are returned as the ascending order of oldest to newest.
Note:
Although when I searched this at the Google issue tracker, I couldn't find about it. So how about reporting this as the future request? Ref
References:
Method: mediaItems.search
Related thread.
How to use Google Photos API Method: mediaItems.search in Google apps script for a spreadsheet
Google photos api adding photos not working, upload seems to work
Google Apps Scripts: import (upload) media from Google Drive to Google Photos?

Related

When I import Swagger API to Postman, all request names end up blank in Postman GUI

I am QA engineer. The Dev team produces documentation for our product's RESTful API using Swagger. I need to import this to Postman to make it easy to invoke the product's API.
After importing the JSON file (in Swagger format) into Postman, there is 1 but big problem: All titles (and descriptions) of individual requests are blank! (see screen shot below).
Apparently, this is a known issue, documented here: https://github.com/postmanlabs/postman-app-support/issues/1434
We have literally hundreds of requests. I need to find a sufficiently effective yet simple way to ensure all request titles in Postman are populated with a value which I would like to calculate on the fly.
I have been considering the following approach:
Write a command line tool (using NodeJS or another
solid platform) which will receive:
1. ID of the collection to fix
2. api key
It will iterate through all requests in the
collection. For each request: if Name field is
blank, then a substring of the request URL
will be assigned to the Name field; if name is
not blank, the request is left alone.
What I am unsure about:
Can I do this programmatically from Postman? It does not make sense to put this code into any one individual request (as pre or post).
(If I have to code this util outside of Postman)
For NodeJS there are "postman-collection" and
"postman-sdk" but I am slightly confused which I
should use.
Unfortunately, I have not yet found any suitable > library for maintaining Postman collections using C# > or Java.
I am quite frankly confused by the available options. Any guidance will be appreciated.
I had the same problem, solved it thanks to Ian T Price solution (just copy operationId value into a new key summary). I decided to write a little javascript utility for this:
function swagPostman(swaggerJson) {
for (let path in swaggerJson.paths) {
let methods = ["get", "head", "post", "put", "delete", "connect", "options", "trace", "patch"];
methods.map(method => {
if ((swaggerJson.paths[path] || {})[method]) {
swaggerJson.paths[path][method].summary =
swaggerJson.paths[path][method].operationId;
}
});
}
return JSON.stringify(swaggerJson);
}
Also made a simple pen where to run the script with a GUI: https://codepen.io/0x616c65/full/pMaQpb. You just copy-paste your swagger.json file in that pen and woilĂ !
A simple answer to this is to add a line summary: <RequestName>
I came across this problem using the excellent APIs-Gurus OpenAPIDirectory repo
These swagger.yaml files have a operationId: line which can be duplicated and the key replaced with summary: using:
awk '{if (!/ operationId:/) {print ; next} ; { print; a=gensub(/ operationId:/, " summary:",1) ; print a}}' swagger.yaml > swagger-new.yaml
Importing this into Postman then shows the correct request name.
PostMan is separating out the Import/Export functions in to separate plug-ins but their plug-in model leaves a lot to be desired at the current time.

Why won't the Google Maps Directions API example in the 'Google Maps' book work for me?

I'm using the Petrousos 'Google Maps' book, and trying to run the example in the CHAPTER17/HTML/Directions Service.html downloaded from the book's website at www.mhprofessional.com at item 0071823026.
I had to adjust the table dimensions to get it to display properly, but otherwise made no changes. I'm running it through Firefox.
I set the origin and destination and clicked "Show Directions", at which point nothing happened.
The event called the following function:
function showDirections() {
var start = document.getElementById("origin").value;
var end = document.getElementById("destination").value;
var request = {
origin: start,
destination: end,
travelMode: google.maps.TravelMode.DRIVING
};
directionsService.route(request, function(result, status) {
if (status == google.maps.DirectionsStatus.OK) {
directionsDisplay.setDirections(result);
printDirections(result);
}
});
}
I get as far as the directionsService.route call, but it is apparently not being executed, and status and result are not defined.
I have no idea how to debug this further, not having access to the API code..
Could the syntax of the call in the example be outdated?
I don't have an API key, but I understand it is not necessary, and I have run other examples without one.
Do you old examples still work? If not, then it's because you don't have an API key. Google will shut you down after you surpassed the quota. I believe the quota is at 50 requests per hours.
Google has the most recent documentation (and it's impressively well documented)
https://developers.google.com/maps/documentation/directions/intro#Audience
Also, can you look at what the object returns? If so, that will usually indicate the quote being surpassed. You can do this by setting a breakpoint at the link
if (status == google.maps.DirectionsStatus.OK) {
If you're using Chrome, you can get to the debugger by right-clicking on the page adn choosing 'Inspect Element', then go to sources, find your line of code and click the link number to set a breakpoint.

Google Apps Script login to website with HTTP request

I have a spreadsheet on my Google Drive and I want to download a CSV from another website and put it into my spreadsheet. The problem is that I have to login to the website first, so I need to use some HTTP request to do that.
I have found this site and this. If either of these sites has the answer on it, then I clearly don't understand them enough to figure it out. Could someone help me figure this out? I feel that the second site is especially close to what I need, but I don't understand what it is doing.
To clarify again, I want to login with an HTTP request and then make a call to the same website with a different URL that is the call to get the CSV file.
I have done a lot of this in the past month so I should be able to help you, we are trying to emulate the browsers behaviour here so first you need to use chrome's developer tools(or something similar) and note down the exact things the browser does like the form values posted, the url that is called and so on. The following example shows the general techinique to be used:
The first step is to login to the website and get the session cookie:
var payload =
{
"user_session[email]" : "username",
"user_session[password]" : "password",
};// The actual values of the post variables (like user_session[email]) depends on the site so u need to get it either from the html of the login page or using the developer tools I mentioned.
var options =
{
"method" : "post",
"payload" : payload,
"followRedirects" : false
};
var login = UrlFetchApp.fetch("https://www.website.com/login" , options);
var sessionDetails = login.getAllHeaders()['Set-Cookie'];
We have logged into the website (In order to confirm just log the sessionDetails and match it with the cookies set by chrome). The next step is purely dependent on the website so I will give u a general example
var downloadPayload =
{
"__EVENTTARGET" : 'ctl00$ActionsPlaceHolder$exportDownloadLink1',
};// This is just an example it may or may not be needed, if needed u need to trace the values from the developer tools.
var downloadCsv = UrlFetchApp.fetch("https://www.website.com/",
{"headers" : {"Cookie" : sessionDetails},
"method" : "post",
"payload" : downloadPayload,
});
Logger.log(downloadCsv.getContentText())
The file should now be logged, you can then parse the csv using hte GAS inbuilt function and dump the data in the spreadsheet.
A few points to note:
I have assumed that all form post values are static and can be
hardcoded, in case this is not true then let me know I will give you
a function that can extract values from the html.
Some websites require the browser to send a token value(the value will be present in the html) along with the credentials. In this case you need to extract the values and then post it.

Using Google Apps Script and Google URL shortener API to get click analytics

in Apps Script, I would like to fetch click statistics for previously generated short URLs via the Google URL shortener API.
I'm afraid I don't really get the API documentation. This is what I tried:
function GetShortUrlClicks() {
var analytics = UrlShortener.Url.get('http://goo.gl/WPN7wo').getAnalytics();
var clicks = analytics.getAllTime();
Logger.log(clicks)
}
It seems that the variable analytics is undefined and thus the next line throws an error.
Any help is much appreciated.
You are missing the optional args required to get the Analytics data back.
This function ought to work -
function GetShortUrlClicks() {
var analytics = UrlShortener.Url.get('http://goo.gl/WPN7wo',{projection:'FULL'}).getAnalytics();
var clicks = analytics.getAllTime();
Logger.log(clicks)
}
You can use more see more reference here - https://developers.google.com/url-shortener/v1/url/get

Using Twitter API on shared server - Rate limit exceeded even though I am caching the response

I have written a php script which gets the latest status update for 12 different twitter accounts by pulling an xml for each and caching it on my server. This currently runs every 30 minutes.
Unfortunately I keep getting the "Rate limit exceeded. Clients may not make more than 150 requests per hour." error event though i'm only making 24 requests from the 150 I should have.
I assume this is because my domain is on a shared server and twitter is counting other requests against me.
How can I authorise my requests so i'm not restriced by the standard IP limit?
I have no experience of OAuth so need step by step instructions if possible.
Thanks in advance!
OK so I managed to get the most of this working with no previous experience of API's etc.
Here is my step by step guide:
Step 1.
Create a Twitter list.
Go to: https://twitter.com/username/lists
Click "Create list"
Enter details and save.
Go to a twitter user you wish to add to the list and click the gear dropdown and select "Add or remove from lists". Tick the checkbox next to your list.
Step 2.
Create a Twitter App via: https://dev.twitter.com/apps/new
Log in using your Twitter credentials.
Give your app a name, description etc.
Go to the Settings tab and change the Access type to Read and Write then click "Update this Twitter application's settings".
Click "Create my access token" at the bottom of the page.
You will now have a Consumer Key, Consumer secret, Access token and Access token secret. Make a note of these.
Step 3. Create API tokens.
Download and install onto your server the Abraham Twitter oAuth library from: https://github.com/abraham/twitteroauth (I'll use a folder called "twitter").
Create a new file, name it authorise.php in the oAuth folder and put the following code inside (with your generated keys in place of the named text). (Put the code between < ? PHP and ?> brackets).
// Create our twitter API object
require_once("twitteroauth/twitteroauth.php");
$oauth = new TwitterOAuth('Put-Consumer-Key-here', 'Put-Consumer-secret-here',
'Put-Access-Token-here', 'Put-Access-token-secret-here');
// Send an API request to verify credentials
$credentials = $oauth->get("account/verify_credentials");
echo "Connected as #" . $credentials->screen_name;
// Post our new "hello world" status
$oauth->post('statuses/update', array('status' => "hello world"));
This has now authorised your twitter App for the API and posted a "hello world" status on your twitter account.
Note: The Read / Write access change we did earlier alowed the code to post the status update, it's not actually needed to pull the list from the API but I did it to make sure it was working OK. (You can turn this off again by going back to the Settings).
Step 4.
Create PHP file to pull your list and cache the file.
Create an XML file (YOUR-FILE-NAME.xml) and save it in the oAuth folder.
Create a PHP file (YOUR-PHP-FILE.php) and save it in the oAuth folder
Edit the below code with your twitter API keys, file name and twitter list details and save it in your PHP file. (Put the code within < ? PHP and ?> brackets).
/* Twitter keys & secrets here */
$consumer_key = 'INSERT HERE';
$consumer_secret = 'INSERT HERE';
$access_token = 'INSERT HERE';
$access_token_secret = 'INSERT HERE';
// Create Twitter API object
require_once('twitteroauth/twitteroauth.php');
// get access token and secret from Twitter
$oauth = new TwitterOAuth($consumer_key, $consumer_secret, $access_token, $access_token_secret);
// fake a user agent to have higher rate limit
$oauth->useragent = 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.9) Gecko/20071025 Firefox/2.0.0.9';
// Send an API request to verify credentials
$credentials = $oauth->get('account/verify_credentials');
echo 'Connected as #' . $credentials->screen_name . '\n';
// Show API hits remaining
$remaining = $oauth->get('account/rate_limit_status');
echo "Current API hits remaining: {$remaining->remaining_hits}.\n";
$ch = curl_init();
$file = fopen("YOUR-FILE-NAME.xml", "w+");
curl_setopt($ch, CURLOPT_URL,'https://api.twitter.com/1/lists/statuses.xml?slug=INSERT-LIST-NAME&owner_screen_name=INSERT-YOUR-TWITTER-USERNAME-HERE&include_entities=true');
curl_setopt($ch, CURLOPT_FILE, $file);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_exec($ch);
curl_close($ch);
fclose($file);?>
Copy the file path into your browser and test it. (e.g. http://www.yourwebsite.com/twitter/YOUR-PHP-FILE.php)
This should contact twitter, pull the list as an XMl file and save it into YOUR-FILE-NAME.xml. Test it by opening the XML file, it should have the latest statuses from the users in your twitter list.
Step 5.
Automate the PHP script to run as often as you like (up to 350 times per hour) via a Cron job.
Open your Cpanel and click "Cron jobs" (usually under Advanced).
You can choose the regularity of your script using the common settings.
In the command field add the following code:
php /home/CPANEL-USERNAME/public_html/WEBSITE/twitter/YOUR-PHP-FILE.php >/dev/null 2>&1
Your script will now run as often as you have chosen, pull the list from twitter and save it into YOUR-FILE-NAME.xml.
Step 6.
You can now pull statuses from the cached XML file meaning your visitors will not be making unnecessary calls to the API.
I've not worked out how to target a specific screen_name yet if anyone can help there?
a) don't check 12 different accounts, create a [public] list https://twitter.com/lists and check only the it => 12 times less requests
b) use this awesome oAuth lib: https://github.com/abraham/twitteroauth and use oAuth requests instead of unsigned => you will get 350 requests and they will not be affected by IP limit