Skip to content
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
35406ef
fix:QuerySpecsOptionalResponseModel
Feb 24, 2025
52dfaee
fix:PR Comments
Feb 24, 2025
58e5bd8
feat: add projection and update response models
Shri2109 Feb 24, 2025
b6df853
refactor: update query products response model
Shri2109 Feb 25, 2025
fee23c0
feat:NewResponseModel
Feb 25, 2025
f94c805
refactor: include a common products response
Shri2109 Feb 25, 2025
256b80f
fix:DeleteUnwantedResponseModel
Feb 25, 2025
c84db16
refactor: include unqiue models for product request and response
Shri2109 Feb 25, 2025
06118e0
refactor: change from ProductRequest to Product
Shri2109 Feb 26, 2025
b619beb
fix:PRComments
Feb 26, 2025
7872a98
Merge branch 'users/sam-rishi/fix-projection' of https://github.com/s…
Feb 26, 2025
10714ad
fix:PRComments
Feb 26, 2025
3cf7a28
refactor: add separate projection and order-by enums and add update r…
Shri2109 Feb 26, 2025
fb9653a
refactor: update request models and product enums
Shri2109 Feb 26, 2025
f0eefca
Merge pull request #3 from shri2k2/users/sam-rishi/fix-spec-response-…
sam-rishi Feb 27, 2025
cfb7835
refactor: update specification enum name
Shri2109 Feb 27, 2025
e35715b
feat: add retry mechanism for both specs and product clients
Shri2109 Feb 28, 2025
1e73458
test: reformat spec column projection test case
Shri2109 Mar 3, 2025
070d68f
refactor: change from ProductResponse to Product
Shri2109 Mar 3, 2025
8f83083
fix:PRComments
Mar 3, 2025
023e3a7
fix:PRComments
Mar 3, 2025
d7edfdf
fix:PRComments
Mar 3, 2025
1fab252
fix:PRComments
Mar 3, 2025
55bed9b
fix:PRComments
Mar 3, 2025
110c2da
refactor:SpecsModelsAndProductDocstring
Mar 4, 2025
1017a4a
docs: update product doc string
Shri2109 Mar 4, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions examples/product/products.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
from nisystemlink.clients.core import HttpConfiguration
from nisystemlink.clients.product import ProductClient
from nisystemlink.clients.product.models import (
Product,
CreateProductRequest,
ProductField,
ProductOrderBy,
QueryProductsRequest,
QueryProductValuesRequest,
)
Expand All @@ -14,14 +15,14 @@
def create_some_products():
"""Create two example products on your server."""
new_products = [
Product(
CreateProductRequest(
part_number="Example 123 AA",
name=name,
family=family,
keywords=["original keyword"],
properties={"original property key": "yes"},
),
Product(
CreateProductRequest(
part_number="Example 123 AA1",
name=name,
family=family,
Expand Down Expand Up @@ -58,9 +59,9 @@ def create_some_products():
query_request = QueryProductsRequest(
filter=f'family="{family}" && name="{name}"',
return_count=True,
order_by=ProductField.FAMILY,
order_by=ProductOrderBy.FAMILY,
)
response = client.query_products_paged(query_request)
query_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]
Expand Down
2 changes: 1 addition & 1 deletion examples/spec/update_and_delete_specs.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,4 @@
# query all specs
response = client.query_specs(QuerySpecificationsRequest(product_ids=[product]))
if response.specs:
client.delete_specs(ids=[spec.id for spec in response.specs])
client.delete_specs(ids=[spec.id for spec in response.specs if spec.id])
7 changes: 3 additions & 4 deletions nisystemlink/clients/product/_product_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
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
Expand All @@ -30,7 +29,7 @@ def __init__(self, configuration: Optional[core.HttpConfiguration] = None):

@post("products", args=[Field("products")])
def create_products(
self, products: List[Product]
self, products: List[models.CreateProductRequest]
) -> models.CreateProductsPartialSuccess:
"""Creates one or more products and returns errors for failed creations.

Expand Down Expand Up @@ -73,7 +72,7 @@ def get_products_paged(
...

@get("products/{id}")
def get_product(self, id: str) -> models.Product:
def get_product(self, id: str) -> models.ProductResponse:
"""Retrieves a single product by id.

Args:
Expand Down Expand Up @@ -128,7 +127,7 @@ def query_product_values(

@post("update-products", args=[Field("products"), Field("replace")])
def update_products(
self, products: List[Product], replace: bool = False
self, products: List[models.UpdateProductRequest], replace: bool = False
) -> models.CreateProductsPartialSuccess:
"""Updates a list of products with optional field replacement.

Expand Down
7 changes: 5 additions & 2 deletions nisystemlink/clients/product/models/__init__.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
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,
ProductOrderBy,
ProductProjection,
QueryProductsRequest,
QueryProductValuesRequest,
)
from ._product_response import ProductResponse
from ._product_request import CreateProductRequest, UpdateProductRequest

# flake8: noqa
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,21 @@

from nisystemlink.clients.core import ApiError
from nisystemlink.clients.core._uplink._json_model import JsonModel
from nisystemlink.clients.product.models import Product
from nisystemlink.clients.product.models._product_request import CreateProductRequest
from nisystemlink.clients.product.models._product_response import ProductResponse


class CreateProductsPartialSuccess(JsonModel):
products: List[Product]
products: List[ProductResponse]
"""The list of products that were successfully created."""

failed: Optional[List[Product]] = None
failed: Optional[List[CreateProductRequest]]
"""The list of products that were not created.

If this is `None`, then all products were successfully created.
"""

error: Optional[ApiError] = None
error: Optional[ApiError]
"""Error messages for products that were not created.

If this is `None`, then all products were successfully created.
Expand Down
8 changes: 5 additions & 3 deletions nisystemlink/clients/product/models/_paged_products.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
from typing import List, Optional

from nisystemlink.clients.core._uplink._with_paging import WithPaging
from nisystemlink.clients.product.models import Product
from nisystemlink.clients.product.models._product_response import ProductResponse


class PagedProducts(WithPaging):
"""The response for a Products query containing matched products."""
"""The response containing the list of products, total count of products and the continuation
token if applicable.
"""

products: List[Product]
products: List[ProductResponse]
"""A list of all the products in this page."""

total_count: Optional[int]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,11 @@
from datetime import datetime
from typing import Dict, List, Optional

from nisystemlink.clients.core._uplink._json_model import JsonModel


class Product(JsonModel):
class BaseProductRequest(JsonModel):
"""Contains information about a product."""

id: Optional[str]
"""The globally unique id of the product."""

part_number: str
"""The part number is the unique identifier of a product within a single org.

Usually the part number refers to a specific revision or version of a given product."""

name: Optional[str]
"""The name of the product.

Expand All @@ -29,9 +20,6 @@ class Product(JsonModel):
names within it.
"""

updated_at: Optional[datetime]
"""The last time that this product was updated."""

file_ids: Optional[List[str]]
"""A list of file ids that are attached to this product."""

Expand All @@ -43,3 +31,18 @@ class Product(JsonModel):

workspace: Optional[str]
"""The id of the workspace that this product belongs to."""


class CreateProductRequest(BaseProductRequest):

part_number: str
"""The part number is the unique identifier of a product within a single org.

Usually the part number refers to a specific revision or version of a given product."""


class UpdateProductRequest(BaseProductRequest):
"""This is the request model to update a product."""

id: str
"""The globally unique id of the product."""
51 changes: 51 additions & 0 deletions nisystemlink/clients/product/models/_product_response.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
from datetime import datetime
from typing import Dict, List, Optional

from nisystemlink.clients.core._uplink._json_model import JsonModel


class ProductResponse(JsonModel):
"""Contains information about a product, where all the fields are optional.

- This is because when using query products' projection, user can request for any of the available
fields. So, we are making sure that all the available fields are optional.

- Also while creating a product it is not mandatory that the product should be created with all the fields.
"""

id: Optional[str]
"""The globally unique id of the product."""

part_number: Optional[str]
"""The part number is the unique identifier of a product within a single org.

Usually the part number refers to a specific revision or version of a given product."""

name: Optional[str]
"""The name of the product.

Usually the name is used to refer to several part numbers that all have the same name but
different revisions or versions.
"""

family: Optional[str]
"""The family that that this product belongs to.

Usually the family is a grouping above product name. A family usually has multiple product
names within it.
"""

updated_at: Optional[datetime]
"""The last time that this product was updated."""

file_ids: Optional[List[str]]
"""A list of file ids that are attached to this product."""

keywords: Optional[List[str]]
"""A list of keywords that categorize this product."""

properties: Optional[Dict[str, str]]
"""A list of custom properties for this product."""

workspace: Optional[str]
"""The id of the workspace that this product belongs to."""
40 changes: 38 additions & 2 deletions nisystemlink/clients/product/models/_query_products_request.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from pydantic import Field


class ProductField(str, Enum):
class ProductOrderBy(str, Enum):
"""The valid ways to order a product query."""

ID = "ID"
Expand All @@ -15,6 +15,32 @@ class ProductField(str, Enum):
UPDATED_AT = "UPDATED_AT"


class ProductField(str, Enum):
"""An enumeration of product fields for which the values can be queried for."""

ID = "ID"
FAMILY = "FAMILY"
PART_NUMBER = "PART_NUMBER"
NAME = "NAME"
UPDATED_AT = "UPDATED_AT"


class ProductProjection(str, Enum):
"""An enumeration of all fields in a Product. These are used to project the required fields
from the API response.
"""

ID = "ID"
FAMILY = "FAMILY"
PART_NUMBER = "PART_NUMBER"
NAME = "NAME"
UPDATED_AT = "UPDATED_AT"
WORKSPACE = "WORKSPACE"
KEYWORDS = "KEYWORDS"
PROPERTIES = "PROPERTIES"
FILE_IDS = "FILE_IDS"


class QueryProductsBase(JsonModel):
filter: Optional[str] = None
"""
Expand Down Expand Up @@ -50,7 +76,7 @@ class QueryProductsBase(JsonModel):

class QueryProductsRequest(QueryProductsBase):

order_by: Optional[ProductField] = Field(None, alias="orderBy")
order_by: Optional[ProductOrderBy] = Field(None, alias="orderBy")
"""Specifies the fields to use to sort the products.

By default, products are sorted by `id`
Expand All @@ -61,11 +87,21 @@ class QueryProductsRequest(QueryProductsBase):

By default, this value is `false` and products are sorted in ascending order.
"""

projection: Optional[List[ProductProjection]] = None
"""Specifies the product fields to project.

When a field value is given here, the corresponding field will be present in all returned products,
and all unspecified fields will be excluded. If no projection is specified, all product fields
will be returned.
"""

take: Optional[int] = None
"""Maximum number of products to return in the current API response.

Uses the default if the specified value is negative. The default value is `1000` products.
"""

continuation_token: Optional[str] = None
"""Allows users to continue the query at the next product that matches the given criteria.

Expand Down
8 changes: 6 additions & 2 deletions nisystemlink/clients/product/utilities/_file_utilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,17 @@

from nisystemlink.clients.product._product_client import ProductClient
from nisystemlink.clients.product.models._paged_products import PagedProducts
from nisystemlink.clients.product.models._product import Product
from nisystemlink.clients.product.models._product_response import (
ProductResponse,
)
from nisystemlink.clients.product.models._query_products_request import (
QueryProductsRequest,
)


def get_products_linked_to_file(client: ProductClient, file_id: str) -> List[Product]:
def get_products_linked_to_file(
client: ProductClient, file_id: str
) -> List[ProductResponse]:
"""Gets a list of all the products that are linked to the file.

Args:
Expand Down
8 changes: 6 additions & 2 deletions nisystemlink/clients/spec/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,12 @@
CreateSpecificationsRequest,
)
from ._delete_specs_request import DeleteSpecificationsPartialSuccess
from ._query_specs import QuerySpecificationsRequest, QuerySpecifications
from ._query_specs import (
QuerySpecificationsRequest,
QuerySpecifications,
SpecificationProjection,
SpecificationWithOptionalFields,
)
from ._specification import (
Specification,
SpecificationCreation,
Expand All @@ -22,7 +27,6 @@
SpecificationType,
SpecificationUpdated,
SpecificationUserManaged,
SpecificationWithHistory,
)
from ._update_specs_request import (
UpdatedSpecification,
Expand Down
Loading