diff --git a/modules/tutorials/pages/rest-api/rest-api-intro.adoc b/modules/tutorials/pages/rest-api/rest-api-intro.adoc index a1857d2c6..2d0ecd835 100644 --- a/modules/tutorials/pages/rest-api/rest-api-intro.adoc +++ b/modules/tutorials/pages/rest-api/rest-api-intro.adoc @@ -23,7 +23,7 @@ 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_python[The `thoughtspot_rest_api` GitHub repository, window=_blank] (supports 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), installed in your local Python environment with `python3 -m pip install thoughtspot_rest_api` 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: diff --git a/modules/tutorials/pages/rest-api/rest-api_lesson-01.adoc b/modules/tutorials/pages/rest-api/rest-api_lesson-01.adoc index dfb69b6cb..f8d5edcb7 100644 --- a/modules/tutorials/pages/rest-api/rest-api_lesson-01.adoc +++ b/modules/tutorials/pages/rest-api/rest-api_lesson-01.adoc @@ -51,7 +51,7 @@ Reports:: exports in a file format Security:: permissions and access control (sharing) Data:: exports in a JSON data format -Once you click on an individual endpoint, you will see the specification for that endpoint in the middle area, and an example of using cURL to issue the request to the endpoint properly on the left: +Once you click on an individual endpoint, you will see the specification for that endpoint in the middle area, and an example of using cURL to issue the request to the endpoint properly on the right: [.widthAuto] [.bordered] @@ -77,15 +77,15 @@ DELETE:: delete, often only has a URL (but some systems use a body) [NOTE] ==== -ThoughtSpot REST API V2.0 only uses *GET* and *POST* verbs. +ThoughtSpot REST API V2.0 mostly uses *POST* and *GET* verbs. A few endpoints use *PUT* or *DELETE*. The full endpoint name includes the action the endpoint will take, rather than the same endpoint responding to different HTTP verbs. ==== -Any endpoint that ends in `/search` is a `GET` request for querying information. +Most endpoints that end in `/search` are `POST` requests for querying information. The criteria for the search are sent as a JSON body, which is required even if it is empty (`{}`). === HTTP headers -Headers describe aspects of the request, and are mandatory for any given endpoint. +Headers describe aspects of the request, and some are required for any given endpoint. ThoughtSpot's V2.0 REST API typically requires the following headers: ---- @@ -98,7 +98,7 @@ The endpoints to request access tokens do not require the `Authorization` header Endpoints that return something other than a JSON response do not use the `Accept: application/json` header. === HTTP Body -Body is data sent with the request, that does not appear in the URL (is secure). The body will vary with each endpoint, and some endpoints do not require a body at all. +Body is data sent with the request, that does not appear in the URL. The body will vary with each endpoint, and some endpoints do not require a body at all. The body of all V2.0 REST API requests will be in JSON format. @@ -111,7 +111,7 @@ You can copy the JSON request from the Playground and use directly in JavaScript == 03 - HTTP responses Each HTTP request will result in some action within the web server and then a *response*. -For *GET* requests, the only expected action is a *response* with some element. For example, the `/search` endpoints within the V2.0 REST API are *GET* requests that return sets of information about the system. +For requests that only read information, the only expected action is a *response* with some element. For example, the `/search` endpoints within the V2.0 REST API are *POST* requests that return sets of information about the system without changing it. Other types of requests cause a state change on the server, and then a response describing the new state after the action has completed. 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 09465f8d9..276196a16 100644 --- a/modules/tutorials/pages/rest-api/rest-api_lesson-02.adoc +++ b/modules/tutorials/pages/rest-api/rest-api_lesson-02.adoc @@ -6,7 +6,7 @@ [IMPORTANT] ==== -The workflows and examples in this tutorial use a legacy version of the ThoughtSpot REST API link:https://github.com/thoughtspot/thoughtspot_rest_api_python[ThoughtSpot Community SDK, window=_blank]. We recommend using the xref:python-sdk.adoc[ThoughtSpot-provided Python SDK for REST API v2], which supports both asynchronous and synchronous invocation, transparent token refresh, server-sent event (SSE) streaming, file uploads and downloads, and error handling. +The workflows and examples in this tutorial use a legacy version of the ThoughtSpot REST API link:https://github.com/thoughtspot/thoughtspot_rest_api_python[ThoughtSpot Community SDK, window=_blank]. We recommend using the xref:rest-api-python-sdk.adoc[ThoughtSpot-provided Python SDK for REST API v2], which supports both asynchronous and synchronous invocation, transparent token refresh, server-sent event (SSE) streaming, file uploads and downloads, and error handling. ==== == Get started @@ -14,14 +14,12 @@ 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_final.py`. . Open up your command line or terminal environment as well. [NOTE] ==== -There are many ways to install and configure Python on a system. This tutorial shows "plain" versions of all commands as if you are using the main system-wide install of Python for the tutorial. - -Please adjust all Python commands according to your own environment. +The commands in this tutorial are the basic versions, which assume that you use the system-wide Python installation. If your environment is different, for example a virtual environment, adjust the Python commands accordingly. ==== == 01 - Imports and variables @@ -101,7 +99,9 @@ requests_session = requests.Session() requests_session.headers.update(api_headers) # Define the JSON message, in Python object syntax (close but not exactly JSON) -json_post_data = { # a request body } +json_post_data = { + # a request body +} # Set the URL of the endpoint url = base_url + "{api_endpoint_ending}" @@ -175,7 +175,7 @@ image:images/tutorials/rest-api/python-command-in-terminal.png[Running script in If you have a more complex local Python environment you are using, find the appropriate way to send the script you have updated through the Python environment set up for this tutorial. === Update the Session object headers -Almost all REST API endpoints other than the token requests require authentication, either within the cookies or via an `Authentication: Bearer {token}` header. +Almost all REST API endpoints other than the token requests require authentication, either within the cookies or via an `Authorization: Bearer {token}` header. You need to update the `Session` object with this new header while keeping the original ones. @@ -219,6 +219,7 @@ try: except Exception as e: # do whatever is necessary in exception case + print(e) # Code after the try block will now run even after Exception ---- @@ -236,7 +237,7 @@ try: resp = requests_session.post(url=url, json=json_post_data) resp.raise_for_status() print(resp) - token = resp["token"] + token = resp.json()["token"] except requests.exceptions.HTTPError as e: print(e) print(e.request) @@ -258,6 +259,15 @@ thoughtspot_url = 'https://{}.thoughtspot.cloud' org_id = 0 api_version = '2.0' +base_url = '{thoughtspot_url}/api/rest/{version}/'.format(thoughtspot_url=thoughtspot_url, version=api_version) +api_headers = { + 'X-Requested-By': 'ThoughtSpot', + 'Accept': 'application/json' +} + +requests_session = requests.Session() +requests_session.headers.update(api_headers) + endpoint = 'auth/token/full' url = base_url + endpoint @@ -293,6 +303,7 @@ except requests.exceptions.HTTPError as e: print(e.request.headers) print(e.request.body) print(e.response.content) + exit() # Stop here, because there is no token to use # Update api_headers from before with header for Bearer token api_headers['Authorization'] = 'Bearer {}'.format(token) @@ -305,9 +316,13 @@ user_search_url = base_url + "users/search" # Every request must be wrapped in try...except try: - search_resp = requests_session.get(url=user_search_url) + # Search endpoints are POST requests and need a JSON body, even if it is only {} + search_resp = requests_session.post(url=user_search_url, json={"record_offset": 0, "record_size": 10}) search_resp.raise_for_status() -... + print(json.dumps(search_resp.json(), indent=2)) +except requests.exceptions.HTTPError as e: + print(e) + print(e.response.content) ---- You may have noticed many steps that are repeated each time for any given request. 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 a205c4325..361fbb9a8 100644 --- a/modules/tutorials/pages/rest-api/rest-api_lesson-03.adoc +++ b/modules/tutorials/pages/rest-api/rest-api_lesson-03.adoc @@ -5,7 +5,7 @@ :toc!: == Get started -The files for this tutorial are `api_training_python_2_begin.py` and `api_training_python_2_end.py`. +The files for this tutorial are `api_training_python_2_begin.py` and `api_training_python_2_final.py`. 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. @@ -180,7 +180,7 @@ The first of our tasks is: # Is this a case-sensitive or case-insensitive operation? Are we finding anywhere in the name or just at the start or end? ---- -Information about the objects in the system lives under the `Metadata` heading within the Playground. Endpoints labeled `Search` are `GET` methods that query information without causing any changes. +Information about the objects in the system lives under the `Metadata` heading within the Playground. Endpoints labeled `Search` are `POST` requests that query information without causing any changes. `/metadata/search` has many different xref:rest-api-v2-metadata-search.adoc[request parameters] available to help filter and select all of the necessary information. diff --git a/modules/tutorials/pages/rest-api/rest-api_lesson-04.adoc b/modules/tutorials/pages/rest-api/rest-api_lesson-04.adoc index effe6ca22..9a191ac52 100644 --- a/modules/tutorials/pages/rest-api/rest-api_lesson-04.adoc +++ b/modules/tutorials/pages/rest-api/rest-api_lesson-04.adoc @@ -112,7 +112,7 @@ const apiRequestObject = { The `fetch()` function in JavaScript uses the browser itself as a store of cookies and other details when you use the `credentials: "include"` parameter. -`fetch` is an asynchronous call that returns a `Promise`, which has `.then()` and `.error()` methods for calling the next code to run once the REST API response is returned: +`fetch` is an asynchronous call that returns a `Promise`, which has `.then()` and `.catch()` methods for calling the next code to run once the REST API response is returned: [source,javascript] ---- @@ -122,7 +122,7 @@ const apiFullEndpoint = tsHost + "/" + publicApiUrl + endpoint; await fetch( apiFullEndpoint, // URL { - method: ‘POST’, + method: 'POST', headers: { "Accept": "application/json", "X-Requested-By": "ThoughtSpot", @@ -159,7 +159,7 @@ const apiFullEndpoint = tsHost + "/" + publicApiUrl + endpoint; await tokenizedFetch( apiFullEndpoint, // URL { - method: ‘POST’, + method: 'POST', headers: { "Accept": "application/json", "X-Requested-By": "ThoughtSpot", @@ -276,7 +276,7 @@ To use such a function you define your request object, copied from the REST API [source,javascript] ---- // Here we actually CALL the functions to make the request -function makeSearchRequest(){ +async function makeSearchRequest(){ // Copy the request directly from the REST API Playground, substituting in any variables you need const apiRequestObject = { "metadata": [ @@ -294,7 +294,7 @@ function makeSearchRequest(){ } - let results = callMetadataSearchApi(apiRequestObject); + let results = await callMetadataSearchApi(apiRequestObject); console.log("Final results from the callMetadataSearchApi function: "); console.log(results); } diff --git a/modules/tutorials/pages/style-customization/style-customization_tutorial-step-02.adoc b/modules/tutorials/pages/style-customization/style-customization_tutorial-step-02.adoc index 74b307c13..6184bccd8 100644 --- a/modules/tutorials/pages/style-customization/style-customization_tutorial-step-02.adoc +++ b/modules/tutorials/pages/style-customization/style-customization_tutorial-step-02.adoc @@ -37,7 +37,7 @@ The code includes examples of several settings that are commented out using the . Click *Run* again to see the updated results. == Available options for component element customization -There are many more configurations available for each component than those available in the Playground. To see all available options, see link:https://developers.thoughtspot.com/docs/VisualEmbedSdk#_interfaces[Embed Classes^] and link:https://developers.thoughtspot.com/docs/VisualEmbedSdk#_interfaces[Embed Interfaces^]. +There are many more configurations available for each component than those available in the Playground. To see all available options, see link:https://developers.thoughtspot.com/docs/VisualEmbedSdk#_classes[Embed Classes^] and link:https://developers.thoughtspot.com/docs/VisualEmbedSdk#_interfaces[Embed Interfaces^]. [.bordered] diff --git a/modules/tutorials/pages/style-customization/style-customization_tutorial-step-03.adoc b/modules/tutorials/pages/style-customization/style-customization_tutorial-step-03.adoc index 751027c1a..da9f72406 100644 --- a/modules/tutorials/pages/style-customization/style-customization_tutorial-step-03.adoc +++ b/modules/tutorials/pages/style-customization/style-customization_tutorial-step-03.adoc @@ -69,7 +69,7 @@ Variables declare a __single__ property, therefore are defined as `"{var-name}" Selectors apply properties to elements with many layers of styling. Always add `!important` after each property to ensure the browser overrides whatever other style rules may be applied for the same property. ==== -One use case of the `rules_UNSTABLE` section is `xref:css-customization.adoc#font-declarations[@font-face]` declarations, which have many properties for one selector. +One use case of the `rules_UNSTABLE` section is xref:css-customization.adoc#_font_declarations[`@font-face`] declarations, which have many properties for one selector. We'll switch the main font to link:https://fonts.google.com/specimen/Poppins[Poppins^], available from Google Fonts: @@ -107,7 +107,7 @@ Notice the format shows the selector as the __key__, then an object block contai == CSS files You can collect a set of variables and selectors into a CSS file, rather than declaring them in the JavaScript code block. -CSS files can be included from any domain, but they must be added to the *CSP style-src domains* and *CSP font-src* domains on the *Develop* > *Customizations* -> *Security settings* page. +CSS files can be included from any domain, but they must be added to the *CSP style-src domains* and *CSP font-src domains* lists on the *Develop* > *Customizations* > *Security settings* page. Both `\https://cdn.jsdelivr.net` and `\https://fonts.gstatic.com` sites are automatically added to ThoughtSpot's CSP allowlist. @@ -118,7 +118,7 @@ In your CSS file, the global variables must be declared in the `:root { }` block :root { --ts-var-button--primary-background: #2359B6; --ts-var-button--primary--hover-background: blue; - --ts-var-button--primary--font-family: Poppins,Helvetica,Arial,sans-serif;; + --ts-var-button--primary--font-family: Poppins,Helvetica,Arial,sans-serif; } @font-face { @@ -141,7 +141,7 @@ As seen in the CSS file example above, one of the use cases for selectors is to Make sure that the selector you use is specific and does not affect other elements that you don't intend to hide. -If you have been hiding certain elements via CSS selectors, link:https://community.thoughtspot.com/s/ideas[contact ThoughtSpot^] to request configuration options for such elements, so that the overall configurations can be expanded over time. Similarly, provide feedback on properties that variables are unavailable by submitting an idea to the link:https://community.thoughtspot.com/s/ideas[ThoughtSpot Community^]. +If you have been hiding certain elements via CSS selectors, link:https://community.thoughtspot.com/s/ideas[contact ThoughtSpot^] to request configuration options for such elements, so that the overall configurations can be expanded over time. Similarly, provide feedback on properties for which variables are unavailable by submitting an idea to the link:https://community.thoughtspot.com/s/ideas[ThoughtSpot Community^]. ++++