-
Notifications
You must be signed in to change notification settings - Fork 20
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Add client for SystemLink products API (#69)
- Loading branch information
1 parent
e1dc1c6
commit d53a9f4
Showing
17 changed files
with
855 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
.. _api_tag_page: | ||
|
||
nisystemlink.clients.product | ||
====================== | ||
|
||
.. autoclass:: nisystemlink.clients.product.ProductClient | ||
:exclude-members: __init__ | ||
|
||
.. automethod:: __init__ | ||
.. automethod:: create_products | ||
.. automethod:: get_products | ||
.. automethod:: query_products | ||
.. automethod:: query_product_values | ||
.. automethod:: update_products | ||
.. automethod:: delete_product | ||
.. automethod:: delete_products | ||
|
||
.. automodule:: nisystemlink.clients.product.models | ||
:members: | ||
:imported-members: | ||
|
||
.. automodule:: nisystemlink.clients.product.utilities | ||
:members: |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
from nisystemlink.clients.core import HttpConfiguration | ||
from nisystemlink.clients.product import ProductClient | ||
from nisystemlink.clients.product.models import ( | ||
Product, | ||
ProductField, | ||
QueryProductsRequest, | ||
QueryProductValuesRequest, | ||
) | ||
|
||
name = "Example Name" | ||
family = "Example Family" | ||
|
||
|
||
def create_some_products(): | ||
"""Create two example products on your server.""" | ||
new_products = [ | ||
Product( | ||
part_number="Example 123 AA", | ||
name=name, | ||
family=family, | ||
keywords=["original keyword"], | ||
properties={"original property key": "yes"}, | ||
), | ||
Product( | ||
part_number="Example 123 AA1", | ||
name=name, | ||
family=family, | ||
keywords=["original keyword"], | ||
properties={"original property key": "original"}, | ||
), | ||
] | ||
create_response = client.create_products(new_products) | ||
return create_response | ||
|
||
|
||
# Setup the server configuration to point to your instance of SystemLink Enterprise | ||
server_configuration = HttpConfiguration( | ||
server_uri="https://yourserver.yourcompany.com", | ||
api_key="YourAPIKeyGeneratedFromSystemLink", | ||
) | ||
client = ProductClient(configuration=server_configuration) | ||
|
||
# Get all the products using the continuation token in batches of 100 at a time. | ||
response = client.get_products_paged(take=100, return_count=True) | ||
all_products = response.products | ||
while response.continuation_token: | ||
response = client.get_products_paged( | ||
take=100, continuation_token=response.continuation_token, return_count=True | ||
) | ||
all_products.extend(response.products) | ||
|
||
create_response = create_some_products() | ||
|
||
# use get for first product created | ||
created_product = client.get_product(create_response.products[0].id) | ||
|
||
# Query products without continuation | ||
query_request = QueryProductsRequest( | ||
filter=f'family="{family}" && name="{name}"', | ||
return_count=True, | ||
order_by=ProductField.FAMILY, | ||
) | ||
response = client.query_products_paged(query_request) | ||
|
||
# Update the first product that you just created and replace the keywords | ||
updated_product = create_response.products[0] | ||
updated_product.keywords = ["new keyword"] | ||
updated_product.properties = {"new property key": "new value"} | ||
update_response = client.update_products([create_response.products[0]], replace=True) | ||
|
||
# Query for just the ids of products that match the family | ||
values_query = QueryProductValuesRequest( | ||
filter=f'family="{family}"', field=ProductField.ID | ||
) | ||
values_response = client.query_product_values(query=values_query) | ||
|
||
# delete each created product individually by id | ||
for product in create_response.products: | ||
client.delete_product(product.id) | ||
|
||
# Create some more and delete them with a single call to delete. | ||
create_response = create_some_products() | ||
client.delete_products([product.id for product in create_response.products]) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
from ._product_client import ProductClient | ||
|
||
# flake8: noqa |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,182 @@ | ||
"""Implementation of Product Client""" | ||
|
||
from typing import List, Optional | ||
|
||
from nisystemlink.clients import core | ||
from nisystemlink.clients.core._uplink._base_client import BaseClient | ||
from nisystemlink.clients.core._uplink._methods import delete, get, post | ||
from nisystemlink.clients.product.models import Product | ||
from uplink import Field, Query, returns | ||
|
||
from . import models | ||
|
||
|
||
class ProductClient(BaseClient): | ||
def __init__(self, configuration: Optional[core.HttpConfiguration] = None): | ||
"""Initialize an instance. | ||
Args: | ||
configuration: Defines the web server to connect to and information about | ||
how to connect. If not provided, the | ||
:class:`HttpConfigurationManager <nisystemlink.clients.core.HttpConfigurationManager>` | ||
is used to obtain the configuration. | ||
Raises: | ||
ApiException: if unable to communicate with the Product Service. | ||
""" | ||
if configuration is None: | ||
configuration = core.HttpConfigurationManager.get_configuration() | ||
super().__init__(configuration, base_path="/nitestmonitor/v2/") | ||
|
||
@post("products", args=[Field("products")]) | ||
def create_products( | ||
self, products: List[Product] | ||
) -> models.CreateProductsPartialSuccess: | ||
"""Creates one or more products and returns errors for failed creations. | ||
Args: | ||
products: A list of products to attempt to create. | ||
Returns: A list of created products, products that failed to create, and errors for | ||
failures. | ||
Raises: | ||
ApiException: if unable to communicate with the ``/nitestmonitor`` service of provided invalid | ||
arguments. | ||
""" | ||
... | ||
|
||
@get( | ||
"products", | ||
args=[Query("continuationToken"), Query("take"), Query("returnCount")], | ||
) | ||
def get_products_paged( | ||
self, | ||
continuation_token: Optional[str] = None, | ||
take: Optional[int] = None, | ||
return_count: Optional[bool] = None, | ||
) -> models.PagedProducts: | ||
"""Reads a list of products. | ||
Args: | ||
continuation_token: The token used to paginate results. | ||
take: The number of products to get in this request. | ||
return_count: Whether or not to return the total number of products available. | ||
Returns: | ||
A list of products. | ||
Raises: | ||
ApiException: if unable to communicate with the ``/nitestmonitor`` Service | ||
or provided an invalid argument. | ||
""" | ||
... | ||
|
||
@get("products/{id}") | ||
def get_product(self, id: str) -> models.Product: | ||
"""Retrieves a single product by id. | ||
Args: | ||
id (str): Unique ID of a products. | ||
Returns: | ||
The single product matching `id` | ||
Raises: | ||
ApiException: if unable to communicate with the ``/nitestmonitor`` Service | ||
or provided an invalid argument. | ||
""" | ||
... | ||
|
||
@post("query-products") | ||
def query_products_paged( | ||
self, query: models.QueryProductsRequest | ||
) -> models.PagedProducts: | ||
"""Queries for products that match the filter. | ||
Args: | ||
query : The query contains a DynamicLINQ query string in addition to other details | ||
about how to filter and return the list of products. | ||
Returns: | ||
A paged list of products with a continuation token to get the next page. | ||
Raises: | ||
ApiException: if unable to communicate with the ``/nitestmonitor`` Service or provided invalid | ||
arguments. | ||
""" | ||
... | ||
|
||
@returns.json # type: ignore | ||
@post("query-product-values") | ||
def query_product_values( | ||
self, query: models.QueryProductValuesRequest | ||
) -> List[str]: | ||
"""Queries for products that match the query and returns a list of the requested field. | ||
Args: | ||
query : The query for the fields you want. | ||
Returns: | ||
A list of the values of the field you requested. | ||
Raises: | ||
ApiException: if unable to communicate with the ``/nitestmonitor`` Service or provided | ||
invalid arguments. | ||
""" | ||
... | ||
|
||
@post("update-products", args=[Field("products"), Field("replace")]) | ||
def update_products( | ||
self, products: List[Product], replace: bool = False | ||
) -> models.CreateProductsPartialSuccess: | ||
"""Updates a list of products with optional field replacement. | ||
Args: | ||
`products`: A list of products to update. Products are matched for update by id. | ||
`replace`: Replace the existing fields instead of merging them. Defaults to `False`. | ||
If this is `True`, then `keywords` and `properties` for the product will be | ||
replaced by what is in the `products` provided in this request. | ||
If this is `False`, then the `keywords` and `properties` in this request will | ||
merge with what is already present in the server resource. | ||
Returns: A list of updates products, products that failed to update, and errors for | ||
failures. | ||
Raises: | ||
ApiException: if unable to communicate with the ``/nitestmonitor`` Service | ||
or provided an invalid argument. | ||
""" | ||
... | ||
|
||
@delete("products/{id}") | ||
def delete_product(self, id: str) -> None: | ||
"""Deletes a single product by id. | ||
Args: | ||
id (str): Unique ID of a product. | ||
Raises: | ||
ApiException: if unable to communicate with the ``/nitestmonitor`` Service | ||
or provided an invalid argument. | ||
""" | ||
... | ||
|
||
@post("delete-products", args=[Field("ids")]) | ||
def delete_products( | ||
self, ids: List[str] | ||
) -> Optional[models.DeleteProductsPartialSuccess]: | ||
"""Deletes multiple products. | ||
Args: | ||
ids (List[str]): List of unique IDs of products. | ||
Returns: | ||
A partial success if any products failed to delete, or None if all | ||
products were deleted successfully. | ||
Raises: | ||
ApiException: if unable to communicate with the ``/nitestmonitor`` Service | ||
or provided an invalid argument. | ||
""" | ||
... |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
from ._product import Product | ||
from ._create_products_partial_success import CreateProductsPartialSuccess | ||
from ._delete_products_partial_success import DeleteProductsPartialSuccess | ||
from ._paged_products import PagedProducts | ||
from ._query_products_request import ( | ||
QueryProductsRequest, | ||
ProductField, | ||
QueryProductValuesRequest, | ||
) | ||
|
||
# flake8: noqa |
22 changes: 22 additions & 0 deletions
22
nisystemlink/clients/product/models/_create_products_partial_success.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
from typing import List, Optional | ||
|
||
from nisystemlink.clients.core import ApiError | ||
from nisystemlink.clients.core._uplink._json_model import JsonModel | ||
from nisystemlink.clients.product.models import Product | ||
|
||
|
||
class CreateProductsPartialSuccess(JsonModel): | ||
products: List[Product] | ||
"""The list of products that were successfully created.""" | ||
|
||
failed: Optional[List[Product]] = None | ||
"""The list of products that were not created. | ||
If this is `None`, then all products were successfully created. | ||
""" | ||
|
||
error: Optional[ApiError] = None | ||
"""Error messages for products that were not created. | ||
If this is `None`, then all products were successfully created. | ||
""" |
17 changes: 17 additions & 0 deletions
17
nisystemlink/clients/product/models/_delete_products_partial_success.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
from typing import List, Optional | ||
|
||
from nisystemlink.clients.core import ApiError | ||
from nisystemlink.clients.core._uplink._json_model import JsonModel | ||
|
||
|
||
class DeleteProductsPartialSuccess(JsonModel): | ||
"""The result of deleting multiple products when one or more products could not be deleted.""" | ||
|
||
ids: List[str] | ||
"""The IDs of the products that were successfully deleted.""" | ||
|
||
failed: Optional[List[str]] | ||
"""The IDs of the products that could not be deleted.""" | ||
|
||
error: Optional[ApiError] | ||
"""The error that occurred when deleting the products.""" |
Oops, something went wrong.