arrow_back

Develop and Secure APIs with Apigee X: Challenge Lab

Join Sign in
Test and share your knowledge with our community!
done
Get access to over 700 hands-on labs, skill badges, and courses

Develop and Secure APIs with Apigee X: Challenge Lab

Lab 2 hours 30 minutes universal_currency_alt 5 Credits show_chart Intermediate
Test and share your knowledge with our community!
done
Get access to over 700 hands-on labs, skill badges, and courses

GSP363

Google Cloud self-paced labs logo

Overview

In a challenge lab you’re given a scenario and a set of tasks. Instead of following step-by-step instructions, you will use the skills learned from the labs in the course to figure out how to complete the tasks on your own! An automated scoring system (shown on this page) will provide feedback on whether you have completed your tasks correctly.

When you take a challenge lab, you will not be taught new Google Cloud concepts. You are expected to extend your learned skills, like changing default values and reading and researching error messages to fix your own mistakes.

To score 100% you must successfully complete all tasks within the time period!

This lab is recommended for students who have completed the labs in the Develop and Secure APIs with Apigee X course. Are you ready for the challenge?

Setup

Before you click the Start Lab button

Read these instructions. Labs are timed and you cannot pause them. The timer, which starts when you click Start Lab, shows how long Google Cloud resources will be made available to you.

This hands-on lab lets you do the lab activities yourself in a real cloud environment, not in a simulation or demo environment. It does so by giving you new, temporary credentials that you use to sign in and access Google Cloud for the duration of the lab.

To complete this lab, you need:

  • Access to a standard internet browser (Chrome browser recommended).
Note: Use an Incognito or private browser window to run this lab. This prevents any conflicts between your personal account and the Student account, which may cause extra charges incurred to your personal account.
  • Time to complete the lab---remember, once you start, you cannot pause a lab.
Note: If you already have your own personal Google Cloud account or project, do not use it for this lab to avoid extra charges to your account.

Challenge scenario

You are a Cloud Engineer for Cymbal Shops, a national retailer. Cymbal Shop is focused on global sales, and translation services have been identified as a key tool to help expand the global business. You are responsible for creating the first version of a translation API.

You are expected to have the skills and knowledge for these tasks, so step-by-step guides are not provided.

Your challenge

You will create a new Apigee API proxy and other resources in the project's Apigee organization. Read through each task description, and create the required functionality.

Save errors

When you save changes to your API proxy, you might encounter a Could not save new revision error. If you use the Save dropdown button (Save dropdown button), and then select Save as new revision, you should see an error message that tells you what is invalid.

Task 1. Proxy the Cloud Translation API

Cymbal Shops has decided to use Google Cloud's Translation API as the backend service for the API proxy.

Requirements:

  1. In the Google Cloud Console, confirm that the Cloud Translation API is enabled in the API Library.
  2. Create a service account for the API proxy named apigee-proxy, and grant it the role Logging > Logs Writer.
  3. In the Google Cloud Console,from the Navigation menu, select Apigee to open the Apigee UI and create your API proxy.
  4. The API proxy should be a reverse proxy named translate-v1, with a base path of /translate/v1.
  5. The API proxy's target is the HTTP URL for the basic version of the Cloud Translation API (https://translation.googleapis.com/language/translate/v2).
  6. Do not add authorization, CORS, or a quota using the Common Policies page of the proxy wizard.
  7. On the summary page, create the API proxy by leaving the settings at their defaults.
  8. Add an Authentication section to the default TargetEndpoint, causing an access token to be sent with every backend request. Use a GoogleAccessToken element with a Scope of https://www.googleapis.com/auth/cloud-translation.
Note: Edit the proxy and in the Develop tab, under Target endpoints section, edit the default.xml file.
  1. Use the following Cloud Shell script to confirm that the Apigee runtime is completely installed:
