How do I get images from a particular project using the OpenAsset REST API? - rest

I asked a while ago about how to get the actual image file from the File noun using the OA REST API.
I'm confused as to how to retrieve images related to a specific project.

Thanks to a helpful nudge from Jason I've discovered that the secret password is filtering using the params.
This still feels a bit unpleasant as it requires 4 trips to the OA server, so I'm probably still missing something that would allow me to concatenate these requests into one.
I wrote a short Ruby programme that will go and get a single image to illustrate the principle:
baseURL = "http://IP.A.DDR.ESS"
password = "password"
username = "login"
require 'net/http'
require "uri"
require 'json'
def ask_oa(request, baseURL, params, username, password, useParams)
#This constructs a REST request, makes it, and returns the JSON response.
uri = URI.parse(baseURL + request)
if useParams
uri.query = URI.encode_www_form(params)
end
puts uri
http = Net::HTTP.new(uri.host, uri.port)
request = Net::HTTP::Get.new(uri.request_uri)
request.basic_auth(username, password)
response = Net::HTTP.start(uri.hostname, uri.port) {|http|
http.request(request)
}
json_response = JSON.parse(response.body)
puts JSON.pretty_generate(json_response)
return json_response
end
puts "Step 1"
#You'll need to change this for *your* project name
project_name = "M020707"
puts "go and get a project where the project number is "+project_name+":"
request = "/REST/1/Projects"
params = { :textMatching => "exact",
:code => project_name
}
projectJSON = ask_oa(request, baseURL, params, username, password, true)
projectNumber = projectJSON[0]["id"]
#This ID is the ID of the Project. You use this as the Project_id to
#key into the Files list that we access next
puts "Step 2"
puts "go and get the files that have a project_id of "+projectNumber+":"
request = "/REST/1/Files"
params = { :textMatching => "exact",
:project_id => projectNumber
}
fileJSON = ask_oa(request, baseURL, params, username, password, true)
#You'd want a more exciting way of picking a file out of that list
#probably based on the ranking of the files or some other secret knowledge
the_file_I_want = 0
fileID = fileJSON[the_file_I_want]["id"]
#This is a single file, picked from a list of all the files associated
#with the project that we pulled in step 1. Now that we have a file id
#we're back to where we were when I asked my last question on SO.
#To OA, File means the source file, we now need to pick a
#processed/formatted version of it:
puts "Step 3"
puts "go and get the size options for the file with the ID "+fileID+":"
request = "/REST/1/Files/"+fileID+"/Sizes"
params = {}
sizeJSON = ask_oa(request, baseURL, params, username, password, false)
#implement a picker in here to get the format that you want. 0 is just
#the first, and I don't know if the order that they are provided in is
#deterministic
s = sizeJSON[0]
#This gets to the list of actual files that you can now construct a URL from
puts "Step 4"
puts "Request an image with this URL:"
puts baseURL + s["http_root"] + s["relative_path"]
#Now we can just request the file at the end of
#this url and we'll see a picture!

Related

How to specify file name for a download done via POST in akka http

The user sends a post request, than based on that post body I create an Excel file (.xlsx) and want to send that file back, without storage of that file itself.
def writeAsync(out: OutputStream): Unit = {
Future {
val wb = new XSSFWorkbook
val sheet1: Sheet = wb.createSheet("sheet1");
val os = new ByteArrayOutputStream()
wb.write(os)
os.writeTo(out)
out.close()
wb.close()
}
}
...
pathPrefix("createAndDownloadExcel") {
post {
...
val generatedFileName = "customGeneratedFileName.xlsx" // <-- this name should the file name be like
val (out, source) = StreamConverters.asOutputStream().preMaterialize()
writeAsync(out)
complete(HttpEntity(ContentTypes.`application/octet-stream`, source))
}
}
The response has the excel content with the file name: "createAndDownloadExcel", but I would like it to have the file name based on the individual generated file name.
The name will be later manually generated based on the POST body, whereby a simple change in pathPrefix("fixedName.xlsx") does not work for my needs.
How can I solve this, being able to give a dynamic file name for that returned OutputStream?
"org.apache.poi" % "poi-ooxml" % "5.2.0"
Try adding response header Content-Disposition.
The first parameter in the HTTP context is either inline (default value, indicating it can be displayed inside the Web page, or as the Web page) or attachment (indicating it should be downloaded; most browsers presenting a 'Save as' dialog, prefilled with the value of the filename parameters if present).
import akka.http.scaladsl.model.headers.ContentDispositionTypes.attachment
import akka.http.scaladsl.model.headers.`Content-Disposition`
....
respondWithHeader(`Content-Disposition`(attachment, Map("filename" -> "customGeneratedFileName.xlsx"))) {
complete(HttpEntity(ContentTypes.`application/octet-stream`, source))
}

