From d1d89a28f849d7a8ac1c55e32a3537f91afbf55e Mon Sep 17 00:00:00 2001 From: nlathia Date: Tue, 17 Feb 2026 12:48:17 +0000 Subject: [PATCH] Add missing resource sources apis --- src/gradient_labs/_resource_source_create.py | 59 +++++++++ src/gradient_labs/_resource_source_delete.py | 9 ++ src/gradient_labs/_resource_source_list.py | 23 ++++ src/gradient_labs/_resource_source_read.py | 11 ++ src/gradient_labs/_resource_source_update.py | 71 +++++++++++ ...source_source_update_schema_by_examples.py | 40 ++++++ src/gradient_labs/client.py | 82 +++++++++++++ src/gradient_labs/resource_source.py | 114 ++++++++++++++++++ 8 files changed, 409 insertions(+) create mode 100644 src/gradient_labs/_resource_source_create.py create mode 100644 src/gradient_labs/_resource_source_delete.py create mode 100644 src/gradient_labs/_resource_source_list.py create mode 100644 src/gradient_labs/_resource_source_read.py create mode 100644 src/gradient_labs/_resource_source_update.py create mode 100644 src/gradient_labs/_resource_source_update_schema_by_examples.py create mode 100644 src/gradient_labs/resource_source.py diff --git a/src/gradient_labs/_resource_source_create.py b/src/gradient_labs/_resource_source_create.py new file mode 100644 index 0000000..56d451e --- /dev/null +++ b/src/gradient_labs/_resource_source_create.py @@ -0,0 +1,59 @@ +from typing import Optional, Dict +from dataclasses import dataclass +from dataclasses_json import dataclass_json + +from ._http_client import HttpClient +from .resource_source import ( + ResourceSource, + SourceType, + ResourceHTTPDefinition, + ResourceWebhookDefinition, +) + + +@dataclass_json +@dataclass(frozen=True) +class CreateResourceSourceParams: + """CreateResourceSourceParams contains the parameters for creating a resource source.""" + + # DisplayName is a human-readable name for the resource source. + display_name: str + + # SourceType describes how the data is fetched from the source. + source_type: SourceType + + # Description is an optional human-readable description of the resource source. + description: Optional[str] = None + + # HTTPConfig can be set up by customers. Required if source_type is HTTP. + http_config: Optional[ResourceHTTPDefinition] = None + + # WebhookConfig can be set up by customers. Required if source_type is WEBHOOK. + webhook_config: Optional[ResourceWebhookDefinition] = None + + # AttributeDescriptions optional raw attribute-level descriptions. + attribute_descriptions: Optional[Dict[str, str]] = None + + +def create_resource_source( + *, client: HttpClient, params: CreateResourceSourceParams +) -> ResourceSource: + """create_resource_source creates a new resource source. + + Note: requires a Management API key. + """ + body = { + "display_name": params.display_name, + "source_type": params.source_type.value, + } + if params.description is not None: + body["description"] = params.description + if params.http_config is not None: + body["http_config"] = params.http_config.to_dict() + if params.webhook_config is not None: + body["webhook_config"] = params.webhook_config.to_dict() + if params.attribute_descriptions is not None: + body["attribute_descriptions"] = params.attribute_descriptions + + rsp = client.post(path="resource-sources", body=body) + return ResourceSource.from_dict(rsp) diff --git a/src/gradient_labs/_resource_source_delete.py b/src/gradient_labs/_resource_source_delete.py new file mode 100644 index 0000000..d16d8d4 --- /dev/null +++ b/src/gradient_labs/_resource_source_delete.py @@ -0,0 +1,9 @@ +from ._http_client import HttpClient + + +def delete_resource_source(*, client: HttpClient, id: str) -> None: + """delete_resource_source deletes a resource source. + + Note: requires a Management API key. + """ + _ = client.delete(path=f"resource-sources/{id}", body={}) diff --git a/src/gradient_labs/_resource_source_list.py b/src/gradient_labs/_resource_source_list.py new file mode 100644 index 0000000..f851034 --- /dev/null +++ b/src/gradient_labs/_resource_source_list.py @@ -0,0 +1,23 @@ +from typing import List +from dataclasses import dataclass +from dataclasses_json import dataclass_json + +from ._http_client import HttpClient +from .resource_source import ResourceSource + + +@dataclass_json +@dataclass(frozen=True) +class ResourceSourcesList: + """ResourceSourcesList contains a list of resource sources.""" + + resource_sources: List[ResourceSource] + + +def list_resource_sources(*, client: HttpClient) -> ResourceSourcesList: + """list_resource_sources lists all resource sources. + + Note: requires a Management API key. + """ + rsp = client.get(path="resource-sources", body={}) + return ResourceSourcesList.from_dict(rsp) diff --git a/src/gradient_labs/_resource_source_read.py b/src/gradient_labs/_resource_source_read.py new file mode 100644 index 0000000..6c1badf --- /dev/null +++ b/src/gradient_labs/_resource_source_read.py @@ -0,0 +1,11 @@ +from ._http_client import HttpClient +from .resource_source import ResourceSource + + +def read_resource_source(*, client: HttpClient, id: str) -> ResourceSource: + """read_resource_source reads a specific resource source by ID. + + Note: requires a Management API key. + """ + rsp = client.get(path=f"resource-sources/{id}", body={}) + return ResourceSource.from_dict(rsp) diff --git a/src/gradient_labs/_resource_source_update.py b/src/gradient_labs/_resource_source_update.py new file mode 100644 index 0000000..d7e8591 --- /dev/null +++ b/src/gradient_labs/_resource_source_update.py @@ -0,0 +1,71 @@ +from typing import Optional, Dict, Any +from dataclasses import dataclass +from dataclasses_json import dataclass_json + +from ._http_client import HttpClient +from .resource_source import ( + ResourceSource, + SourceType, + ResourceHTTPDefinition, + ResourceWebhookDefinition, +) + + +@dataclass_json +@dataclass(frozen=True) +class UpdateResourceSourceParams: + """UpdateResourceSourceParams contains the parameters for updating a resource source. + + All fields are optional. If a field is not provided, its value will not be changed. + """ + + # DisplayName is a human-readable name for the resource source. + display_name: Optional[str] = None + + # Description is an optional human-readable description of the resource source. + description: Optional[str] = None + + # SourceType describes how the data is fetched from the source. + # Note: source_type cannot be updated once set. To change the source_type, you must create a new resource source. + source_type: Optional[SourceType] = None + + # HTTPConfig can be set up by customers. + # When updating http_config, the entire object must be provided. + http_config: Optional[ResourceHTTPDefinition] = None + + # WebhookConfig can be set up by customers. + # When updating webhook_config, the entire object must be provided. + webhook_config: Optional[ResourceWebhookDefinition] = None + + # AttributeDescriptions optional raw attribute-level descriptions. + attribute_descriptions: Optional[Dict[str, str]] = None + + # Schema is the schema of the resource source. + schema: Optional[Any] = None + + +def update_resource_source( + *, client: HttpClient, id: str, params: UpdateResourceSourceParams +) -> ResourceSource: + """update_resource_source updates an existing resource source. + + Note: requires a Management API key. + """ + body = {} + if params.display_name is not None: + body["display_name"] = params.display_name + if params.description is not None: + body["description"] = params.description + if params.source_type is not None: + body["source_type"] = params.source_type.value + if params.http_config is not None: + body["http_config"] = params.http_config.to_dict() + if params.webhook_config is not None: + body["webhook_config"] = params.webhook_config.to_dict() + if params.attribute_descriptions is not None: + body["attribute_descriptions"] = params.attribute_descriptions + if params.schema is not None: + body["schema"] = params.schema + + rsp = client.put(path=f"resource-sources/{id}", body=body) + return ResourceSource.from_dict(rsp) diff --git a/src/gradient_labs/_resource_source_update_schema_by_examples.py b/src/gradient_labs/_resource_source_update_schema_by_examples.py new file mode 100644 index 0000000..e46f04e --- /dev/null +++ b/src/gradient_labs/_resource_source_update_schema_by_examples.py @@ -0,0 +1,40 @@ +from typing import List, Any, Optional +from dataclasses import dataclass +from dataclasses_json import dataclass_json + +from ._http_client import HttpClient +from .resource_source import ResourceSource, SchemaUpdateStrategy + + +@dataclass_json +@dataclass(frozen=True) +class UpdateResourceSourceSchemaByExamplesParams: + """UpdateResourceSourceSchemaByExamplesParams contains the parameters for updating a resource source schema by examples.""" + + # Examples is an array of example data payloads that represent the structure + # of the data your resource source returns. + examples: List[Any] + + # SchemaUpdateStrategy controls how the new schema is applied. + # Defaults to "merge" if not specified. + schema_update_strategy: Optional[SchemaUpdateStrategy] = None + + +def update_resource_source_schema_by_examples( + *, client: HttpClient, id: str, params: UpdateResourceSourceSchemaByExamplesParams +) -> ResourceSource: + """update_resource_source_schema_by_examples updates a resource source schema by providing example data payloads. + + Instead of manually defining the JSON schema structure, you send representative examples of the data + your resource source returns, and the system automatically infers the schema from these examples. + + Note: requires a Management API key. + """ + body = { + "examples": params.examples, + } + if params.schema_update_strategy is not None: + body["schema_update_strategy"] = params.schema_update_strategy.value + + rsp = client.post(path=f"resource-sources/{id}/schema-by-examples", body=body) + return ResourceSource.from_dict(rsp) diff --git a/src/gradient_labs/client.py b/src/gradient_labs/client.py index 1600b0d..387e3c9 100644 --- a/src/gradient_labs/client.py +++ b/src/gradient_labs/client.py @@ -83,11 +83,29 @@ from ._resource_type_update import update_resource_type, UpdateResourceTypeParams from ._resource_type_delete import delete_resource_type +from ._resource_source_create import create_resource_source, CreateResourceSourceParams +from ._resource_source_list import list_resource_sources, ResourceSourcesList +from ._resource_source_read import read_resource_source +from ._resource_source_update import update_resource_source, UpdateResourceSourceParams +from ._resource_source_update_schema_by_examples import ( + update_resource_source_schema_by_examples, + UpdateResourceSourceSchemaByExamplesParams, +) +from ._resource_source_delete import delete_resource_source + from ._http_client import HttpClient, API_BASE_URL from .tool import * from .note import Note from .secret import Secret from .resource_type import ResourceType, Scope, RefreshStrategy, SourceConfig +from .resource_source import ( + ResourceSource, + SourceType, + SchemaUpdateStrategy, + ResourceHTTPDefinition, + ResourceHTTPBodyDefinition, + ResourceWebhookDefinition, +) from .webhook import Webhook, WebhookEvent @@ -637,3 +655,67 @@ def delete_resource_type(self, *, id: str) -> None: client=self.http_client, id=id, ) + + # ==================== Resource Source Operations ==================== + + def create_resource_source( + self, *, params: CreateResourceSourceParams + ) -> ResourceSource: + """create_resource_source creates a new resource source. + + Note: requires a `Management` API key.""" + return create_resource_source( + client=self.http_client, + params=params, + ) + + def list_resource_sources(self) -> ResourceSourcesList: + """list_resource_sources returns all of your resource sources. + + Note: requires a `Management` API key.""" + return list_resource_sources(client=self.http_client) + + def read_resource_source(self, *, id: str) -> ResourceSource: + """read_resource_source retrieves a specific resource source by ID. + + Note: requires a `Management` API key.""" + return read_resource_source( + client=self.http_client, + id=id, + ) + + def update_resource_source( + self, *, id: str, params: UpdateResourceSourceParams + ) -> ResourceSource: + """update_resource_source updates an existing resource source. + + Note: requires a `Management` API key.""" + return update_resource_source( + client=self.http_client, + id=id, + params=params, + ) + + def update_resource_source_schema_by_examples( + self, *, id: str, params: UpdateResourceSourceSchemaByExamplesParams + ) -> ResourceSource: + """update_resource_source_schema_by_examples updates a resource source schema by providing example data payloads. + + Instead of manually defining the JSON schema structure, you send representative examples of the data + your resource source returns, and the system automatically infers the schema from these examples. + + Note: requires a `Management` API key.""" + return update_resource_source_schema_by_examples( + client=self.http_client, + id=id, + params=params, + ) + + def delete_resource_source(self, *, id: str) -> None: + """delete_resource_source permanently deletes a resource source. This action cannot be undone. + + Note: requires a `Management` API key.""" + delete_resource_source( + client=self.http_client, + id=id, + ) diff --git a/src/gradient_labs/resource_source.py b/src/gradient_labs/resource_source.py new file mode 100644 index 0000000..06b3018 --- /dev/null +++ b/src/gradient_labs/resource_source.py @@ -0,0 +1,114 @@ +from typing import Optional, Dict, Any +from enum import Enum +from dataclasses import dataclass +from dataclasses_json import dataclass_json +from datetime import datetime + + +class SourceType(str, Enum): + """SourceType describes how the data is fetched from the source.""" + + # HTTP means the source fetches data via HTTP requests. + HTTP: str = "http" + + # INTERNAL means the source is provided by the system. + INTERNAL: str = "internal" + + # WEBHOOK means the source receives data via webhooks. + WEBHOOK: str = "webhook" + + +class SchemaUpdateStrategy(str, Enum): + """SchemaUpdateStrategy represents how the new schema should be applied.""" + + # MERGE merges the inferred schema with the existing schema, preserving existing fields and adding new ones. + MERGE: str = "merge" + + # REPLACE completely replaces the existing schema with the newly inferred one. + REPLACE: str = "replace" + + +@dataclass_json +@dataclass(frozen=True) +class ResourceHTTPBodyDefinition: + """ResourceHTTPBodyDefinition determines how the HTTP request body is constructed.""" + + # Encoding determines how the HTTP request body will be encoded. + encoding: str + + # JSONTemplate contains a template that will be used to generate JSON for the HTTP request body. + # Only used when Encoding is "application/json". + json_template: Optional[str] = None + + # FormFieldTemplates contains templates for the values that will be form-encoded and used as the HTTP body. + # Only used when Encoding is "application/x-www-form-urlencoded". + form_field_templates: Optional[Dict[str, str]] = None + + +@dataclass_json +@dataclass(frozen=True) +class ResourceHTTPDefinition: + """ResourceHTTPDefinition contains configuration for HTTP actions.""" + + # Method is the HTTP request method that will be used. + method: str + + # URLTemplate contains a template used to construct the request URL. + url_template: str + + # HeaderTemplates contains templates for the values that will be used as request headers. + header_templates: Optional[Dict[str, str]] = None + + # Body determines how the HTTP request body is constructed. + body: Optional[ResourceHTTPBodyDefinition] = None + + +@dataclass_json +@dataclass(frozen=True) +class ResourceWebhookDefinition: + """ResourceWebhookDefinition contains configuration for webhook actions.""" + + # Name will be included in the `data.action` field of the webhook payload. + name: str + + +@dataclass_json +@dataclass +class ResourceSource: + """ResourceSource represents a resource source in the system.""" + + # ID is a generated ID for the resource source. + id: str + + # DisplayName free-text field to describe the source, eg. "Intercom User Attributes". + # Enforced unique per company. + display_name: str + + # SourceType describes how the data is fetched from the source. + source_type: SourceType + + # Created is the timestamp when the resource source was created. + created: datetime + + # Updated is the timestamp when the resource source was last updated. + updated: datetime + + # Description optional free-text field to describe the source in more detail. + description: Optional[str] = None + + # HTTPConfig can be set up by customers. + http_config: Optional[ResourceHTTPDefinition] = None + + # WebhookConfig can be set up by customers. + webhook_config: Optional[ResourceWebhookDefinition] = None + + # AttributeDescriptions optional raw attribute-level descriptions, used when generating the schema + # and as additional information for the agent. + # - key: a JSONPath eg. `$.name` or `$.items[*].name` + # - value: a description of the attribute + attribute_descriptions: Optional[Dict[str, str]] = None + + # Schema is the schema of the resource source, inferred from the source payloads. + # It is updated asynchronously as data is fetched from the source. + # Nil if the schema has not been inferred yet. Includes attribute descriptions if present. + schema: Optional[Any] = None