export INSTANCE_NAME=eval-instance; export ENV_NAME=eval; export PREV_INSTANCE_STATE=; echo "waiting for runtime instance ${INSTANCE_NAME} to be active"; while : ; do export INSTANCE_STATE=$(curl -s -H "Authorization: Bearer $(gcloud auth print-access-token)" -X GET "https://apigee.googleapis.com/v1/organizations/${GOOGLE_CLOUD_PROJECT}/instances/${INSTANCE_NAME}" | jq "select(.state != null) | .state" --raw-output); [[ "${INSTANCE_STATE}" == "${PREV_INSTANCE_STATE}" ]] || (echo; echo "INSTANCE_STATE=${INSTANCE_STATE}"); export PREV_INSTANCE_STATE=${INSTANCE_STATE}; [[ "${INSTANCE_STATE}" != "ACTIVE" ]] || break; echo -n "."; sleep 5; done; echo; echo "instance created, waiting for environment ${ENV_NAME} to be attached to instance"; while : ; do export ATTACHMENT_DONE=$(curl -s -H "Authorization: Bearer $(gcloud auth print-access-token)" -X GET "https://apigee.googleapis.com/v1/organizations/${GOOGLE_CLOUD_PROJECT}/instances/${INSTANCE_NAME}/attachments" | jq "select(.attachments != null) | .attachments[] | select(.environment == \"${ENV_NAME}\") | .environment" --join-output); [[ "${ATTACHMENT_DONE}" != "${ENV_NAME}" ]] || break; echo -n "."; sleep 5; done; echo "***ORG IS READY TO USE***";

When the script returns ORG IS READY TO USE, you can proceed to next steps.

Note: If you are waiting for the runtime to install, you may read ahead and plan your development for Task 2.
  1. Save and deploy the translate-v1 proxy to the eval environment using the following service account:
apigee-proxy@{{{ project_0.project_id | PROJECT }}}.iam.gserviceaccount.com
  1. Test the API proxy.

The eval environment in the Apigee organization can be called using the hostname eval.example.com. This DNS entry is only available in the internal network, so you must use a VM that has been created for you.

  1. In Cloud Shell, open an SSH connection to apigeex-test-vm:
TEST_VM_ZONE=$(gcloud compute instances list --filter="name=('apigeex-test-vm')" --format "value(zone)") gcloud compute ssh apigeex-test-vm --zone=${TEST_VM_ZONE} --force-key-file-overwrite
  1. If asked to authorize, click Authorize. For each question asked in the gcloud command, click Enter or Return to specify the default input.

  2. Once you have successfully completed Task 1, the following curl command should translate the text:

curl -i -k -X POST "https://eval.example.com/translate/v1" -H "Content-Type: application/json" -d '{ "q": "Translate this text!", "target": "es" }'

The response should look similar to this:

{ "data": { "translations": [ { "translatedText": "¡Traduce este texto!", "detectedSourceLanguage": "en" } ] } } Note: For a few minutes, you may receive a 502 error response until your API proxy is fully deployed.

Click Check my progress to verify the objective. Proxy the Cloud Translation API

Note: If a green check mark isn't displayed, click the score fly-out on the upper-right, and then click Check my progress on the relevant step. A pop-up box will give you advice.

Task 2. Change the API request and response

Cymbal Shops wants to create an API that is different from the interface provided by the Translation API. There are two Translation API calls that should be modified.

The first call retrieves a list of valid languages.

Cloud Translation API request:

REQUEST: POST https://translation.googleapis.com/language/translate/v2/languages Authorization: Bearer ACCESSTOKEN Content-Type: application/json { "target": "en" }

Cloud Translation API response:

Content-Type: application/json { "data": { "languages": [ { "language": "af", "name": "Afrikaans" }, { "language": "sq", "name": "Albanian" }, ... ] } }

translate-v1 request:

GET https://eval.example.com/translate/v1/languages

translate-v1 response:

Content-Type: application/json [{"language":"af","name":"Afrikaans"},{"language":"sq","name":"Albanian"}, ... ]