Create session redirect link in content asset

Our company has multiple brands and each brand has its own host name, but they are all part of the same site. We can let customers share baskets and other session information when they switch between brands via a redirect link using URLUtils.sessionRedirect.
But URLUtils is not available in content assets. Is it possible to form a session redirect link in content asset keeping all the session information?
Thanks in advance.
You can include dynamic content in Content Assets with the $include('Controller-Name', 'name1', 'value1', 'name2', 'value2', ...)$ syntax. See the MarkupText Class Documentation for more info on that syntax. The 'name1' and 'value1' parameters are mapped as query string attributes eg: Controller-Name?name1=value1&name2=value2
Create a controller that outputs the session redirect link you need, and call it via that syntax like: $include(Util-RenderSessionLink, 'siteID', 'foo')$
The controller would need to use a response Content-Type header of text/plain or something like that so that nothing is injected into the response. (eg: Storefront toolkit or tracking tags) For example:
response.setContentType('text/plain');
Alternatively, you could process the content asset for some sorts of keys that you perform find & replace operations on. For example, the following code does a find & replace on a Content Asset's body content for the key: '%%SessionLink%%'.
var ContentMgr = require('dw/content/ContentMgr');
var URLUtils = require('dw/web/URLUtils');
if (!empty(content) {
var content = ContentMgr.getContent('my-content-id');
var contentOut = "";
var viewData = {};
contentOut = content.custom.body.getMarkup()
.replace('%%SessionLink%%', URLUtils.sessionRedirect(...));
viewData.content = contentOut;
// then output your `pdict.content` key within a template with the appropriate encoding
}
If anybody else is running into this, we added a bit of client-side javascript that pickups up all outbound links and if it's one of our domains it sends them through a session redirect. This way we don't need content managers to fix very link between domains:
var domains = ["domaina.com", "domainb.com", "domainc.com"]
var sessionRedirectBase = '/s/Our-Site/dw/shared_session_redirect';
$(document).on("click.crossSite", "a", (e) => {
const href = $(e.currentTarget).attr("href");
if (href) { //does the link have a href
if (href.match(/^(http(s)?:)?\/\//)) { //is href an absolute url
const url = new URL(href);
if (url.hostname != window.location.hostname && domains.indexOf(url.hostname) > -1) { //is hostname not the current one and part of the domains array
e.preventDefault();
const sessionRedirect = `${sessionRedirectBase}?url=${encodeURIComponent(href)}`
window.location = sessionRedirect;
}
}
}
});

Meteor Sending dynamic urls through email

I'm trying to send a dynamic url using a template. I want to direct users to a specific link, but I'm not sure how to do that dynamically? I can just hardcode the url, but would prefer to use dynamic urls in case I make changes in the future.
here's what I have, I realize that trying to store a url variable like this
url: "{{pathFor 'welcome'}}"
is pretty dumb, but I can't figure out how to do this. Any help is appreciated! This is what I have:
var dataContext = {
message: "To set up your profile follow the link",
url: "{{pathFor 'welcome'}}",
hyperlinkText: "Get Started",
title: "Welcome to site!"
};
var html = Blaze.toHTMLWithData(Template.emailTemplate, dataContext);
var from = "myemail#mysite.com";
var to = currentUser.emails[0].address;
var subject = "Welcome to site";
Meteor.call("sendEmail", to, from, subject, html);
In javascript just evaluate Router.path(pathName) to get the path. You can get the base path with Meteor.absoluteUrl(). Note that the former will include a leading / while the latter includes a trailing / so you'll need to remove one. Ex:
function dynamicPath(pathName){
return Meteor.absoluteUrl() + Router.path(pathName).substr(1);
}

How to export 15000+ Google Group members?

There are around 15000+ members in my Google Group and I'd like to export them to CSV file. Google Groups says there are too many to export Is there any other way to export all the 15000+ members?
This is possible using the Directory API. You can see how to set up a project in the Google Developer's Console with the appropriate scopes here. I have a sample of code below that shows how I did this for my email list manager (written in Python3). Note that mine just outputs to a list, but you could write to a csv as well.
import urllib.request, urllib.parse, urllib.error
import json
req = urllib.request.Request("https://www.googleapis.com/admin/directory/v1/groups/%s/members" % (group.replace("#", "%40").replace(".", "%2E")))
req.add_header("Authorization", "Bearer %s" % (authToken))
rawResponse = urllib.request.urlopen(req)
fResponse = json.loads(rawResponse.readall().decode('utf-8'))
if "members" in fResponse:
curMembers = fResponse["members"]
while "nextPageToken" in fResponse:
req = urllib.request.Request("https://www.googleapis.com/admin/directory/v1/groups/%s/members?pageToken=%s" % (group.replace("#", "%40").replace(".", "%2E"), fResponse['nextPageToken']))
req.add_header("Authorization", "Bearer %s" % (authToken))
rawResponse = urllib.request.urlopen(req)
fResponse = json.loads(rawResponse.readall().decode('utf-8'))
if "members" in fResponse:
curMembers.extend(fResponse["members"])
You'll need to define a way to retrieve the oauth2 authentication token (authToken in the above snippet). I do so as below, but there are dedicated libraries for this (just not for Python3 that I know of):
def RenewToken():
"""
This function retrieves an authentication token from Google which will allow the script
to view and edit group membership
"""
clientID = "client_id.apps.googleusercontent.com"
clientSecret = "client_secret"
grantType = "refresh_token"
responseType = "code"
refreshToken = "refresh_token"
requestDict = {"client_secret": clientSecret, #field-value pairs for the request body
"grant_type": grantType,
"refresh_token": refreshToken,
"client_id": clientID}
requestUri = "https://accounts.google.com/o/oauth2/token"
requestString = urllib.parse.urlencode(requestDict)
requestBody = requestString.encode('utf-8')
request = urllib.request.Request(requestUri, data=requestBody)
response = urllib.request.urlopen(request)
responseDict = json.loads(response.readall().decode('utf-8'))
return responseDict['access_token']
You can get client_id, client_secret and refresh_token from the Google developer's console for your project.
you can export all contacts in your google group in a Google Spreadsheet
link : https://developers.google.com/apps-script/reference/groups/
After you can create a trigger to add new contact dynamically, and export you Google spreadsheet on a cvs's file.
you can create this solution quickly and simple.

using Zend_Gdata_Spreadsheets for public spreadsheets?

I have this code which is working, to load a Google Spreadsheet and load some data from it. If the spreadsheet in question is public, how do i modify the code to not require a username/password?
$key="keytothespreadsheet";
$user="test#example.com";
$pass="*****";
$authService = Zend_Gdata_Spreadsheets::AUTH_SERVICE_NAME;
$httpClient = Zend_Gdata_ClientLogin::getHttpClient($user, $pass, $authService);
$gdClient = new Zend_Gdata_Spreadsheets($httpClient);
$query = new Zend_Gdata_Spreadsheets_DocumentQuery();
$query->setSpreadsheetKey($key);
$feed = $gdClient->getWorksheetFeed($query);
print_r($feed);
In the following line, the HTTP client is optional:
$gdClient = new Zend_Gdata_Spreadsheets($httpClient);
So, just don't pass it. The following are equivalent:
$gdClient = new Zend_Gdata_Spreadsheets();
// or
$gdClient = new Zend_Gdata_Spreadsheets(null);
// or
$gdClient = new Zend_Gdata_Spreadsheets(new Zend_Http_Client());
Like #Matt, I wanted to access a public spreadsheet without supplying credentials. Thanks to #Derek Illchuk, I got part of the way there. It still wasn't working, however, until I learned the following:
Note that the File > Publish to the Web feature is not the same thing as Sharing Settings > Public On The Web. If you forget to enable "Publish to the Web", you'll get this error: "Expected response code 200, got 400 The spreadsheet at this URL could not be found. Make sure that you have the right URL and that the owner of the spreadsheet hasn't deleted it."
In the "Publish to the Web" settings, be sure to uncheck "Require viewers to sign in with their ___ account.". Otherwise you'll get this error: "Expected response code 200, got 403 You do not have view access to the spreadsheet. Make sure you are properly authenticated."
According to Google's documentation, "The spreadsheets feed only supports the 'private' visibility and the 'full' projection." However, I found that I needed to specify 'public' visibility and 'basic' projection. Otherwise I got this error:
"Expected response code 200, got 501 Bad or unsupported projection for this type of operation."
Here is what worked for me:
$spreadsheetService = new Zend_Gdata_Spreadsheets(null);
$query = new Zend_Gdata_Spreadsheets_CellQuery();
$query->setSpreadsheetKey($spreadsheetKey);
$query->setWorksheetId($worksheetId);
$query->setVisibility('public'); //options are 'private' or 'public'
$query->setProjection('basic'); //options are 'full' or 'basic'
$cellFeed = $spreadsheetService->getCellFeed($query);
foreach ($cellFeed as $cellEntry) {
$text = $cellEntry->content->text;
//Do something
break; //I only wanted the first cell (R1C1).
}