diff --git a/modules/tutorials/pages/rest-api/rest-api-intro.adoc b/modules/tutorials/pages/rest-api/rest-api-intro.adoc index 84ce54ee8..50cc0aa39 100644 --- a/modules/tutorials/pages/rest-api/rest-api-intro.adoc +++ b/modules/tutorials/pages/rest-api/rest-api-intro.adoc @@ -4,7 +4,7 @@ :page-title: ThoughtSpot REST API tutorials :page-pageid: rest-api__intro -:page-description: This lesson covers the security setup necessary to embed ThoughtSpot into TSE applications. +:page-description: Hands-on tutorial for using ThoughtSpot REST API V2.0 with Python and JavaScript. This tutorial is a hands-on guide to practically working with the ThoughtSpot V2.0 REST API. @@ -15,18 +15,18 @@ Once completed, you will be able to use the ThoughtSpot V2.0 REST API via Python 1. xref:rest-api_lesson-01.adoc[REST API Overview] 2. xref:rest-api_lesson-02.adoc[Simple Python implementation] 3. xref:rest-api_lesson-03.adoc[Complex REST API Workflows] -4. xref:rest-api_lesson-04.adoc[Browser JavaScript REST API implementation] +4. xref:rest-api_lesson-04.adoc[Browser JavaScript REST API implementation] // 5. TypeScript SDK -== Pre-requisites for the tutorial +== Prerequisites for the tutorial Your setup must have the following applications and tools installed: * Chrome or Firefox * Python 3.8 or higher * link:https://code.visualstudio.com/[Visual Studio Code, window=_blank] or link:https://www.jetbrains.com/pycharm/[PyCharm, window=_blank] -* link:https://github.com/thoughtspot/thoughtspot_rest_api_v1_python[The `thoughtspot_rest_api_v1` GitHub repo, window=_blank] library (capable of REST API V1 and V2.0) in your local Python environment +* link:https://github.com/thoughtspot/thoughtspot_rest_api_python[The `thoughtspot_rest_api` GitHub repository, window=_blank] (supports REST API V1 and V2.0) in your local Python environment -Download the entire link:https://github.com/thoughtspot/tse-api-tutorial[tse-api-tutorial directory^] as a ZIP file from GitHub, then unzip on your system: +Download the entire link:https://github.com/thoughtspot/tse-api-tutorial[tse-api-tutorial repository, window=_blank] as a ZIP file from GitHub, then unzip it on your system: [.widthAuto] [.bordered] diff --git a/modules/tutorials/pages/rest-api/rest-api_lesson-02.adoc b/modules/tutorials/pages/rest-api/rest-api_lesson-02.adoc index 84678bf43..3f551f338 100644 --- a/modules/tutorials/pages/rest-api/rest-api_lesson-02.adoc +++ b/modules/tutorials/pages/rest-api/rest-api_lesson-02.adoc @@ -11,7 +11,7 @@ We'll use the files from the link:https://github.com/thoughtspot/tse-api-tutoria . Open your IDE + Visual Studio Code will be used in all images and instructions. -The files for this lesson are `api_training_python_1_begin.py` and `api_training_python_1_end.py` +The files for this lesson are `api_training_python_1_begin.py` and `api_training_python_1_end.py`. . Open up your command line or terminal environment as well. [NOTE] @@ -24,7 +24,7 @@ Please adjust all Python commands according to your own environment. == 01 - Imports and variables At the top of `api_training_python_1_begin.py`, there is a set of *imports* and *variables* that configure the overall script. -=== Import Requests library +=== Import Requests library To issue HTTP commands in a given programming language, you must use a library that sends and receives HTTP. For this lesson, we'll use link:https://requests.readthedocs.io/en/latest/[Requests, window=_blank], the most commonly used high-level HTTP library for Python. @@ -38,11 +38,11 @@ import json ---- ==== Install requests package -If you followed the prerequisites and installed the link:https://github.com/thoughtspot/thoughtspot_rest_api_v1_python[thoughtspot_rest_api_v1 package, window=_blank] using your command line or terminal: +If you followed the prerequisites and installed the link:https://github.com/thoughtspot/thoughtspot_rest_api_python[thoughtspot_rest_api package, window=_blank] using your command line or terminal: [code,bash] ---- -pip install thoughtspot_rest_api_v1 +pip install thoughtspot_rest_api ---- The `requests` package should have automatically been installed along with the other requirements. @@ -57,7 +57,7 @@ pip install requests --upgrade === Set global variables It is very convenient to declare global variables at the beginning of a script so that they can be reused throughout. -The URL of the ThoughtSpot instance is always necessary to send a REST API command, and if the instance has Orgs enabled, you need to send the `org_id` when you request a login token. We'll set those as global variables along with the `api_version` (which hasn't changed ever at this point). +The URL of the ThoughtSpot instance is always necessary to send a REST API command, and if the instance has Orgs enabled, you need to send the `org_id` when you request a login token. We'll set those as global variables along with the `api_version` (which has remained unchanged so far). [,python] ---- @@ -82,13 +82,13 @@ api_headers = { } ---- -== 02 - Use a Session object +== 02 - Use a Session object -Rather than set the full configuration of each HTTP request you make, you can construct a `Session` object from the `requests` library, which an open HTTP connection and maintains settings like headers and cookies between individual HTTP actions. +Rather than setting the full configuration for each HTTP request, you can construct a `Session` object from the `requests` library, which keeps an open HTTP connection and maintains settings like headers and cookies between individual HTTP actions. You send HTTP commands by calling the `.get()`, `.post()`, `.put()` and `.delete()` methods of the `Session` object. -All of those methods will return a Response object, which you assign to a variable to do further processing. This will look something the following: +All of those methods return a Response object, which you assign to a variable for further processing. This will look something like the following: [,python] ---- @@ -109,7 +109,7 @@ resp = requests_session.post(url=url, json=json_post_data) ---- == 03 - Authenticate into ThoughtSpot REST API -There are two ways of establishing authentication with ThoughtSpot's REST API, `cookie-based` with session cookies and `cookieless` using a bearer token in the headers. +There are two ways to establish authentication with ThoughtSpot's REST API: `cookie-based` with session cookies, and `cookieless` using a bearer token in the headers. For backend scripts, we prefer the bearer token approach: @@ -123,8 +123,8 @@ In the REST API V2.0 Playground: . Go to *Authentication* > *Get Full Access Token*. . Specify the parameters. -. Copy the JSON body from the right side of the Playground, Python Dict uses the same syntax, but you must update the booleans to be *uppercase*. -. Replace any of the hard-coded values with the *global variables* you declared so that you can change your requests in an easy way at the top of your script and make sure the values change in all the necessary places: +. Copy the JSON body from the right side of the Playground. Python dicts use the same syntax, but you must update booleans to be *uppercase*. +. Replace any hard-coded values with the *global variables* you declared so that you can easily update requests at the top of your script and ensure those values change everywhere they are used: + [,python] ---- @@ -146,7 +146,7 @@ json_post_data = { We expect a JSON response on success, which you can access using the `.json()` method of the `Response` object. + -From the Playground, we can see that there is `token` property in the response. +From the Playground, we can see that there is a `token` property in the response. . Create a variable for the `token` value to use in the headers as the Bearer token. + @@ -178,7 +178,7 @@ Almost all REST API endpoints other than the token requests require authenticati You need to update the `Session` object with this new header while keeping the original ones. -Use the `token` variable from above to form the exact header to update the original `api_headers` Dict, then use the `.headers.update()` method of the `Session` object: +Use the `token` variable from above to form the exact header to update the original `api_headers` dict, then use the `.headers.update()` method of the `Session` object: [,python] ---- @@ -203,7 +203,7 @@ Python code raises `link:https://docs.python.org/3/tutorial/errors.html[Exceptio If an `Exception` is raised and is not *handled*, the script exits and displays the message provided with the Exception and other details of what failed. -A `try...except block` encloses a set of lines that may result in a known `Exception` in the `try` portion, and then the `except` line defines which `Exception` type to listen for and how to proceed if the `Exception` is thrown. +A `try...except` block encloses a set of lines that may result in a known `Exception` in the `try` portion, and then the `except` line defines which `Exception` type to listen for and how to proceed if the `Exception` is thrown. Every HTTP request can potentially result in an error, and we don't want to continue within the script as planned if the expected action on the ThoughtSpot server did not complete correctly. @@ -227,7 +227,7 @@ The `requests` library does not raise an `Exception` when an HTTP request comple However, as you saw in the previous lesson, HTTP responses include a *Status Code* that indicates if the requested action was a *Success* or an *Error*. -To cause `Exceptions` if the response does not include a *Success* status code, call the `Response.raise_for_status()` method for each call, which will throw the specifc `requests.exceptions.HTTPError` `Exception` when a 400 series or 500 status code is returned: +To raise `Exceptions` when the response does not include a *Success* status code, call the `Response.raise_for_status()` method for each call, which throws the specific `requests.exceptions.HTTPError` `Exception` when a 400 series or 500 status code is returned: [source,python] ---- @@ -269,14 +269,14 @@ json_post_data = { } try: - # requests returns back Response object + # requests returns a Response object resp = requests_session.post(url=url, json=json_post_data) - # This method causes Python Exception to throw if status not 2XX + # This method raises a Python exception if status is not 2XX resp.raise_for_status() - # Retrieve the JSON body of response and convert into Dict - # Some endpoints returns 204 not 200 for success, with no body, will error if you call .json() method + # Retrieve the JSON body of the response and convert it into a dict + # Some endpoints return 204 instead of 200 for success, with no body, which errors if you call .json() resp_json = resp.json() # You can just print(resp_json) to see the Python Dict diff --git a/modules/tutorials/pages/rest-api/rest-api_lesson-03.adoc b/modules/tutorials/pages/rest-api/rest-api_lesson-03.adoc index e3944dbe8..a201ea354 100644 --- a/modules/tutorials/pages/rest-api/rest-api_lesson-03.adoc +++ b/modules/tutorials/pages/rest-api/rest-api_lesson-03.adoc @@ -8,27 +8,27 @@ == Get started The files for this tutorial are `api_training_python_2_begin.py` and `api_training_python_2_end.py`. -You must have installed the `thoughtspot_rest_api_v1` library per the prerequisites at the beginning into the Python environment you are using. Despite the name, the library has components for interacting with both the V1 and V2.0 ThoughtSpot REST APIs. +You must have installed the `thoughtspot_rest_api` library in the Python environment you are using, based on the tutorial prerequisites. Despite the name, the library has components for interacting with both the V1 and V2.0 ThoughtSpot REST APIs. [NOTE] ==== -* You'll need a ThoughtSpot account with administrator privileges to complete the following tutorial. -* We'll create a "Tag" and a "Group" and delete these at the end of the tutorial. +* You'll need a ThoughtSpot account with administrator privileges to complete the following tutorial. +* We'll create a "Tag" and delete it at the end of the tutorial. ==== == 01 - Use ThoughtSpot REST API library -The link:https://github.com/thoughtspot/thoughtspot_rest_api_v1_python[thoughtspot_rest_api_v1 library^] was originally created because the V1 ThoughtSpot REST API is uniformly structured, so a library with an implementation of each endpoint was created as a "reference" on how to format and send each request correctly. +The link:https://github.com/thoughtspot/thoughtspot_rest_api_python[thoughtspot_rest_api library^] was originally created because the V1 ThoughtSpot REST API is uniformly structured, so a library with an implementation of each endpoint was created as a reference for formatting and sending each request correctly. The V2.0 REST API is simple enough to implement in any language. We've just completed the initial steps in Python in the previous lesson. The V2.0 portion of the library implements the repeated standard steps everyone would have to do for themselves each time, and issues can be reported to and fixed in the library once for everyone. -The library encapsulates logic around constructing REST API requests correctly, so that you don’t have to rewrite code. +The library encapsulates logic around constructing REST API requests correctly, so that you don't have to rewrite code. -Endpoints are defined properly, along with HTTP request details and response handling. +Endpoints are defined properly, along with HTTP request details and response handling. -=== Import thoughtspot_rest_api_v1 library -Rather than helper functions like in JavaScript, the *thoughtspot_rest_api_v1* library provides two *classes* that represent the entire set of the two REST API versions: `TSRestApiV1` & `TSRestApiV2`. +=== Import thoughtspot_rest_api library +Rather than helper functions like in JavaScript, the `thoughtspot_rest_api` library provides two *classes* that represent the two REST API versions: `TSRestApiV1` and `TSRestApiV2`. *Classes* define how to build *Objects*, which combine data (called properties) and functions (called methods). @@ -38,7 +38,7 @@ To get started, let's import all of the classes from the library and then create [,python] ---- -from thoughtspot_rest_api_v1 import * +from thoughtspot_rest_api import * username = 'username' password = 'password' @@ -48,11 +48,11 @@ ts: TSRestApiV2 = TSRestApiV2(server_url=server) ---- == 02 - Authentication -The `TSRestApiV2` object doesn’t automatically log in a user. You must explicitly request an authentication token and set the `TSRestApiV2.bearer_token` property: +The `TSRestApiV2` object doesn't automatically log in a user. You must explicitly request an authentication token and set the `TSRestApiV2.bearer_token` property: [,python] ---- -from thoughtspot_rest_api_v1 import * +from thoughtspot_rest_api import * username = 'username' password = 'password' @@ -87,17 +87,17 @@ You'll notice that we've already accomplished everything we did in the previous All of the methods of the `TSRestApiV2` class are named after their equivalent REST endpoints, with an underscore character `_` replacing the forward slashes `/` from the URLs. -For example, `/users/search` endpoint is accessed via `TSRestApiV2.users_search()` method. +For example, the `/users/search` endpoint is accessed via the `TSRestApiV2.users_search()` method. If everything is installed and configured properly in your IDE, you should get auto-complete on the available endpoints as you type: image:images/tutorials/rest-api/autocomplete-in-ide.png[Autocomplete in IDE, width=475, height=229] -For endpoints that have only a few strictly defined arguments, the method will define Python arguments to match the endpoint’s arguments: +For endpoints that have only a few strictly defined arguments, the method will define Python arguments to match the endpoint's arguments: `users_delete(user_identifier:str)` -Endpoints with lots of request options simply take a `request=` argument, which expects a Python Dict matching the JSON request you see in the REST API Playground: +Endpoints with many request options simply take a `request=` argument, which expects a Python Dict matching the JSON request you see in the REST API Playground: image:images/tutorials/rest-api/json-request-format.png[JSON request format in Playground, width=521, height=445] @@ -131,9 +131,9 @@ We'll walk through the process of determining the steps for a sample task, and t Our example task is to *find all Liveboards and Answers with a name that includes '(Sample)' and tag them with the tag called 'Tutorial Test'*. === Define steps -It’s easiest to program by defining the exact requirements, breaking down those requirements into logical steps, and then writing the code accordingly. +It's easiest to program by defining the exact requirements, breaking them down into logical steps, and then writing the code accordingly. -Let’s split the task into discrete steps: +Let's split the task into discrete steps: 1. Find all Liveboards and Answers with a name that includes '(Sample)' 2. Add a tag called 'Tutorial Test' to all of the items @@ -153,7 +153,7 @@ Even this basic step opens up new questions as to what our exact requirements ar # 1. Find all Liveboards and Answers with a name that includes '(Sample)' # Get all of the items with names including '(Sample)' -# Is this a case-sensitive or insensitive operation? Are we finding anywhere in the name or just at start or end? +# Is this a case-sensitive or case-insensitive operation? Are we finding anywhere in the name or just at start or end? # 2. Add a tag to each item called 'Tutorial Test' @@ -199,7 +199,7 @@ The second task is: `Tags` have their own section in the Playground - `/tags/search` will help us find a tag by a particular name. -Look at the description of `tag_identifier` parameter of the request: "Name or Id of the tag". Almost every `_identifier` argument within the API works this way - it can take an object's *GUID* or the *name* property. +Look at the description of the `tag_identifier` parameter in the request: "Name or Id of the tag". Almost every `_identifier` argument within the API works this way - it can take an object's *GUID* or the *name* property. Our comments remind us to consider the situation where the `Tutorial Test` tag does not exist. @@ -235,7 +235,7 @@ search_request = { "type": "ANSWER" }, { - "name_pattern": "Sample)", + "name_pattern": "(Sample)", "type": "LIVEBOARD" } ], @@ -257,16 +257,16 @@ Remember the note about case-sensitivity? We can use Python's string methods to [,python] ---- -# Create List to hold the final set of Answers + Liveboards we want to tag and share -final_list_of_objs =[] +# Create a list to hold the final set of Answers + Liveboards we want to tag and share +final_list_of_objs = [] # Iterate through the results from the API response to double-check that the name value matches exactly for item in metadata_resp: m_name = item["metadata_name"] m_id = item["metadata_id"] - # Python string find is Case-Sensitive + # Python string find is case-sensitive if m_name.find("(Sample)") != -1: - final_list_of_objs.append(item) # We'll add the whole object to the new List + final_list_of_objs.append(item) # We'll add the whole object to the new list # optional print to command line to see what happened print(json.dumps(final_list_of_objs, indent=2)) @@ -274,10 +274,10 @@ print(json.dumps(final_list_of_objs, indent=2)) Next, we'll find the tag to apply using the `/tags/search` endpoint. -You'll notice that the autocomplete for the `TSRestApiV2.tags_search()` method shows defined arguments rather than a generic `request` argument. +You'll notice that the autocomplete for the `TSRestApiV2.tags_search()` method shows defined arguments rather than a generic `request` argument. When an endpoint has very few possibilities, the library often has the full set of arguments available directly. -image:images/tutorials/rest-api/tags-search-autocomplete.png[Assign tag, width=504, height=158] +image:images/tutorials/rest-api/tags-search-autocomplete.png[Tags search autocomplete, width=504, height=158] [source,python] ---- @@ -287,7 +287,7 @@ image:images/tutorials/rest-api/tags-search-autocomplete.png[Assign tag, width=5 # What if there is no tag called 'Tutorial Test'? # -# Find the Tag Identifer so we can assign +# Find the Tag Identifier so we can assign it # Create new Tag if it doesn't exist # try: @@ -318,7 +318,7 @@ else: Finally, we'll take the tag ID and the objects whose names matched and apply the tag. -Let's go back to the Playground to copy the request. Remember that the `metadata` section is not a simple array, but an array of objects: +Let's go back to the Playground to copy the request. Remember that the `metadata` section is not a simple array, but an array of objects: [,python] ---- @@ -344,10 +344,10 @@ We'll need to create the data structure that the `metadata` parameter needs by i try: # When we copied from the Playground, the format of the `metadata` section is an array of objects, - # which needs to be a List of Dicts in Python syntax [ {"identifier": metadata_id}, ...] - + # which needs to be a list of Dicts in Python syntax [ {"identifier": metadata_id}, ...] + tag_metadata_section = [] - # Iterate through each object and make the Dict in create format + # Iterate through each object and construct the Dict in the correct format for obj in final_list_of_objs: tag_metadata_section.append({"identifier" : obj['metadata_id']}) @@ -356,7 +356,7 @@ try: "tag_identifiers": [tag_id] } - assign_resp = ts.tags_assign(requst=assign_req) + assign_resp = ts.tags_assign(request=assign_req) except requests.exceptions.HTTPError as e: print("Error from the API: ") print(e) @@ -366,12 +366,12 @@ except requests.exceptions.HTTPError as e: == 05 - Conclusion -After completing these lessons, you should be very capable at using the REST API V2.0 Playground and the link:https://github.com/thoughtspot/thoughtspot_rest_api_v1_python[thoughtspot_rest_api_v1 library^] to retrieve and process the results of the `/search` endpoints and then issue other commands using the IDs of objects. +After completing these lessons, you should be very capable at using the REST API V2.0 Playground and the link:https://github.com/thoughtspot/thoughtspot_rest_api_python[thoughtspot_rest_api library^] to retrieve and process the results of the `/search` endpoints and then issue other commands using the IDs of objects. By moving hard-coded values into variables, you can develop reusable scripts to accomplish tasks that otherwise would require a lot of manual effort. -There are many link:https://github.com/thoughtspot/thoughtspot_rest_api_v1_python/tree/main/examples_v2[existing examples^] of workflows that can be pieced together to accomplish any number of administration and integration tasks. +There are many link:https://github.com/thoughtspot/thoughtspot_rest_api_python/tree/main/examples_v2[existing examples^] of workflows that can be pieced together to accomplish any number of administration and integration tasks. ''' -xref:rest-api_lesson-02.adoc[< Back: 02 - Simple Python Implementation of V2.0 REST API]| xref:rest-api_lesson-04.adoc[Next: 04 - Browser JavaScript REST API implementation >] +xref:rest-api_lesson-02.adoc[< Back: 02 - Simple Python implementation of V2.0 REST API] | xref:rest-api_lesson-04.adoc[Next: 04 - Browser JavaScript REST API implementation >]