The API proxy must replace the GET with a POST, remove the data and languages response fields, and get the target language code from a property set. The access token was added automatically by the Authentication section in Task 1.

The second call translates text to a specified language.

Cloud Translate API request:

POST https://translation.googleapis.com/language/translate/v2 Authorization: Bearer ACCESSTOKEN Content-Type: application/json { "q": "Hello world!", "target": "de" }

Cloud Translate API response:

Content-Type: application/json { "data": { "translations": [ { "translatedText": "Hallo Welt!", "detectedSourceLanguage": "en" } ] } }

translate-v1 request:

POST https://eval.example.com/translate/v1?lang=de Content-Type: application/json { "text": "Hello world!" }

translate-v1 response:

Content-Type: application/json { "translated": "Hallo Welt!" }

The API proxy must take the target language from the lang query parameter, and change the field names for the incoming and translated text. The lang query parameter may optionally be omitted from the translate-v1 request, in which case the target language will be taken from a property in the property set.

Note: The Translation API accepts either a single string or an array of strings for the 'q' field. Your API should only support a single string.

Requirements:

  1. Within the API proxy, create a property set named language.properties. The property set should have two properties: output with a value of es, and caller with a value of en. The caller property will be used to specify the target language when listing languages (the language used for the name field). output will specify the default target language to be used if the lang query parameter is not provided.

  2. In the proxy endpoint, create a path and verb conditional flow for the POST / resource. Name it translate.

  3. In the proxy endpoint, create a path (no verb) conditional flow for the /languages resource. Name it getLanguages. (Do not include a verb. You will be modifying the request's verb from GET (for the input to the proxy) to POST (required by the backend). If you include a verb in the condition, response policies in the flow would not execute because request.verb is no longer equal to GET.)

  4. Create an AssignMessage policy named AM-BuildTranslateRequest to create the backend request used in the translate conditional flow.

The policy should include:

  • An AssignVariable with a template to create variables which will be used later in a logged message. The variable named text should use the jsonPath message template function to extract the text field from the request.

  • The variable named language should be created by using the firstnonnull message template function. This variable should contain the lang query parameter value if it exists, and the language property set's output property for the target language if the lang query parameter has not been specified.

  • A Set section should be used to set the JSON payload required by the backend service. Both variables you have created will be used in the payload.

  • The [AssignTo] element should use the existing request message.

    The AssignVariable sections in the AssignMessage policy should look similar to this:

    <AssignVariable> <Name>...</Name> <Template>...</Template> <AssignVariable>
  1. Create an AssignMessage policy named AM-BuildTranslateResponse under translate conditional flow to create the response for the caller using the Translation API response.

The policy should include:

  • An AssignVariable with a jsonPath template to create a variable named translated, extracting the translatedText field from the Translation API response. Hint: the JSONPath expression to extract this field is $.data.translations[0].translatedText.

  • Set createNew to true.

  • The new JSON payload will use the translated variable.

    The AssignVariable sections in the AssignMessage policy should look similar to this:

    <AssignVariable> <Name>...</Name> <Template>...</Template> <AssignVariable>
  1. Create an AssignMessage policy named AM-BuildLanguagesRequest to create the backend request used in the getLanguages conditional flow.

The policy should include:

  • Use Set to set the correct verb and payload for the backend request.

  • The caller property in the language property set should be used for the target field in the backend payload.

  • Set createNew to true.

  • Set the backend request payload to have a content type of application/json.

    The AssignVariable sections in the AssignMessage policy should look similar to this:

    <AssignVariable> <Name>...</Name> <Set> ... </Set> </AssignVariable>
  1. Create a JavaScript policy named JS-BuildLanguagesResponse under getLanguages conditional flow to create the response for the caller. The JavaScript code should use these steps:

Your JavaScript code should look similar to this:

var payload = ...; var payloadObj = JSON.parse(...); var newPayload = JSON.stringify(...); context.setVariable(...); Note: Make sure to create the desired policies under their correct conditional flows and edit the desired policy configurations in respective .xml files.
  1. Test the API. From the apigeex-test-vm virtual machine, use the following curl commands to test the examples shown above:
  • List of languages:

    curl -i -k -X GET "https://eval.example.com/translate/v1/languages"
  • Translate to specified language (German):

    curl -i -k -X POST "https://eval.example.com/translate/v1?lang=de" -H "Content-Type:application/json" -d '{ "text": "Hello world!" }'
  • Translate to default language (Spanish):

    curl -i -k -X POST "https://eval.example.com/translate/v1" -H "Content-Type:application/json" -d '{ "text": "Hello world!" }'

Click Check my progress to verify the objective. Change the API request and response

Note: If a green check mark isn't displayed, click the score fly-out on the upper-right, and then click Check my progress on the relevant step. A pop-up box will give you advice.

Task 3. Add API key verification and quota enforcement

Access to this API should be limited to approved applications, so you will add a VerifyAPI key policy, as well as a Quota policy to limit the number of requests.

Requirements:

  1. Create an API product with a name and display name of translate-product. This API product should have public access, automatically approve access requests, and be available in the eval environment.

  2. Add an operation to the translate-product API product. The operation should allow access to the translate-v1 proxy and use a path of /, which allows access to any request, including /. Allowed methods are GET and POST. Add an operation quota setting of 10 requests per 1 minute.

  3. Create a developer with the email joe@example.com. Choose your own first name, last name, and username.

  4. Create an app called translate-app, and enable the translate-product API product for it. You will need to associate it with your joe@example.com developer.

  5. Add a VerifyAPIKey policy named VAK-VerifyKey to the proxy endpoint preflow. The API key should be required for every request, and should be sent in using the Key header.

  6. Add a Quota policy named Q-EnforceQuota to the proxy endpoint preflow.

The policy should include the steps:

  • Use a type of calendar. The calendar type requires a StartTime element.
  • Specify UseQuotaConfigInAPIProduct to use the quota from the API product, with a default quota of 5 requests every one hour if the API product does not specify quota settings.
  • Set Distributed and Synchronous to true, and remove the AsynchronousConfiguration element.

Once these changes have been made, the request should return an error if a valid API key is not specified in the Key header.

  1. From the apigeex-test-vm virtual machine, use the following curl commands to test the API key functionality:
  • Fails (no API key):

    curl -i -k -X POST "https://eval.example.com/translate/v1?lang=de" -H "Content-Type:application/json" -d '{ "text": "Hello world!" }'
  • Fails (invalid API key):

    curl -i -k -X POST "https://eval.example.com/translate/v1?lang=de" -H "Content-Type:application/json" -H "Key: ABC123" -d '{ "text": "Hello world!" }'
  • Succeeds (when KEY variable is set to a valid API key KEY=<get this from the earlier step when setting up a Developer App>):

    curl -i -k -X POST "https://eval.example.com/translate/v1?lang=de" -H "Content-Type:application/json" -H "Key: $KEY" -d '{ "text": "Hello world!" }'

Click Check my progress to verify the objective. Add API key verification and quota enforcement

Note: If a green check mark isn't displayed, click the score fly-out on the upper-right, and then click Check my progress on the relevant step. A pop-up box will give you advice.

Task 4. Add message logging

To understand how the translation service is being used, a MessageLogging policy will log each translated message.

Requirements:

  1. Add a MessageLogging policy named ML-LogTranslation to the translate conditional flow. The policy must execute after the AM-BuildTranslateResponse step.
Note: Don't add it to the PostClientFlow because logs are only created for the translation operation.
  1. The policy should log to Cloud Logging. Use this policy documentation.
  • The LogName value should be:

    projects/{organization.name}/logs/translate
  • The logged message should have a contentType of text/plain, and the message contents should be:

    {language}|{text}|{translated}

This message requires the language, text, and translated variables that were created in the AM-BuildTranslateRequest and AM-BuildTranslateResponse policies.

  1. Validate the logged messages in the Logging page of the Google Cloud Console. Use the query logName : "translate" to see only the translated logs.

  2. Once the MessageLogging policy has been successfully added, use the following curl command:

curl -i -k -X POST "https://eval.example.com/translate/v1?lang=de" -H "Content-Type:application/json" -H "Key: $KEY" -d '{ "text": "Hello world!" }'
  • That curl command should create a log message with the following contents:

    de|Hello world!|Hallo Welt!
Note: There is a short delay before a logged message appears in the logs.

Click Check my progress to verify the objective. Add message logging

Note: If a green check mark isn't displayed, click the score fly-out on the upper-right, and then click Check my progress on the relevant step. A pop-up box will give you advice.

Task 5. Rewrite a backend error message

When the target parameter sent to the Translation API is invalid, a 400 Bad Request error message is returned:

{ "error": { "code": 400, "message": "Invalid Value", "errors": [ { "message": "Invalid Value", "domain": "global", "reason": "invalid" } ] } }

This error message would be confusing to the caller, so you will rewrite the error message.

Requirements:

  1. Add a FaultRules section to the default target endpoint. When the backend returns a 400 response, it will automatically evaluate any matching fault rules in the target endpoint.
Note: The UI Navigator menu on the left cannot be used to add a FaultRules section. You must add it in the target endpoint's XML configuration.
  1. Add a FaultRule to the FaultRules section. The Condition of this FaultRule should be set so that it executes if fault.name is ErrorResponseCode.

  2. Create an AssignMessage policy named AM-BuildErrorResponse and attach it to the FaultRule. Use the following policy configuration:

<AssignMessage name="AM-BuildErrorResponse"> <Set> <Payload contentType="application/json">{ "error": "Invalid request. Verify the lang query parameter." }</Payload> </Set> </AssignMessage>

Once the policy is attached, your FaultRules section in the target endpoint should look similar to this:

<FaultRules> <FaultRule name="..."> <Step> <Name>...</Name> </Step> <Condition>...</Condition> </FaultRule> </FaultRules> Note: You need to manually edit the Target Endpoint XML (make sure it is the TargetEndpoint, not the ProxyEndpoint).
  1. Test the API.
  • A valid request like this should still work:

    curl -i -k -X POST "https://eval.example.com/translate/v1?lang=de" -H "Content-Type:application/json" -H "Key: $KEY" -d '{ "text": "Hello world!" }'
  • An invalid language query parameter like this should return the rewritten error message:

    curl -i -k -X POST "https://eval.example.com/translate/v1?lang=invalid" -H "Content-Type:application/json" -H "Key: $KEY" -d '{ "text": "Hello world!" }'

Click Check my progress to verify the objective. Rewrite a backend error message

Note: If a green check mark isn't displayed, click the score fly-out on the upper-right, and then click Check my progress on the relevant step. A pop-up box will give you advice.

Congratulations!

Over the course of this challenge lab you have demonstrated your knowledge of Apigee X API development and security.

Develop and Secure APIs with Apigee X Badge

Earn your next skill badge

This self-paced lab is part of the Develop and Secure APIs with Apigee X quest. Completing this skill badge quest earns you the badge above, to recognize your achievement. Share your badge on your resume and social platforms, and announce your accomplishment using #GoogleCloudBadge.

This skill badge quest is part of Google Cloud’s API Developer learning path. Continue your learning journey by enrolling in the Deploy and Manage Apigee X quest.

Google Cloud training and certification

...helps you make the most of Google Cloud technologies. Our classes include technical skills and best practices to help you get up to speed quickly and continue your learning journey. We offer fundamental to advanced level training, with on-demand, live, and virtual options to suit your busy schedule. Certifications help you validate and prove your skill and expertise in Google Cloud technologies.

Manual Last Updated March 13, 2024

Lab Last Tested March 13, 2024

Copyright 2024 Google LLC All rights reserved. Google and the Google logo are trademarks of Google LLC. All other company and product names may be trademarks of the respective companies with which they are associated.