From 965a5382001e73d8309db3cdb866b21fc15dcbe3 Mon Sep 17 00:00:00 2001 From: Benji Nguyen <45523555+solidiquis@users.noreply.github.com> Date: Mon, 29 Jul 2024 21:24:25 -0700 Subject: [PATCH] chore: docs + example cleanup (#79) --- docs/grpc_codes_and_timeouts.md | 97 ++++++++++++ examples/python/annotations/.env-example | 9 -- examples/python/annotations/README.md | 35 ----- examples/python/annotations/main.py | 30 ---- examples/python/annotations/requirements.txt | 2 - .../python/ingest_with_config/.env-example | 9 -- examples/python/ingest_with_config/README.md | 40 ----- examples/python/ingest_with_config/main.py | 147 ------------------ .../ingest_with_config/requirements.txt | 2 - .../sift_ingestion_utils.py | 146 ----------------- {examples/go => go/examples}/.env-example | 0 {examples/go => go/examples}/README.md | 0 {examples/go => go/examples}/go.mod | 0 {examples/go => go/examples}/go.sum | 0 {examples/go => go/examples}/main.go | 0 .../examples}/annotations/.env-example | 0 .../examples}/annotations/Cargo.lock | 0 .../examples}/annotations/Cargo.toml | 0 .../examples}/annotations/README.md | 0 .../examples}/annotations/src/main.rs | 0 .../ingestion_with_config/.env-example | 0 .../ingestion_with_config/Cargo.lock | 0 .../ingestion_with_config/Cargo.toml | 0 .../examples}/ingestion_with_config/README.md | 0 .../configs/lunar_rover0.yml | 0 .../ingestion_with_config/src/config.rs | 0 .../src/grpc/credentials.rs | 0 .../src/grpc/interceptor.rs | 0 .../ingestion_with_config/src/grpc/mod.rs | 0 .../ingestion_with_config/src/ingestion.rs | 0 .../ingestion_with_config/src/main.rs | 0 31 files changed, 97 insertions(+), 420 deletions(-) create mode 100644 docs/grpc_codes_and_timeouts.md delete mode 100644 examples/python/annotations/.env-example delete mode 100644 examples/python/annotations/README.md delete mode 100644 examples/python/annotations/main.py delete mode 100644 examples/python/annotations/requirements.txt delete mode 100644 examples/python/ingest_with_config/.env-example delete mode 100644 examples/python/ingest_with_config/README.md delete mode 100644 examples/python/ingest_with_config/main.py delete mode 100644 examples/python/ingest_with_config/requirements.txt delete mode 100644 examples/python/ingest_with_config/sift_ingestion_utils.py rename {examples/go => go/examples}/.env-example (100%) rename {examples/go => go/examples}/README.md (100%) rename {examples/go => go/examples}/go.mod (100%) rename {examples/go => go/examples}/go.sum (100%) rename {examples/go => go/examples}/main.go (100%) rename {examples/rust => rust/examples}/annotations/.env-example (100%) rename {examples/rust => rust/examples}/annotations/Cargo.lock (100%) rename {examples/rust => rust/examples}/annotations/Cargo.toml (100%) rename {examples/rust => rust/examples}/annotations/README.md (100%) rename {examples/rust => rust/examples}/annotations/src/main.rs (100%) rename {examples/rust => rust/examples}/ingestion_with_config/.env-example (100%) rename {examples/rust => rust/examples}/ingestion_with_config/Cargo.lock (100%) rename {examples/rust => rust/examples}/ingestion_with_config/Cargo.toml (100%) rename {examples/rust => rust/examples}/ingestion_with_config/README.md (100%) rename {examples/rust => rust/examples}/ingestion_with_config/configs/lunar_rover0.yml (100%) rename {examples/rust => rust/examples}/ingestion_with_config/src/config.rs (100%) rename {examples/rust => rust/examples}/ingestion_with_config/src/grpc/credentials.rs (100%) rename {examples/rust => rust/examples}/ingestion_with_config/src/grpc/interceptor.rs (100%) rename {examples/rust => rust/examples}/ingestion_with_config/src/grpc/mod.rs (100%) rename {examples/rust => rust/examples}/ingestion_with_config/src/ingestion.rs (100%) rename {examples/rust => rust/examples}/ingestion_with_config/src/main.rs (100%) diff --git a/docs/grpc_codes_and_timeouts.md b/docs/grpc_codes_and_timeouts.md new file mode 100644 index 00000000..8b8a046b --- /dev/null +++ b/docs/grpc_codes_and_timeouts.md @@ -0,0 +1,97 @@ +# Sift API Status Codes and Timeouts + +This document covers all the various status codes Sift may return and under what conditions, as well as what users can expect with regard to timeouts. + +- [Sift gRPC API Status Codes](#sift-grpc-api-status-codes) + * [Status Codes Explicitly Set by Sift](#status-codes-explicitly-set-by-sift) + * [Status Codes Set by gRPC Libraries](#status-codes-set-by-grpc-libraries) + * [Recommendations](#recommendations) +- [Sift API Timeouts](sift-api-timeouts) + * [Keepalive Pings During Streaming](#keepalive-pings-during-streaming) + +## Sift gRPC API Status Codes + +The gRPC status codes that are ultimately returned to the client may fall into one of these two categories: + +- Status codes explicitly set by Sift +- Status codes set by underlying gRPC libraries used by the Sift API or the client + +Below provides further details to elucidate situations where a known status code might be expected to occur and is subject to change in the future. The meaning behind each error code can be found at this [link](https://grpc.github.io/grpc/core/md_doc_statuscodes.html). + +### Status Codes Explicitly Set by Sift + +At the unary interceptor (i.e. gRPC middleware) level Sift controls what status codes are returned to the client. The status codes currently being utilized by Sift are limited to what is shown in the following table. Any status code that falls outside of this list can be assumed to be set at the level of gRPC libraries with the exception of `INTERNAL` and `UNAUTHENTICATED` which could be set either by Sift or the gRPC libraries used by Sift or the client. + +| Code | Number | +| -------- | ------- | +|OK|0| +|INVALID_ARGUMENT|3| +|NOT_FOUND|5| +|ALREADY_EXISTS|6| +|PERMISSION_DENIED|7| +|INTERNAL|13| +|UNAUTHENTICATED|16| + +### Status Codes Set by gRPC Libraries + +The following status codes may be generated by the gRPC libraries used by the server or client, meaning that they can be set either at the server or client level. These are not explicitly set by Sift. The following table details where each status code is generated and some common example situations demonstrating when they may be set. + +|Example Scenarios|Code|Number|Generated at Client or Server| +| -------- | ------- | ------- | ------- | +|Indicates the operation was canceled either by server or client. | CANCELLED | 1 | Both | +|Server side application doesn’t return a valid gRPC status|UNKNOWN|2|Server| +|Error parsing a returned status|UNKNOWN|2|Client| +|No response received before Deadline expires. This may occur either when the client is unable to send the request to the server or when the server fails to respond in time.|DEADLINE_EXCEEDED|4|Both| +|Server temporarily out of resources|RESOURCE_EXHAUSTED|8|Server| +|Client does not have enough memory to hold the server response|RESOURCE_EXHAUSTED|8|Client| +|Sent or received message was larger than configured limit|RESOURCE_EXHAUSTED|8|Both| +|Error parsing request proto|INTERNAL|13|Server| +|Error parsing response proto|INTERNAL|13||Client| +|Client tries to send a request to a closing connection|INTERNAL|13|Server| +|Server shutting down|UNAVAILABLE|14|Server| +|Occurs during abrupt shutdown of application server or network connection|UNAVAILABLE|14|Both| +|Incorrect Auth metadata ( Credentials failed to get metadata, Incompatible credentials set on channel and call, Invalid host set in :authority metadata, etc.)|UNAUTHENTICATED|16|Both| + +### Recommendations + +Generally speaking knowing where the status code gets set is incredibly useful for debugging. In common situations such as user error the Sift API will provide a message in the response proto to indicate what needs to be rectified, e.g. the request is missing a required parameter. In situations where a gRPC status code is INTERNAL it is crucial to inspect the response beyond just the status code. INTERNAL errors set by Sift often come with a trace-ID which you can provide to Sift in order to help us debug your particular issue. If there is no trace-ID, it’s usually indicative that the errors are occurring library-level. Regardless, if there is an issue that you are experiencing with the Sift API that you are unable to resolve please provide us with as much information about the request and the response as possible and we will do our best to assist you. + +## Sift API Timeouts + +Regardless of which API a client is connecting to—whether it be gRPC or REST—the maximum duration a connection is allowed to stay alive without any network traffic is 60 seconds. This is what is referred to as an idle timeout. When an idle timeout occurs, one of two things may occur: + +Either the Sift API gracefully shuts down the connection by sending an HTTP/2 GOAWAY frame, or + +the reverse proxy automatically terminates the connection. + +Depending on the language and gRPC library used by the client, and idle timeout could produce either a DEADLINE_EXCEEDED gRPC status code or a UNAVAILABLE. + +### Keepalive Pings During Streaming + +When clients are streaming data to Sift, it is common for there to be periods of inactivity that might go beyond Sift’s configured idle timeout. In these situations it is important to properly configure the client to leverage gRPC’s keepalive mechanism to ensure that a connection isn’t prematurely terminated. + +How keepalive pings are configured is largely dependent on the language and library, but regardless of which the following channel arguments are what is used to configure periodic keep-alive pings: + +- `GRPC_ARG_KEEPALIVE_TIME_MS` + * This channel argument controls the period (in milliseconds) after which a keep-alive ping is sent on the transport. + +- `GRPC_ARG_KEEPALIVE_TIMEOUT_MS` + * This channel argument controls the amount of time (in milliseconds) the sender of the keep-alive ping waits for an acknowledgement. If it does not receive an acknowledgment within this time, it will close the connection. + +As an example, consider the following channel configurations in pseudo-code: + +```python +channel_options = [ + ("grpc.keepalive_time_ms", 8000), + ("grpc.keepalive_timeout_ms", 5000), +] +``` + +With respect to this configuration: +- A keepalive ping will be sent every 8 seconds (8000 ms) during a period of inactivity +- The client will wait for 5 seconds (5000 ms) for the server to acknowledge a keepalive ping before deciding to take a particular action: + * The client could decide to completely shut down the connection, or + * The client could try to manually re-establish a connection, or + * An automated retry mechanism could be initiated. + +For a full-guide on available channel options to configure keepalive pings, please refer to this [link](https://github.com/grpc/grpc/blob/master/doc/keepalive.md). diff --git a/examples/python/annotations/.env-example b/examples/python/annotations/.env-example deleted file mode 100644 index 4bd2d050..00000000 --- a/examples/python/annotations/.env-example +++ /dev/null @@ -1,9 +0,0 @@ -# Retrieve the BASE_URI from the Sift team -# Be sure to exclude "https://" from the BASE_URI -# -# BASE URIs and further details can be found here: -# https://docs.siftstack.com/ingestion/overview -# BASE_URI= - -# Your sift api key -SIFT_API_KEY= diff --git a/examples/python/annotations/README.md b/examples/python/annotations/README.md deleted file mode 100644 index 39478718..00000000 --- a/examples/python/annotations/README.md +++ /dev/null @@ -1,35 +0,0 @@ -# Annotations Example - -This example demonstrates using Sift protos to create a basic CLI that queries annotations based on name search. - -To run this example proceed with the following steps and be sure to have your -Sift API key ready. If you need a Sift API key please refer to [these instructions](https://help.siftstack.com/en/articles/8600475-api-keys). - -Activate your virtual environment: - -```bash -$ python -m venv venv -$ source venv/bin/activate -``` - -Install dependencies: - -```bash -$ pip install -r requirements.txt -``` - -Create your `.env` file: - -```bash -$ cp .env-example .env -``` - -Be sure to set the appropriate environment variables in your `.env` file depending on the environment you're using. Comments -meant to serve as guides can be found in the `.env-example` file. - -Now execute the program by providing the partial string of the annotations you wish to query. In the following example -we'll be querying for all annotations whose name matches the `voltage` substring in a case-insensitive manner. - -```bash -$ python main.py voltage -``` diff --git a/examples/python/annotations/main.py b/examples/python/annotations/main.py deleted file mode 100644 index a200d139..00000000 --- a/examples/python/annotations/main.py +++ /dev/null @@ -1,30 +0,0 @@ -import sys -from dotenv import load_dotenv -import grpc -import os -from sift.annotations.v1.annotations_pb2_grpc import AnnotationServiceStub -from sift.annotations.v1.annotations_pb2 import ListAnnotationsRequest - -if __name__ == "__main__": - load_dotenv() - API_KEY = os.getenv("SIFT_API_KEY") - BASE_URI = os.getenv("BASE_URI") - - if len(sys.argv) < 2: - print("Please provide a name.") - sys.exit(1) - - name = sys.argv[1] - - credentials = grpc.ssl_channel_credentials() - call_credentials = grpc.access_token_call_credentials(API_KEY) - composite_credentials = grpc.composite_channel_credentials( - credentials, call_credentials - ) - - with grpc.secure_channel(BASE_URI, composite_credentials) as channel: - annotation_service = AnnotationServiceStub(channel) - request = ListAnnotationsRequest(filter=f'name.matches("(?i){name}")') - response = annotation_service.ListAnnotations(request) - print(response) - channel.close() diff --git a/examples/python/annotations/requirements.txt b/examples/python/annotations/requirements.txt deleted file mode 100644 index 82f6ef93..00000000 --- a/examples/python/annotations/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -python-dotenv==1.0.1 -sift @ git+https://github.com/sift-stack/sift.git#subdirectory=python diff --git a/examples/python/ingest_with_config/.env-example b/examples/python/ingest_with_config/.env-example deleted file mode 100644 index 4bd2d050..00000000 --- a/examples/python/ingest_with_config/.env-example +++ /dev/null @@ -1,9 +0,0 @@ -# Retrieve the BASE_URI from the Sift team -# Be sure to exclude "https://" from the BASE_URI -# -# BASE URIs and further details can be found here: -# https://docs.siftstack.com/ingestion/overview -# BASE_URI= - -# Your sift api key -SIFT_API_KEY= diff --git a/examples/python/ingest_with_config/README.md b/examples/python/ingest_with_config/README.md deleted file mode 100644 index ec1b6f61..00000000 --- a/examples/python/ingest_with_config/README.md +++ /dev/null @@ -1,40 +0,0 @@ -# Ingesting Data with a Config - -This example demonstrates how to use Sift protos in Python to ingest data using an ingestion config. The script -contains doc-strings that you may find helpful. - -To run this example proceed with the following steps and be sure to have your -Sift API key ready. If you need a Sift API key please refer to [these instructions](https://help.siftstack.com/en/articles/8600475-api-keys). - -Activate your virtual environment: - -```bash -$ python -m venv venv -$ source venv/bin/activate -``` - -Install dependencies: - -```bash -$ pip install -r requirements.txt -``` - -Create your `.env` file: - -```bash -$ cp .env-example .env -``` - -Be sure to set the appropriate environment variables in your `.env` file depending on the environment you're using. Comments -meant to serve as guides can be found in the `.env-example` file. - -Before executing the script, there is a class in [main.py](main.py#L76) called `ExampleTestRunConfig` that you are free to edit -to change the run names, asset names, etc.. - -To execute the script run the following: - -```bash -$ python main.py -``` - -Once the script finishes executing, you should see a new run in the Sift UI containing newly telemetered data. diff --git a/examples/python/ingest_with_config/main.py b/examples/python/ingest_with_config/main.py deleted file mode 100644 index 359e8fce..00000000 --- a/examples/python/ingest_with_config/main.py +++ /dev/null @@ -1,147 +0,0 @@ -from dotenv import load_dotenv -from google.protobuf.timestamp_pb2 import Timestamp -from typing import Any, Generator, List, Optional, Tuple -import math -import os -import sift_ingestion_utils as ingest -import sift.ingest.v1.ingest_pb2 as ingestpb -import sift.ingestion_configs.v1.ingestion_configs_pb2 as ingestconf -import sift.runs.v2.runs_pb2 as runpb - - -def main(): - """ - This is an example script demonstrating how to use Sift's IngestService_IngestWithConfigDataStream - API. In this example we will be creating an asset that contains two channels: velocity and pressure. - With this asset and its associated channels we will create a run which describes the window of time - to associate with data that gets ingested. - - Streaming data using this API at a high level involves the following steps: - - 1. Create channel configs - 2. Create flow configs - 3. Create an ingestion config - 4. Create a run - 5. Ingest data - - Once this script runs through completion the data should be ready to visualize on the Sift UI not long after. - - If in running this example you created a lot of unwanted runs that you wish to delete, the sift_ingestion_utils - module has a delete_run method that you can use from the Python console. - """ - - load_dotenv() - api_key = os.getenv("SIFT_API_KEY", "") - base_uri = os.getenv("BASE_URI", "") - - run_start_time = Timestamp() - run_start_time.GetCurrentTime() - - with ingest.use_secure_channel(api_key, base_uri) as channel: - channel_configs = [] - - for channel_config in ExampleTestRunConfig.CHANNEL_CONFIG_PARAMS: - name, component, desc, unit = channel_config - conf = ingest.create_double_type_channel_config(name, component, desc, unit) - channel_configs.append(conf) - - flow_config = ingest.create_flow_config( - ExampleTestRunConfig.FLOW_NAME, *channel_configs - ) - - print("Creating ingestion config... ", end="") - ingestion_config = ingest.create_ingestion_config( - channel, - ExampleTestRunConfig.ASSET_NAME, - flow_config, - ) - print(f"ok [ingestion_config_id {ingestion_config.ingestion_config_id}]") - - print(f"Creating run {ExampleTestRunConfig.RUN_NAME}... ", end="") - run = ingest.create_run( - channel, - ExampleTestRunConfig.RUN_NAME, - ExampleTestRunConfig.RUN_DESCRIPTION, - None, - run_start_time, - None, - *ExampleTestRunConfig.RUN_TAGS, - ) - print(f"ok [run_id {run.run_id}]") - - print("Beginning ingestion...") - ingestion_simulator = create_ingestion_simulator( - run, ingestion_config, flow_config - ) - ingest.ingest_with_config(channel, ingestion_simulator) - print("Ingestion completed.") - - channel.close() - - -class ExampleTestRunConfig: - """ - This is a sample of various properties we'll use to constitute an asset, its associated channels, - and a run. We're going to be telemetering data for our asset, 'example_asset_name', from two channels: - a channel called 'pressure' and the other, 'velocity' - """ - - ASSET_NAME = "example_asset_name" - RUN_NAME = "example_run" - RUN_DESCRIPTION = "This is an example run" - RUN_TAGS = ["foo", "bar"] - FLOW_NAME = "example_flow" - - # (name, component, description, units) - CHANNEL_CONFIG_PARAMS: List[ - Tuple[str, Optional[str], Optional[str], Optional[str]] - ] = [ - ("pressure", None, "pressure applied", "mmHg"), - ("velocity", "mainmotor", None, "m/s"), - ] - - -def create_ingestion_simulator( - run: runpb.Run, - ingestion_config: ingestconf.IngestionConfig, - flow_config: ingestconf.FlowConfig, - num_data_points: int = 100, -) -> Generator[ingestpb.IngestWithConfigDataStreamRequest, Any, None]: - """ - This function will create generator which we'll use to simulate ingestion. It's going to essentially generate - some amount of data points (100 by default) 5ms apart that will get ingested into the Sift API. - """ - - current_timestamp = run.start_time - total_messages_sent = 0 - - for i in range(num_data_points): - # 5 milliseconds apart - timestamp = Timestamp() - timestamp.FromMilliseconds(current_timestamp.ToMilliseconds() + 5) - current_timestamp = timestamp - - request = ingestpb.IngestWithConfigDataStreamRequest( - run_id=run.run_id, - ingestion_config_id=ingestion_config.ingestion_config_id, - flow=flow_config.name, - end_stream_on_validation_error=True, - timestamp=timestamp, - ) - - pressure = ingestpb.IngestWithConfigDataChannelValue(double=math.sin(60 * i)) - request.channel_values.append(pressure) - - velocity = ingestpb.IngestWithConfigDataChannelValue(double=math.sin(40 * i)) - request.channel_values.append(velocity) - - total_messages_sent += 1 - print( - f"Sending message [time={timestamp.ToJsonString()} run={run.run_id} total_messages_sent={total_messages_sent}]" - ) - - yield request - - -if __name__ == "__main__": - main() diff --git a/examples/python/ingest_with_config/requirements.txt b/examples/python/ingest_with_config/requirements.txt deleted file mode 100644 index 82f6ef93..00000000 --- a/examples/python/ingest_with_config/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -python-dotenv==1.0.1 -sift @ git+https://github.com/sift-stack/sift.git#subdirectory=python diff --git a/examples/python/ingest_with_config/sift_ingestion_utils.py b/examples/python/ingest_with_config/sift_ingestion_utils.py deleted file mode 100644 index 29bc2c3c..00000000 --- a/examples/python/ingest_with_config/sift_ingestion_utils.py +++ /dev/null @@ -1,146 +0,0 @@ -from google.protobuf import timestamp_pb2 -from typing import Any, cast, Generator, Optional -import grpc -from sift.common.type.v1.channel_data_type_pb2 import ChannelDataType -import sift.ingest.v1.ingest_pb2 as ingest -import sift.ingest.v1.ingest_pb2_grpc as ingest_grpc -import sift.ingestion_configs.v1.ingestion_configs_pb2 as ingestconf -import sift.ingestion_configs.v1.ingestion_configs_pb2_grpc as ingestconf_grpc -import sift.runs.v2.runs_pb2 as run -import sift.runs.v2.runs_pb2_grpc as run_grpc - - -def use_secure_channel(api_key: str, base_uri: str) -> grpc.Channel: - """ - Produces channel that is used to create a secure connection to a gRPC server. - This is intended to be used by all stubs. - """ - credentials = grpc.ssl_channel_credentials() - call_credentials = grpc.access_token_call_credentials(api_key) - composite_credentials = grpc.composite_channel_credentials( - credentials, call_credentials - ) - return grpc.secure_channel(base_uri, composite_credentials) - - -def create_double_type_channel_config( - name: str, - description: Optional[str], - component: Optional[str], - unit: Optional[str], -) -> ingestconf.ChannelConfig: - """ - Creates a channel config for values that are double precision floating points. - """ - config = ingestconf.ChannelConfig(name=name) - config.data_type = ChannelDataType.CHANNEL_DATA_TYPE_DOUBLE - - if component is not None: - config.component = component - - if unit is not None: - config.unit = unit - - if description is not None: - config.description = description - - return config - - -def create_flow_config( - flow_name: str, *channel_configs: ingestconf.ChannelConfig -) -> ingestconf.FlowConfig: - """ - Creates a flow config that describes a group of channels that will telemeter data. - """ - config = ingestconf.FlowConfig(name=flow_name) - - for channel_config in channel_configs: - config.channels.append(channel_config) - - return config - - -def create_ingestion_config( - channel: grpc.Channel, - asset_name: str, - *flow_configs: ingestconf.FlowConfig, -) -> ingestconf.IngestionConfig: - """ - Reaches out to the Sift API to create an ingestion config that constitutes essential metadata for ingestion. - """ - - request = ingestconf.CreateIngestionConfigRequest(asset_name=asset_name) - - for flow_config in flow_configs: - request.flows.append(flow_config) - - response = ingestconf_grpc.IngestionConfigServiceStub( - channel - ).CreateIngestionConfig(request) - - return cast(ingestconf.CreateIngestionConfigResponse, response).ingestion_config - - -def create_run( - channel: grpc.Channel, - name: str, - description: str, - organization_id: Optional[str], - start_time: Optional[timestamp_pb2.Timestamp], - stop_time: Optional[timestamp_pb2.Timestamp], - *tags: str, -) -> run.Run: - """ - Reaches out to the Sift API to create a run that will be associated with ingested data. - """ - - request = run.CreateRunRequest(name=name, description=description) - - for tag in tags: - request.tags.append(tag) - - if organization_id is not None: - request.organization_id = organization_id - - if start_time is not None: - request.start_time.CopyFrom(start_time) - - if stop_time is not None: - request.stop_time.CopyFrom(stop_time) - - response = run_grpc.RunServiceStub(channel).CreateRun(request) - - return cast(run.CreateRunResponse, response).run - - -def ingest_with_config( - channel: grpc.Channel, - ingestion_iter: Generator[ingest.IngestWithConfigDataStreamRequest, Any, None], -): - """ - Consume the ingestion_iter generator and send the data produced to the Sift's ingestion API. - Data should be available to view shortly after this function concludes - """ - try: - ingest_grpc.IngestServiceStub(channel).IngestWithConfigDataStream( - ingestion_iter - ) - except Exception as e: - print(f"Something went wrong during ingestion: {e}") - - -def delete_run(run_id: str, api_key: str, base_uri: str): - """ - Handy utility to delete your test runs - """ - - with use_secure_channel(api_key, base_uri) as channel: - try: - req = run.DeleteRunRequest(run_id=run_id) - run_grpc.RunServiceStub(channel).DeleteRun(req) - print(f"Deleted run {run_id}") - except Exception as e: - print(f"Something went wrong trying to delete the run: {e}") - finally: - channel.close() diff --git a/examples/go/.env-example b/go/examples/.env-example similarity index 100% rename from examples/go/.env-example rename to go/examples/.env-example diff --git a/examples/go/README.md b/go/examples/README.md similarity index 100% rename from examples/go/README.md rename to go/examples/README.md diff --git a/examples/go/go.mod b/go/examples/go.mod similarity index 100% rename from examples/go/go.mod rename to go/examples/go.mod diff --git a/examples/go/go.sum b/go/examples/go.sum similarity index 100% rename from examples/go/go.sum rename to go/examples/go.sum diff --git a/examples/go/main.go b/go/examples/main.go similarity index 100% rename from examples/go/main.go rename to go/examples/main.go diff --git a/examples/rust/annotations/.env-example b/rust/examples/annotations/.env-example similarity index 100% rename from examples/rust/annotations/.env-example rename to rust/examples/annotations/.env-example diff --git a/examples/rust/annotations/Cargo.lock b/rust/examples/annotations/Cargo.lock similarity index 100% rename from examples/rust/annotations/Cargo.lock rename to rust/examples/annotations/Cargo.lock diff --git a/examples/rust/annotations/Cargo.toml b/rust/examples/annotations/Cargo.toml similarity index 100% rename from examples/rust/annotations/Cargo.toml rename to rust/examples/annotations/Cargo.toml diff --git a/examples/rust/annotations/README.md b/rust/examples/annotations/README.md similarity index 100% rename from examples/rust/annotations/README.md rename to rust/examples/annotations/README.md diff --git a/examples/rust/annotations/src/main.rs b/rust/examples/annotations/src/main.rs similarity index 100% rename from examples/rust/annotations/src/main.rs rename to rust/examples/annotations/src/main.rs diff --git a/examples/rust/ingestion_with_config/.env-example b/rust/examples/ingestion_with_config/.env-example similarity index 100% rename from examples/rust/ingestion_with_config/.env-example rename to rust/examples/ingestion_with_config/.env-example diff --git a/examples/rust/ingestion_with_config/Cargo.lock b/rust/examples/ingestion_with_config/Cargo.lock similarity index 100% rename from examples/rust/ingestion_with_config/Cargo.lock rename to rust/examples/ingestion_with_config/Cargo.lock diff --git a/examples/rust/ingestion_with_config/Cargo.toml b/rust/examples/ingestion_with_config/Cargo.toml similarity index 100% rename from examples/rust/ingestion_with_config/Cargo.toml rename to rust/examples/ingestion_with_config/Cargo.toml diff --git a/examples/rust/ingestion_with_config/README.md b/rust/examples/ingestion_with_config/README.md similarity index 100% rename from examples/rust/ingestion_with_config/README.md rename to rust/examples/ingestion_with_config/README.md diff --git a/examples/rust/ingestion_with_config/configs/lunar_rover0.yml b/rust/examples/ingestion_with_config/configs/lunar_rover0.yml similarity index 100% rename from examples/rust/ingestion_with_config/configs/lunar_rover0.yml rename to rust/examples/ingestion_with_config/configs/lunar_rover0.yml diff --git a/examples/rust/ingestion_with_config/src/config.rs b/rust/examples/ingestion_with_config/src/config.rs similarity index 100% rename from examples/rust/ingestion_with_config/src/config.rs rename to rust/examples/ingestion_with_config/src/config.rs diff --git a/examples/rust/ingestion_with_config/src/grpc/credentials.rs b/rust/examples/ingestion_with_config/src/grpc/credentials.rs similarity index 100% rename from examples/rust/ingestion_with_config/src/grpc/credentials.rs rename to rust/examples/ingestion_with_config/src/grpc/credentials.rs diff --git a/examples/rust/ingestion_with_config/src/grpc/interceptor.rs b/rust/examples/ingestion_with_config/src/grpc/interceptor.rs similarity index 100% rename from examples/rust/ingestion_with_config/src/grpc/interceptor.rs rename to rust/examples/ingestion_with_config/src/grpc/interceptor.rs diff --git a/examples/rust/ingestion_with_config/src/grpc/mod.rs b/rust/examples/ingestion_with_config/src/grpc/mod.rs similarity index 100% rename from examples/rust/ingestion_with_config/src/grpc/mod.rs rename to rust/examples/ingestion_with_config/src/grpc/mod.rs diff --git a/examples/rust/ingestion_with_config/src/ingestion.rs b/rust/examples/ingestion_with_config/src/ingestion.rs similarity index 100% rename from examples/rust/ingestion_with_config/src/ingestion.rs rename to rust/examples/ingestion_with_config/src/ingestion.rs diff --git a/examples/rust/ingestion_with_config/src/main.rs b/rust/examples/ingestion_with_config/src/main.rs similarity index 100% rename from examples/rust/ingestion_with_config/src/main.rs rename to rust/examples/ingestion_with_config/src/main.rs