This article is part of #25DaysOfServerless. New challenges will be published every day from Microsoft Cloud Advocates throughout the month of December. Find out more about how Microsoft Azure enables your Serverless functions.
Have an idea or a solution? Share your thoughts on Twitter!
So, an evil grinch has stolen all the servers in the world, and we have to visit many countries to set things right! Now we are in Guatemala, and here, December 7 marks the first day of the official Christmas season. Everybody is scrambling to get ready for the big la quema del diablo (burning of the devil) tonight β at 6pm sharp, everyone will start a bonfire to burn rubbish and items they don't need to cleanse their homes of evil.
Our friend Miguel is concerned about the environmental impact! The past few years, people have been burning a lot of rubber and plastic that makes the air dirty. Some places are switching to burning paper piΓ±atas of the devil, but Miguel still wants to let people metaphorically cleanse their houses of specific items they don't want.
Let's help Miguel by building a web API that lets his neighbors search for images of things they want to get rid of. Build an application (e.g. a cloud function with a single endpoint) that takes text as an input and returns an image found on unsplash or another image platform.
Tip
Make sure to keep your keys private. You can profit from environment variables to do so;
export UNSPLASH_ACCESS_KEY="your_access_key"
export UNSPLASH_SECRET_KEY="your_secret_key"
A Solution
As a Java Developer I created my Azure Function with Java
and maven using Java8. Starting this way, an example http trigger plus test will be generated automatically which can be used and adapted to the challenge.
The generated Http-Trigger provides an easy "Hello Name" example where Name changes due to the given parameters.
import java.util.*;
import com.microsoft.azure.functions.annotation.*;
import com.microsoft.azure.functions.*;
/**
* Azure Functions with HTTP Trigger.
*/
public class Function {
/**
* This function listens at endpoint "/api/HttpTrigger-Java". Two ways to invoke it using "curl" command in bash:
* 1. curl -d "HTTP Body" {your host}/api/HttpTrigger-Java&code={your function key}
* 2. curl "{your host}/api/HttpTrigger-Java?name=HTTP%20Query&code={your function key}"
* Function Key is not needed when running locally, it is used to invoke function deployed to Azure.
* More details: https://aka.ms/functions_authorization_keys
*/
@FunctionName("HttpTrigger-Java")
public HttpResponseMessage run(
@HttpTrigger(name = "req", methods = {HttpMethod.GET, HttpMethod.POST}, authLevel =
AuthorizationLevel.FUNCTION) HttpRequestMessage<Optional<String>> request,
final ExecutionContext context) {
context.getLogger().info("Java HTTP trigger processed a request.");
// Parse query parameter
String query = request.getQueryParameters().get("name");
String name = request.getBody().orElse(query);
if (name == null) {
return request.createResponseBuilder(HttpStatus.BAD_REQUEST)
.body("Please pass a name on the query string or in the request body").build();
} else {
return request.createResponseBuilder(HttpStatus.OK).body("Hello, " + name).build();
}
}
}
Before you begin modifying your generated function, register on unsplash.com and declare your function app at the developer section to get the keys necessary. Those keys got stored in my environment variables for local testing via the command line tool as well as in the local.settings.json following the format.
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "",
"FUNCTIONS_WORKER_RUNTIME": "java",
"UNSPLASH_ACCESS_KEY": "your unsplash access key",
"UNSPLASH_SECRET_KEY": "your unsplash secret key"
}
}
For the deployment in Azure, make sure to set the keys as environment variables as well.
So, let's get back to our generated function. To extract the query from the Http-Trigger some minor adjustments are necessary. As we want to allow the people to search not only for one but also for more than one keywords, we need to replace all blanks with the URL-readable format "%20". In order to keep the query for a human readable output, save the keywords in another variable separated by commas.
final String query = request.getQueryParameters().get("text");
String text = request.getBody().orElse(query);
String resultText;
if (text == null) {
return request.createResponseBuilder(HttpStatus.BAD_REQUEST).body("Please pass a text on the query string or in the request body to search for").build();
}
if (text.contains(" ")) {
resultText = text.replace(" ", ", ");
text = text.replace(" ", "%20");
} else {
resultText = text;
}
Concatenate the url to access the unsplash api with your keys.
BufferedReader reader;
String line;
StringBuilder responseContent = new StringBuilder();
// Get environment variables and access unsplash
String ACCESS_KEY = System.getenv("UNSPLASH_ACCESS_KEY");
String SECRET_KEY = System.getenv("UNSPLASH_SECRET_KEY");
StringBuilder urlBuilder = new StringBuilder();
urlBuilder.append("https://api.unsplash.com/search/photos?client_id=");
urlBuilder.append(ACCESS_KEY);
urlBuilder.append("&client_secret=");
urlBuilder.append(SECRET_KEY);
urlBuilder.append("&query=");
urlBuilder.append(text);
urlBuilder.append("&count=1");
Having the url we just need to open a connection and read the response content. Make sure to set timeouts to prevent your http request running forever and to disconnect your connection afterwards. Yes, with a newer JavaSDK you can also use the apache http client but in this solution we are showcasing a working solution also for Java8.
try {
URL url = new URL(urlBuilder.toString());
connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setConnectTimeout(5000);
connection.setReadTimeout(5000);
int responseCode = connection.getResponseCode();
if (responseCode > 299) {
reader = new BufferedReader(new InputStreamReader(connection.getErrorStream()));
} else {
reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
}
while ((line = reader.readLine()) != null) {
responseContent.append(line);
}
reader.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
connection.disconnect();
}
Finally, we need to parse the response of our unsplash api request. In this solution JSONObject and JSONArray from the org.json library are used. Another well known and good used JSON parser would be json-simple.
JSONObject answer = new JSONObject(responseContent.toString());
JSONArray results = answer.getJSONArray("results");
String smallImageUrl = results.getJSONObject(0).getJSONObject("urls").getString("small");
Last but not least, give back the image or the image url to the people from Guatemala.
// return image url
return request.createResponseBuilder(HttpStatus.OK).body("Search for image with keywords: " + resultText + ". Got url: " + smallImageUrl).build();
Thank you for saving the environment by not letting them burn the stuff they want to get rid of but saving their cultural integrity with this alternative.
We wish you a peaceful holiday season and a happy new year.
Want to submit your solution to this challenge? Build a solution locally and then submit an issue. If your solution doesn't involve code you can record a short video and submit it as a link in the issue desccription. Make sure to tell us which challenge the solution is for. We're excited to see what you build! Do you have comments or questions? Add them to the comments area below.
Watch for surprises all during December as we celebrate 25 Days of Serverless. Stay tuned here on dev.to as we feature challenges and solutions! Sign up for a free account on Azure to get ready for the challenges!
Top comments (0)