From 583e6a644b1f97ee0c7a3a6bf2805d29c9511746 Mon Sep 17 00:00:00 2001 From: Javier San Juan Cervera Date: Fri, 20 Sep 2019 11:26:04 +0200 Subject: [PATCH 01/10] Started work --- connect/models/__init__.py | 7 +-- connect/models/parameters.py | 7 +-- connect/models/product.py | 92 +++++++++++++++++++++++++++++++++-- connect/models/schemas.py | 9 ++++ connect/models/tier_config.py | 18 ++----- 5 files changed, 109 insertions(+), 24 deletions(-) diff --git a/connect/models/__init__.py b/connect/models/__init__.py index 8213d6f..e656b0a 100644 --- a/connect/models/__init__.py +++ b/connect/models/__init__.py @@ -16,10 +16,10 @@ from .marketplace import Activation, Agreement, AgreementStats, Contract, Marketplace from .parameters import Constraints, Param, ValueChoice from .product import CustomerUiSettings, Document, DownloadLink, Item, Product, \ - ProductCategory, ProductConfiguration, ProductFamily, ProductStats, ProductStatsInfo, Renewal + ProductCategory, ProductConfiguration, ProductConfigurationParameter, ProductFamily, \ + ProductStats, ProductStatsInfo, Renewal, Template from .server_error_response import ServerErrorResponse -from .tier_config import Configuration, Template, TierAccount, TierAccounts, TierConfig, \ - TierConfigRequest +from .tier_config import Configuration, TierAccount, TierAccounts, TierConfig, TierConfigRequest from .usage import UsageFile, UsageListing, UsageRecord, UsageRecords __all__ = [ @@ -56,6 +56,7 @@ 'Product', 'ProductCategory', 'ProductConfiguration', + 'ProductConfigurationParameter', 'ProductFamily', 'ProductStats', 'ProductStatsInfo', diff --git a/connect/models/parameters.py b/connect/models/parameters.py index 9b15324..8e805b9 100644 --- a/connect/models/parameters.py +++ b/connect/models/parameters.py @@ -5,8 +5,9 @@ from typing import List, Optional -import connect.models from .base import BaseModel +from .event import Events +from .marketplace import Marketplace from connect.models.schemas import ValueChoiceSchema, ConstraintsSchema, ParamSchema @@ -80,8 +81,8 @@ class Param(BaseModel): phase = None # type: Optional[str] """ (str|None) Param phase. """ - events = None # type: Optional[connect.models.Events] + events = None # type: Optional[Events] """ (:py:class:`.Events` | None) Events. """ - marketplace = None # type: Optional[connect.models.Marketplace] + marketplace = None # type: Optional[Marketplace] """ (:py:class:`.Marketplace` | None) Marketplace. """ diff --git a/connect/models/product.py b/connect/models/product.py index 0341138..6170c82 100644 --- a/connect/models/product.py +++ b/connect/models/product.py @@ -6,11 +6,31 @@ import datetime from typing import List, Optional, Union -import connect.models +from connect.config import Config from .base import BaseModel +""" +from .company import Company +from .event import Events +from .marketplace import Marketplace +from .parameters import Constraints, Param +""" +from connect.resources.base import ApiClient from connect.models.schemas import ProductConfigurationSchema, DownloadLinkSchema, DocumentSchema, \ CustomerUiSettingsSchema, ProductSchema, RenewalSchema, ItemSchema, ProductFamilySchema, \ - ProductCategorySchema, ProductStatsInfoSchema, ProductStatsSchema + ProductCategorySchema, ProductStatsInfoSchema, ProductStatsSchema, \ + ProductConfigurationParameterSchema, TemplateSchema + + +class Template(BaseModel): + """ Tier Template """ + + _schema = TemplateSchema() + + name = None # type: str + """ (str) Template name. """ + + representation = None # type: str + """ (str) Template representation. """ class ProductConfiguration(BaseModel): @@ -122,6 +142,32 @@ class ProductStats(BaseModel): """ (:py:class:`.ProductStatsInfo`) Contracts related to the product """ +class ProductConfigurationParameter(BaseModel): + """ Representation of Configuration Phase Parameter (CPP) Data object """ + + _schema = ProductConfigurationParameterSchema() + + value = None # type: str + """ (str|None) Configuration parameter value. """ + + parameter = None # type: Param + """ (:py:class:`.Param`) Full representation of parameter. """ + + marketplace = None # type: Marketplace + """ (:py:class:`.Marketplace` | None) Reference to Marketplace. """ + + item = None # type: Item + """ (:py:class:`.Item` | None) Reference to Item. """ + + events = None # type: Events + """ (:py:class:`.Events`) Product events. """ + + # Undocumented fields (they appear in PHP SDK) + + constraints = None # type: Constraints + """ (:py:class:`.Constraints`) Constraints. """ + + class Product(BaseModel): """ Represents basic marketing information about salable items, parameters, configurations, latest published version and connections. @@ -160,7 +206,7 @@ class Product(BaseModel): category = None # type: Optional[ProductCategory] """ (:py:class:`.ProductCategory` | None) Reference to ProductCategory Object. """ - owner = None # type: Optional[connect.models.Company] + owner = None # type: Optional[Company] """ (:py:class:`.Company` | None) """ latest = None # type: Optional[bool] @@ -171,6 +217,35 @@ class Product(BaseModel): stats = None # type: Optional[ProductStats] """ (:py:class:``.ProductStats) Statistics of product use, depends on account of callee. """ + def get_templates(self, config=None): + """ + :param Config config: Configuration to use, or None for environment config. + :return: List of all templates associated with the product. + :rtype: List[Template] + """ + text, _ = ApiClient(config or Config.get_instance(), + 'products/' + self.id + '/templates').get() + return Template.deserialize(text) + + def get_product_configurations(self, filters=None, config=None): + """ + :param Dict[str, Any] filters: Filters for the requests. Supported filters are: + - ``parameter.id`` + - ``parameter.title`` + - ``parameter.scope`` + - ``marketplace.id`` + - ``marketplace.name`` + - ``item.id`` + - ``item.name`` + - ``value`` + :param Config config: Configuration to use, or None for environment config. + :return: A list with the product configuration parameter data. + :rtype: List[ProductConfigurationParameter] + """ + text, _ = ApiClient(config or Config.get_instance(), + 'products/' + self.id + '/configurations').get(params=filters) + return ProductConfigurationParameter.deserialize(text) + class Renewal(BaseModel): """ Item renewal data. """ @@ -233,3 +308,14 @@ class Item(BaseModel): name = None # type: str """ (str) Name. """ + + def get_param_by_id(self, param_id): + """ + :param str param_id: Id of the parameter. + :return: A Param by ID, or None if it was not found. + :rtype: Param + """ + try: + return list(filter(lambda p: p.id == param_id, self.params))[0] + except IndexError: + return None diff --git a/connect/models/schemas.py b/connect/models/schemas.py index c4782f9..71c9d23 100644 --- a/connect/models/schemas.py +++ b/connect/models/schemas.py @@ -408,6 +408,15 @@ def make_object(self, data): return ProductStats(**data) +class ProductConfigurationParameterSchema(BaseSchema): + value = fields.Str(allow_none=True) + parameter = fields.Nested(ParamSchema) + marketplace = fields.Nested(MarketplaceSchema, allow_none=True) + item = fields.Nested(ItemSchema, allow_none=True) + events = fields.Nested(EventsSchema) + constraints = fields.Nested(ConstraintsSchema, allow_none=True) + + class ProductSchema(BaseSchema): name = fields.Str() icon = fields.Str() diff --git a/connect/models/tier_config.py b/connect/models/tier_config.py index a4ddcba..df6176a 100644 --- a/connect/models/tier_config.py +++ b/connect/models/tier_config.py @@ -13,9 +13,9 @@ from .event import Events from .marketplace import Activation from .parameters import Param -from .product import Product -from connect.models.schemas import TemplateSchema, TierAccountSchema, \ - TierAccountsSchema, TierConfigSchema, TierConfigRequestSchema, ConfigurationSchema +from .product import Product, Template +from connect.models.schemas import TierAccountSchema, TierAccountsSchema, TierConfigSchema, \ + TierConfigRequestSchema, ConfigurationSchema class TierAccount(BaseModel): @@ -51,18 +51,6 @@ class TierAccounts(BaseModel): """ (:py:class:`.TierAccount`) Level 2 TierAccount Object. """ -class Template(BaseModel): - """ Tier Template """ - - _schema = TemplateSchema() - - name = None # type: str - """ (str) Template name. """ - - representation = None # type: str - """ (str) Template representation. """ - - class Configuration(BaseModel): """ Configuration Phase Parameter Context-Bound Data Object. From 9785215085af5b2d79947cc90555af942ef5e32f Mon Sep 17 00:00:00 2001 From: Javier San Juan Cervera Date: Fri, 20 Sep 2019 11:39:35 +0200 Subject: [PATCH 02/10] Finished merging --- connect/models/__init__.py | 1 + connect/models/product.py | 104 +----------------- .../models/product_configuration_parameter.py | 38 +++++++ 3 files changed, 43 insertions(+), 100 deletions(-) diff --git a/connect/models/__init__.py b/connect/models/__init__.py index 4ba0dca..0cb1c01 100644 --- a/connect/models/__init__.py +++ b/connect/models/__init__.py @@ -36,6 +36,7 @@ from .product import Product from .product_category import ProductCategory from .product_configuration import ProductConfiguration +from .product_configuration_parameter import ProductConfigurationParameter from .product_family import ProductFamily from .product_stats import ProductStats from .product_stats_info import ProductStatsInfo diff --git a/connect/models/product.py b/connect/models/product.py index ca94ae6..5eed45f 100644 --- a/connect/models/product.py +++ b/connect/models/product.py @@ -13,32 +13,8 @@ from .product_configuration import ProductConfiguration from .product_stats import ProductStats from .schemas import ProductSchema - - -class ProductConfigurationParameter(BaseModel): - """ Representation of Configuration Phase Parameter (CPP) Data object """ - - _schema = ProductConfigurationParameterSchema() - - value = None # type: str - """ (str|None) Configuration parameter value. """ - - parameter = None # type: Param - """ (:py:class:`.Param`) Full representation of parameter. """ - - marketplace = None # type: Marketplace - """ (:py:class:`.Marketplace` | None) Reference to Marketplace. """ - - item = None # type: Item - """ (:py:class:`.Item` | None) Reference to Item. """ - - events = None # type: Events - """ (:py:class:`.Events`) Product events. """ - - # Undocumented fields (they appear in PHP SDK) - - constraints = None # type: Constraints - """ (:py:class:`.Constraints`) Constraints. """ +from connect.config import Config +from connect.resources.base import ApiClient class Product(BaseModel): @@ -96,6 +72,7 @@ def get_templates(self, config=None): :return: List of all templates associated with the product. :rtype: List[Template] """ + from .template import Template text, _ = ApiClient(config or Config.get_instance(), 'products/' + self.id + '/templates').get() return Template.deserialize(text) @@ -115,80 +92,7 @@ def get_product_configurations(self, filters=None, config=None): :return: A list with the product configuration parameter data. :rtype: List[ProductConfigurationParameter] """ + from .product_configuration_parameter import ProductConfigurationParameter text, _ = ApiClient(config or Config.get_instance(), 'products/' + self.id + '/configurations').get(params=filters) return ProductConfigurationParameter.deserialize(text) - - -class Renewal(BaseModel): - """ Item renewal data. """ - - _schema = RenewalSchema() - - from_ = None # type: datetime.datetime - """ (datetime.datetime) Date of renewal beginning. """ - - to = None # type: datetime.datetime - """ (datetime.datetime) Date of renewal end. """ - - period_delta = None # type: int - """ (int) Size of renewal period. """ - - period_uom = None # type: str - """ (str) Unit of measure for renewal period. One of: year, month, day, hour. """ - - -class Item(BaseModel): - """ A product item. """ - - _schema = ItemSchema() - - mpn = None # type: str - """ (str) Item manufacture part number. """ - - quantity = None # type: Union[int,float] - """ (int|float) Number of items of the type in the asset (-1 if unlimited) """ - - old_quantity = None # type: Union[int,float,None] - """ (int|float|None) Previous value of quantity. """ - - renewal = None # type: Optional[Renewal] - """ (:py:class:`.Renewal` | None) Parameters of renewal request - (empty for all other types). - """ - - params = None # type: List[connect.models.Param] - """ (List[:py:class:`.Param` | None] List of Item and Item x Marketplace Configuration Phase - Parameter Context-Bound Object - """ - - # Undocumented fields (they appear in PHP SDK) - - display_name = None # type: str - """ (str) Display name. """ - - global_id = None # type: str - """ (str) Global id. """ - - item_type = None # type: str - """ (str) Item type. """ - - period = None # type: str - """ (str) Period. """ - - type = None # type: str - """ (str) Type. """ - - name = None # type: str - """ (str) Name. """ - - def get_param_by_id(self, param_id): - """ - :param str param_id: Id of the parameter. - :return: A Param by ID, or None if it was not found. - :rtype: Param - """ - try: - return list(filter(lambda p: p.id == param_id, self.params))[0] - except IndexError: - return None diff --git a/connect/models/product_configuration_parameter.py b/connect/models/product_configuration_parameter.py index e69de29..27c6cab 100644 --- a/connect/models/product_configuration_parameter.py +++ b/connect/models/product_configuration_parameter.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- + +# This file is part of the Ingram Micro Cloud Blue Connect SDK. +# Copyright (c) 2019 Ingram Micro. All Rights Reserved. + +from .base import BaseModel +from .constraints import Constraints +from .events import Events +from .item import Item +from .marketplace import Marketplace +from .param import Param +from .schemas import ProductConfigurationParameterSchema + + +class ProductConfigurationParameter(BaseModel): + """ Representation of Configuration Phase Parameter (CPP) Data object """ + + _schema = ProductConfigurationParameterSchema() + + value = None # type: str + """ (str|None) Configuration parameter value. """ + + parameter = None # type: Param + """ (:py:class:`.Param`) Full representation of parameter. """ + + marketplace = None # type: Marketplace + """ (:py:class:`.Marketplace` | None) Reference to Marketplace. """ + + item = None # type: Item + """ (:py:class:`.Item` | None) Reference to Item. """ + + events = None # type: Events + """ (:py:class:`.Events`) Product events. """ + + # Undocumented fields (they appear in PHP SDK) + + constraints = None # type: Constraints + """ (:py:class:`.Constraints`) Constraints. """ From 53f9dd79e33b503fe814ae3519dc9b2b0ad67581 Mon Sep 17 00:00:00 2001 From: Javier San Juan Cervera Date: Fri, 20 Sep 2019 11:50:19 +0200 Subject: [PATCH 03/10] Fixed import crashes. --- connect/models/product.py | 3 ++- connect/models/schemas.py | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/connect/models/product.py b/connect/models/product.py index 5eed45f..09f977d 100644 --- a/connect/models/product.py +++ b/connect/models/product.py @@ -14,7 +14,6 @@ from .product_stats import ProductStats from .schemas import ProductSchema from connect.config import Config -from connect.resources.base import ApiClient class Product(BaseModel): @@ -72,6 +71,7 @@ def get_templates(self, config=None): :return: List of all templates associated with the product. :rtype: List[Template] """ + from connect.resources.base import ApiClient from .template import Template text, _ = ApiClient(config or Config.get_instance(), 'products/' + self.id + '/templates').get() @@ -92,6 +92,7 @@ def get_product_configurations(self, filters=None, config=None): :return: A list with the product configuration parameter data. :rtype: List[ProductConfigurationParameter] """ + from connect.resources.base import ApiClient from .product_configuration_parameter import ProductConfigurationParameter text, _ = ApiClient(config or Config.get_instance(), 'products/' + self.id + '/configurations').get(params=filters) diff --git a/connect/models/schemas.py b/connect/models/schemas.py index c38544e..df31aee 100644 --- a/connect/models/schemas.py +++ b/connect/models/schemas.py @@ -416,6 +416,11 @@ class ProductConfigurationParameterSchema(BaseSchema): events = fields.Nested(EventsSchema) constraints = fields.Nested(ConstraintsSchema, allow_none=True) + @post_load + def make_object(self, data): + from connect.models import ProductConfigurationParameter + return ProductConfigurationParameter(**data) + class ProductSchema(BaseSchema): name = fields.Str() From 0acdc724b654f7da3c57a9e6cf7d97252b4fe979 Mon Sep 17 00:00:00 2001 From: Javier San Juan Cervera Date: Fri, 20 Sep 2019 13:48:55 +0200 Subject: [PATCH 04/10] Added missing fields and methods to models. --- connect/models/__init__.py | 2 + connect/models/agreement.py | 5 ++ connect/models/asset.py | 67 ++++++++++++++++++++++++--- connect/models/configuration.py | 12 +++++ connect/models/country.py | 22 +++++++++ connect/models/fulfillment.py | 4 ++ connect/models/item.py | 12 +++++ connect/models/marketplace.py | 7 +++ connect/models/schemas.py | 36 +++++++++++--- connect/models/template.py | 3 ++ connect/models/tier_config.py | 5 ++ connect/models/tier_config_request.py | 14 ++++++ connect/models/usage_file.py | 1 + connect/models/usage_listing.py | 1 + connect/models/user.py | 3 ++ 15 files changed, 182 insertions(+), 12 deletions(-) create mode 100644 connect/models/country.py diff --git a/connect/models/__init__.py b/connect/models/__init__.py index 0cb1c01..78dc957 100644 --- a/connect/models/__init__.py +++ b/connect/models/__init__.py @@ -17,6 +17,7 @@ from .contact import Contact from .contact_info import ContactInfo from .contract import Contract +from .country import Country from .conversation import Conversation from .conversation_message import ConversationMessage from .customer_ui_settings import CustomerUiSettings @@ -69,6 +70,7 @@ 'Contact', 'ContactInfo', 'Contract', + 'Country', 'Conversation', 'ConversationMessage', 'CustomerUiSettings', diff --git a/connect/models/agreement.py b/connect/models/agreement.py index 45c8c96..0126d41 100644 --- a/connect/models/agreement.py +++ b/connect/models/agreement.py @@ -74,3 +74,8 @@ class Agreement(BaseModel): """ (:py:class:`.Marketplace` | None) Reference to marketplace object (for distribution agreement). """ + + # Undocumented fields (they appear in PHP SDK) + + name = None # type: str + """ (str) Name of Agreement. """ diff --git a/connect/models/asset.py b/connect/models/asset.py index c2cdba7..5690799 100644 --- a/connect/models/asset.py +++ b/connect/models/asset.py @@ -6,8 +6,12 @@ from typing import List, Optional from .base import BaseModel +from .configuration import Configuration from .connection import Connection +from .contract import Contract +from .fulfillment import Fulfillment from .item import Item +from .marketplace import Marketplace from .param import Param from .product import Product from .tier_accounts import TierAccounts @@ -58,7 +62,10 @@ class Asset(BaseModel): """ (str) Identification for asset object on eCommerce. """ external_uid = None # type: Optional[str] - """ (str|None) Id of asset in eCommerce system """ + """ (str|None) Id of asset in eCommerce system. """ + + external_name = None # type: Optional[str] + """ (str|None) Name of asset. """ product = None # type: Product """ (:py:class:`.Product`) Product object reference. """ @@ -66,8 +73,11 @@ class Asset(BaseModel): connection = None # type: Connection """ (:py:class:`.Connection`) Connection object. """ - items = None # type: List[Item] - """ (List[:py:class:`.Item`]) List of asset product items. """ + contract = None # type: Contract + """ (:py:class:`.Contract`) Contract Object reference. """ + + marketplace = None # type: Marketplace + """ (:py:class:`.Marketplace`) Marketplace Object reference. """ params = None # type: List[Param] """ (List[:py:class:`.Param`]) List of product parameter objects. """ @@ -75,15 +85,34 @@ class Asset(BaseModel): tiers = None # type: TierAccounts """ (:py:class:`.TierAccounts`) Supply chain accounts. """ - def get_param_by_id(self, id_): + items = None # type: List[Item] + """ (List[:py:class:`.Item`]) List of asset product items. """ + + configuration = None # type: Configuration + """ (:py:class:`.Configuration`) List of Product and Marketplace Configuration Phase Parameter + Context-Bound Object. """ + + def get_param_by_id(self, param_id): """ Get a parameter of the asset. - :param str id_: Id of the the parameter to get. + :param str param_id: Id of the the parameter to get. :return: The parameter with the given id, or ``None`` if it was not found. :rtype: :py:class:`.Param` | None """ try: - return list(filter(lambda param: param.id == id_, self.params))[0] + return list(filter(lambda param: param.id == param_id, self.params))[0] + except IndexError: + return None + + def get_item_by_id(self, item_id): + """ Get an item of the asset. + + :param str item_id: Id of the item to get. + :return: The item with the given id, or ``None`` if it was not found. + :rtype: :py:class:`.Item` | None + """ + try: + return list(filter(lambda item: item.id == item_id, self.items))[0] except IndexError: return None @@ -98,3 +127,29 @@ def get_item_by_mpn(self, mpn): return list(filter(lambda item: item.mpn == mpn, self.items))[0] except IndexError: return None + + def get_item_by_global_id(self, global_id): + """ Get an item of the asset. + + :param str global_id: Global id of the item to get. + :return: The item with the given global id, or ``None`` if it was not found. + :rtype: :py:class:`.Item` | None + """ + try: + return list(filter(lambda item: item.global_id == global_id, self.items))[0] + except IndexError: + return None + + def get_requests(self, config=None): + """ Get the requests for this asset. + + :param Config config: Config object or ``None`` to use environment config (default). + :return: The requests for this asset. + :rtype: List[Fulfillment] + """ + from connect.config import Config + from connect.resources.base import ApiClient + text, _ = ApiClient( + config or Config.get_instance(), + 'assets/' + self.id + '/requests').get() + return Fulfillment.deserialize(text) diff --git a/connect/models/configuration.py b/connect/models/configuration.py index 82e372b..158fe7f 100644 --- a/connect/models/configuration.py +++ b/connect/models/configuration.py @@ -25,3 +25,15 @@ class Configuration(BaseModel): params = None # type: List[Param] """ (List[:py:class:`.Param`]) """ + + def get_param_by_id(self, param_id): + """ Get a parameter of the configuration. + + :param str param_id: Id of the the parameter to get. + :return: The parameter with the given id, or ``None`` if it was not found. + :rtype: :py:class:`.Param` | None + """ + try: + return list(filter(lambda param: param.id == param_id, self.params))[0] + except IndexError: + return None diff --git a/connect/models/country.py b/connect/models/country.py new file mode 100644 index 0000000..90792b9 --- /dev/null +++ b/connect/models/country.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- + +# This file is part of the Ingram Micro Cloud Blue Connect SDK. +# Copyright (c) 2019 Ingram Micro. All Rights Reserved. + +from .base import BaseModel +from .schemas import CountrySchema + + +class Country(BaseModel): + """ An instance of a hub. """ + + _schema = CountrySchema() + + name = None # type: str + """ (str) Country name """ + + icon = None # type: str + """ (str) Icon path. """ + + zone = None # type: str + """ (str) Geographical zone. """ diff --git a/connect/models/fulfillment.py b/connect/models/fulfillment.py index 591b619..2677e3e 100644 --- a/connect/models/fulfillment.py +++ b/connect/models/fulfillment.py @@ -10,6 +10,7 @@ from .contract import Contract from .conversation import Conversation from .marketplace import Marketplace +from .user import User from .schemas import FulfillmentSchema @@ -72,6 +73,9 @@ class Fulfillment(BaseModel): marketplace = None # type: Marketplace """ (:py:class:`.Marketplace`) Marketplace object. """ + assignee = None # type: User + """ (:py:class:`.User`) Details of the user assigned to the request. """ + @property def new_items(self): """ diff --git a/connect/models/item.py b/connect/models/item.py index 368f5dd..8a42855 100644 --- a/connect/models/item.py +++ b/connect/models/item.py @@ -54,3 +54,15 @@ class Item(BaseModel): name = None # type: str """ (str) Name. """ + + def get_param_by_id(self, param_id): + """ Get a parameter of the item. + + :param str param_id: Id of the the parameter to get. + :return: The parameter with the given id, or ``None`` if it was not found. + :rtype: :py:class:`.Param` | None + """ + try: + return list(filter(lambda param: param.id == param_id, self.params))[0] + except IndexError: + return None diff --git a/connect/models/marketplace.py b/connect/models/marketplace.py index 6a4e518..7d919c3 100644 --- a/connect/models/marketplace.py +++ b/connect/models/marketplace.py @@ -7,6 +7,7 @@ from .base import BaseModel from .company import Company +from .country import Country from .ext_id_hub import ExtIdHub from .schemas import MarketplaceSchema @@ -44,3 +45,9 @@ class Marketplace(BaseModel): zone = None # type: str """ (str) Zone where the marketplace is located, there can be following zones: AF, NA, OC, AS, EU, SA (It is continents). """ + + countries = None # type: List[Country] + """ List[:py:class:`.Country`] List of country objects associated with marketplace. """ + + sourcing = None # type: bool + """ (bool) Is marketplace available for sourcing. """ diff --git a/connect/models/schemas.py b/connect/models/schemas.py index df31aee..991d205 100644 --- a/connect/models/schemas.py +++ b/connect/models/schemas.py @@ -130,6 +130,7 @@ def make_object(self, data): class UserSchema(BaseSchema): name = fields.Str() + email = fields.Str(allow_none=True) @post_load def make_object(self, data): @@ -256,6 +257,17 @@ def make_object(self, data): return Marketplace(**data) +class CountrySchema(BaseSchema): + name = fields.Str() + icon = fields.Str() + zone = fields.Str() + + @post_load + def make_object(self, data): + from connect.models import Country + return Country(**data) + + class ParamSchema(BaseSchema): name = fields.Str() description = fields.Str() @@ -272,6 +284,7 @@ class ParamSchema(BaseSchema): phase = fields.Str(allow_none=True) events = fields.Nested(EventsSchema, allow_none=True) marketplace = fields.Nested(MarketplaceSchema, allow_none=True) + countries = fields.Nested(CountrySchema, many=True, allow_none=True) @post_load def make_object(self, data): @@ -315,6 +328,7 @@ class AgreementSchema(BaseSchema): agreements = fields.Nested('AgreementSchema', many=True) parent = fields.Nested('AgreementSchema', only=('id', 'name'), allow_none=True) marketplace = fields.Nested(MarketplaceSchema, only=('id', 'name'), allow_none=True) + name = fields.Str(allow_none=True) @post_load def make_object(self, data): @@ -456,6 +470,7 @@ def make_object(self, data): class TemplateSchema(BaseSchema): name = fields.Str() representation = fields.Str() + body = fields.Str() @post_load def make_object(self, data): @@ -512,13 +527,17 @@ class AssetSchema(BaseSchema): status = fields.Str() external_id = fields.Str() external_uid = fields.Str(allow_none=True) + external_name = fields.Str(allow_none=True) product = fields.Nested(ProductSchema, only=('id', 'name')) connection = fields.Nested( ConnectionSchema, only=('id', 'type', 'provider', 'vendor'), ) - items = fields.Nested(ItemSchema, many=True) + contract = fields.Nested(ContractSchema, allow_none=True) + marketplace = fields.Nested(MarketplaceSchema, allow_none=True) params = fields.Nested(ParamSchema, many=True) tiers = fields.Nested(TierAccountsSchema) + items = fields.Nested(ItemSchema, many=True) + configuration = fields.Nested(ConfigurationSchema, allow_none=True) @post_load def make_object(self, data): @@ -527,17 +546,18 @@ def make_object(self, data): class FulfillmentSchema(BaseSchema): - activation_key = fields.Str() - asset = fields.Nested(AssetSchema) - status = fields.Str() type = fields.Str() - updated = fields.DateTime() created = fields.DateTime() + updated = fields.DateTime() + status = fields.Str() + params_form_url = fields.Str() + activation_key = fields.Str() reason = fields.Str() note = fields.Str() - params_form_url = fields.Str() + asset = fields.Nested(AssetSchema) contract = fields.Nested(ContractSchema, only=('id', 'name')) marketplace = fields.Nested(MarketplaceSchema, only=('id', 'name')) + assignee = fields.Nested(UserSchema, allow_none=True) @post_load def make_object(self, data): @@ -558,6 +578,7 @@ class TierConfigSchema(BaseSchema): marketplace = fields.Nested(MarketplaceSchema) configuration = fields.Nested(ConfigurationSchema, allow_none=True) events = fields.Nested(EventsSchema, allow_none=True) + status = fields.Str(allow_none=True) @post_load def make_object(self, data): @@ -581,6 +602,9 @@ class TierConfigRequestSchema(BaseSchema): activation = fields.Nested(ActivationSchema, allow_none=True) notes = fields.Str(allow_none=True) events = fields.Nested(EventsSchema, allow_none=True) + tiers = fields.Nested(TierAccountsSchema, allow_none=True) + marketplace = fields.Nested(MarketplaceSchema, allow_none=True) + contract = fields.Nested(ContractSchema, allow_none=True) @post_load def make_object(self, data): diff --git a/connect/models/template.py b/connect/models/template.py index c3501c4..ed36f3c 100644 --- a/connect/models/template.py +++ b/connect/models/template.py @@ -17,3 +17,6 @@ class Template(BaseModel): representation = None # type: str """ (str) Template representation. """ + + body = None # type: str + """ (str) Template body. """ diff --git a/connect/models/tier_config.py b/connect/models/tier_config.py index 14e44b5..7491199 100644 --- a/connect/models/tier_config.py +++ b/connect/models/tier_config.py @@ -65,6 +65,11 @@ class TierConfig(BaseModel): events = None # type: Optional[Events] """ (:py:class:`.Events` | None) Tier Config events. """ + # Undocumented fields (they appear in PHP SDK) + + status = None # type: str + """ (str) TierConfig status. """ + @classmethod def get(cls, account_id, product_id, config=None): """ diff --git a/connect/models/tier_config_request.py b/connect/models/tier_config_request.py index ac112a0..e8200c7 100644 --- a/connect/models/tier_config_request.py +++ b/connect/models/tier_config_request.py @@ -7,11 +7,14 @@ from .activation import Activation from .base import BaseModel +from .contract import Contract from .events import Events +from .marketplace import Marketplace from .param import Param from .product import Product from .template import Template from .tier_account import TierAccount +from .tier_accounts import TierAccounts from .tier_config import TierConfig from .user import User from .schemas import TierConfigRequestSchema @@ -69,6 +72,17 @@ class TierConfigRequest(BaseModel): events = None # type: Optional[Events] """ (:py:class:`.Events` | None) Tier Config request Events. """ + # Undocumented fields (they appear in PHP SDK) + + tiers = None # type: Optional[TierAccounts] + """ (:py:class:`.TierAccounts` | None) TierConfig tier accounts. """ + + marketplace = None # type: Optional[Marketplace] + """ (:py:class:`.Marketplace` | None) TierConfig marketplace. """ + + contract = None # type: Optional[Contract] + """ (:py:class:`.Contract` | None) TierConfig contract. """ + def get_param_by_id(self, id_): """ Get a Tier Config Request parameter. diff --git a/connect/models/usage_file.py b/connect/models/usage_file.py index 310dce9..69952d5 100644 --- a/connect/models/usage_file.py +++ b/connect/models/usage_file.py @@ -73,6 +73,7 @@ class UsageFile(BaseModel): error_details = None # type: str """ (str) In case of invalid file, this field will contain errors related to the file. """ + # TODO: In the docs it is error_details, on PHP SDK it appears as error_detail records = None # type: UsageRecords """ (:py:class:`.UsageRecords`) UsageRecords Object. """ diff --git a/connect/models/usage_listing.py b/connect/models/usage_listing.py index e6b44b9..bd4a1f9 100644 --- a/connect/models/usage_listing.py +++ b/connect/models/usage_listing.py @@ -28,6 +28,7 @@ class UsageListing(BaseModel): """ (str) Creation time. """ # Undocumented fields (they appear in PHP SDK) + vendor = None # type: Company """ (:py:class:`.Company`) Vendor Object. """ diff --git a/connect/models/user.py b/connect/models/user.py index 36b74ce..ba68051 100644 --- a/connect/models/user.py +++ b/connect/models/user.py @@ -14,3 +14,6 @@ class User(BaseModel): name = None # type: str """ (str) User name. """ + + email = None # type: str + """ (str) User email. """ From e500b5cb5ea9d919625773f99627ab10b4f5c995 Mon Sep 17 00:00:00 2001 From: Javier San Juan Cervera Date: Fri, 20 Sep 2019 13:51:22 +0200 Subject: [PATCH 05/10] Fixed import bug. --- connect/models/asset.py | 2 +- connect/models/usage_listing.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/connect/models/asset.py b/connect/models/asset.py index 5690799..f347584 100644 --- a/connect/models/asset.py +++ b/connect/models/asset.py @@ -9,7 +9,6 @@ from .configuration import Configuration from .connection import Connection from .contract import Contract -from .fulfillment import Fulfillment from .item import Item from .marketplace import Marketplace from .param import Param @@ -149,6 +148,7 @@ def get_requests(self, config=None): """ from connect.config import Config from connect.resources.base import ApiClient + from .fulfillment import Fulfillment text, _ = ApiClient( config or Config.get_instance(), 'assets/' + self.id + '/requests').get() diff --git a/connect/models/usage_listing.py b/connect/models/usage_listing.py index bd4a1f9..df168f2 100644 --- a/connect/models/usage_listing.py +++ b/connect/models/usage_listing.py @@ -28,7 +28,7 @@ class UsageListing(BaseModel): """ (str) Creation time. """ # Undocumented fields (they appear in PHP SDK) - + vendor = None # type: Company """ (:py:class:`.Company`) Vendor Object. """ From 08eafe4ad97ffdc0164192760f8e4c020e184f47 Mon Sep 17 00:00:00 2001 From: Javier San Juan Cervera Date: Tue, 24 Sep 2019 10:28:24 +0200 Subject: [PATCH 06/10] IF config products is an empty string, the array of products will be empty. --- connect/config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/connect/config.py b/connect/config.py index 21abbe0..f403754 100644 --- a/connect/config.py +++ b/connect/config.py @@ -59,7 +59,7 @@ def __init__(self, api_url=None, api_key=None, products=None, file=None): self._api_key = api_key self._api_url = api_url if api_url.endswith('/') else api_url + '/' self._products = [products] \ - if isinstance(products, str) \ + if isinstance(products, str) and products \ else products or [] # Store first created instance From 0b82bab4f96caa062d4fa0e7ce9507c0c67126b7 Mon Sep 17 00:00:00 2001 From: Javier San Juan Cervera Date: Wed, 25 Sep 2019 13:15:50 +0200 Subject: [PATCH 07/10] Added some unit tests --- tests/data/response2.json | 117 +++++++++++++++++++++++++++++++++++++- tests/test_models.py | 55 +++++++++++++++++- 2 files changed, 170 insertions(+), 2 deletions(-) diff --git a/tests/data/response2.json b/tests/data/response2.json index 159ad61..3740ef1 100644 --- a/tests/data/response2.json +++ b/tests/data/response2.json @@ -20,7 +20,66 @@ "id": "TEAM_ST3L2T1Y", "mpn": "TEAM-ST3L2T1Y", "old_quantity": "0", - "quantity": "100" + "quantity": "100", + "global_id": "XXX", + "params": [ + { + "value": "Value 1", + "events": { + "updated": { + "at": "2019-08-27T14:21:23+00:00", + "by": { + "id": "UR-841-574-187", + "name": "Marc Serrat" + } + }, + "created": { + "at": "2019-08-26T10:42:56+00:00", + "by": { + "id": "UR-841-574-187", + "name": "Marc Serrat" + } + } + }, + "id": "item_parameter", + "title": "item_parameter", + "description": "item_parameter", + "type": "text", + "scope": "item", + "phase": "configuration", + "constraints": { + "hidden": false, + "required": false, + "unique": false + } + }, + { + "value": "item", + "events": { + "updated": { + "at": "2019-08-26T10:44:10+00:00" + }, + "created": { + "at": "2019-08-26T10:44:10+00:00", + "by": { + "id": "UR-841-574-187", + "name": "Marc Serrat" + } + } + }, + "id": "item_per_marketplace", + "title": "item_per_marketplace", + "description": "item_per_marketplace", + "type": "text", + "scope": "item_marketplace", + "phase": "configuration", + "constraints": { + "hidden": false, + "required": true, + "unique": false + } + } + ] }, { "id": "TEAM_ST3L2TAC1M", @@ -134,6 +193,62 @@ "name": "ACME Reseller" }, "tier2": {} + }, + "configuration": { + "params": [ + { + "value": "product_value", + "events": { + "updated": { + "at": "2019-08-26T10:42:49+00:00" + }, + "created": { + "at": "2019-08-26T10:42:49+00:00", + "by": { + "id": "UR-841-574-187", + "name": "Marc Serrat" + } + } + }, + "id": "product_configuration", + "title": "product_configuration", + "description": "product_configuration", + "type": "text", + "scope": "product", + "phase": "configuration", + "constraints": { + "hidden": false, + "required": true, + "unique": false + } + }, + { + "value": "product_marketplace_config", + "events": { + "updated": { + "at": "2019-08-26T10:43:27+00:00" + }, + "created": { + "at": "2019-08-26T10:43:27+00:00", + "by": { + "id": "UR-841-574-187", + "name": "Marc Serrat" + } + } + }, + "id": "product_Marketplace_configuration", + "title": "product_Marketplace_configuration", + "description": "product_Marketplace_configuration", + "type": "text", + "scope": "marketplace", + "phase": "configuration", + "constraints": { + "hidden": false, + "required": true, + "unique": false + } + } + ] } }, "created": "2018-09-03T10:28:18.472670+00:00", diff --git a/tests/test_models.py b/tests/test_models.py index 21e844b..5a21ad4 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -9,7 +9,7 @@ import six from mock import MagicMock, patch -from connect.models import Asset, Param, Fulfillment, Item, TierConfig +from connect.models import Asset, Param, Fulfillment, Item, TierConfig, Configuration from connect.resources import FulfillmentAutomation from .common import Response, load_str @@ -150,10 +150,63 @@ def test_asset_methods(): assert not asset.get_param_by_id('invalid-id') # Get item by id + assert isinstance(asset.get_item_by_id('TEAM_ST3L2T1Y'), Item) + assert asset.get_item_by_id('TEAM_ST3L2T1Y').mpn == 'TEAM-ST3L2T1Y' + assert not asset.get_item_by_id('invalid_id') + + # Get item by mpn assert isinstance(asset.get_item_by_mpn('TEAM-ST3L2T1Y'), Item) assert asset.get_item_by_mpn('TEAM-ST3L2T1Y').mpn == 'TEAM-ST3L2T1Y' assert not asset.get_item_by_mpn('invalid-mpn') + # Get item by global id + assert isinstance(asset.get_item_by_global_id('XXX'), Item) + assert asset.get_item_by_global_id('XXX').mpn == 'TEAM-ST3L2T1Y' + assert not asset.get_item_by_mpn('invalid_id') + + # Get requests + requests = asset.get_requests() + assert isinstance(requests, list) + assert len(requests) == 1 + assert requests[0].id == 'PR-5620-6510-8214' + + +@patch('requests.get', MagicMock(return_value=_get_response_ok2())) +def test_asset_configuration(): + # Get asset + requests = FulfillmentAutomation().list() + assert len(requests) == 1 + assert isinstance(requests[0], Fulfillment) + asset = requests[0].asset + assert isinstance(asset, Asset) + + assert isinstance(asset.configuration, Configuration) + assert len(asset.configuration.params) == 2 + + product_param = asset.configuration.get_param_by_id('product_configuration') + assert isinstance(product_param, Param) + assert product_param.id == 'product_configuration' + assert product_param.scope == 'product' + + marketplace_param = asset.configuration.get_param_by_id('product_Marketplace_configuration') + assert isinstance(marketplace_param, Param) + assert marketplace_param.id == 'product_Marketplace_configuration' + assert marketplace_param.scope == 'marketplace' + + +@patch('requests.get', MagicMock(return_value=_get_response_ok2())) +def test_asset_item(): + # Get asset + requests = FulfillmentAutomation().list() + assert len(requests) == 1 + assert isinstance(requests[0], Fulfillment) + asset = requests[0].asset + assert isinstance(asset, Asset) + + item = asset.get_item_by_mpn('TEAM-ST3L2T1Y') + param = item.get_param_by_id('item_parameter') + assert isinstance(param, Param) + @patch('requests.get') def test_get_tier_config(get_mock): From 97d8c1e19fbab474133e17863b8ec3a3dbd2de55 Mon Sep 17 00:00:00 2001 From: Javier San Juan Cervera Date: Thu, 26 Sep 2019 16:34:20 +0200 Subject: [PATCH 08/10] ContactInfo.state now optional, since it was coming empty from some requests. --- connect/models/contact_info.py | 2 +- connect/models/schemas.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/connect/models/contact_info.py b/connect/models/contact_info.py index 13f1223..c91eb49 100644 --- a/connect/models/contact_info.py +++ b/connect/models/contact_info.py @@ -24,7 +24,7 @@ class ContactInfo(BaseModel): country = None # type: str """ (str) Country code. """ - state = None # type: str + state = None # type: Optional[str] """ (str) State name. """ city = None # type: str diff --git a/connect/models/schemas.py b/connect/models/schemas.py index 991d205..af2d59c 100644 --- a/connect/models/schemas.py +++ b/connect/models/schemas.py @@ -77,7 +77,7 @@ class ContactInfoSchema(BaseSchema): contact = fields.Nested(ContactSchema) country = fields.Str() postal_code = fields.Str() - state = fields.Str() + state = fields.Str(allow_none=True) @post_load def make_object(self, data): From 15a6f69b9f46002b566e8ec372742a9c82b1cbf2 Mon Sep 17 00:00:00 2001 From: Javier San Juan Cervera Date: Mon, 30 Sep 2019 09:49:18 +0200 Subject: [PATCH 09/10] Fixed some imports --- connect/logger/logger.py | 5 +++-- connect/models/product.py | 6 +++--- connect/resources/automation_engine.py | 3 ++- connect/resources/base.py | 3 ++- connect/resources/directory.py | 4 +++- connect/resources/fulfillment_automation.py | 8 ++++++-- connect/resources/template.py | 3 ++- connect/resources/tier_config_automation.py | 6 ++++-- connect/resources/usage_automation.py | 4 +++- connect/resources/usage_file_automation.py | 3 ++- 10 files changed, 30 insertions(+), 15 deletions(-) diff --git a/connect/logger/logger.py b/connect/logger/logger.py index eaef84c..e6dba9a 100644 --- a/connect/logger/logger.py +++ b/connect/logger/logger.py @@ -7,8 +7,9 @@ import json import logging import os + from logging.config import dictConfig -from connect.models import BaseModel, Fulfillment +from connect.models.base import BaseModel with open(os.path.join(os.path.dirname(__file__), 'config.json')) as config_file: config = json.load(config_file) @@ -23,7 +24,7 @@ def log_request_data(args): base = " %(levelname)-6s; %(asctime)s; %(name)-6s; %(module)s:%(funcName)s:line"\ "-%(lineno)d: %(message)s" sformat = args[0].id + base - if isinstance(args[0], Fulfillment): + if hasattr(args[0], 'asset') and hasattr(args[0].asset, 'id'): sformat = args[0].asset.id + " " + sformat [handler.setFormatter(logging.Formatter(sformat, "%I:%M:%S")) for handler in logger.handlers] diff --git a/connect/models/product.py b/connect/models/product.py index 09f977d..6f5e0b9 100644 --- a/connect/models/product.py +++ b/connect/models/product.py @@ -11,9 +11,12 @@ from .customer_ui_settings import CustomerUiSettings from .product_category import ProductCategory from .product_configuration import ProductConfiguration +from .product_configuration_parameter import ProductConfigurationParameter from .product_stats import ProductStats from .schemas import ProductSchema +from .template import Template from connect.config import Config +from connect.resources.base import ApiClient class Product(BaseModel): @@ -72,7 +75,6 @@ def get_templates(self, config=None): :rtype: List[Template] """ from connect.resources.base import ApiClient - from .template import Template text, _ = ApiClient(config or Config.get_instance(), 'products/' + self.id + '/templates').get() return Template.deserialize(text) @@ -92,8 +94,6 @@ def get_product_configurations(self, filters=None, config=None): :return: A list with the product configuration parameter data. :rtype: List[ProductConfigurationParameter] """ - from connect.resources.base import ApiClient - from .product_configuration_parameter import ProductConfigurationParameter text, _ = ApiClient(config or Config.get_instance(), 'products/' + self.id + '/configurations').get(params=filters) return ProductConfigurationParameter.deserialize(text) diff --git a/connect/resources/automation_engine.py b/connect/resources/automation_engine.py index 331f213..b30d07f 100644 --- a/connect/resources/automation_engine.py +++ b/connect/resources/automation_engine.py @@ -6,7 +6,8 @@ from typing import Any, Dict from connect.logger import function_log -from connect.models import ActivationTileResponse, BaseModel +from connect.models.activation_tile_response import ActivationTileResponse +from connect.models.base import BaseModel from .base import BaseResource from .template import TemplateResource diff --git a/connect/resources/base.py b/connect/resources/base.py index d1dc043..002951d 100644 --- a/connect/resources/base.py +++ b/connect/resources/base.py @@ -12,7 +12,8 @@ from connect.config import Config from connect.exceptions import ServerError from connect.logger import function_log, logger -from connect.models import BaseModel, ServerErrorResponse +from connect.models.base import BaseModel +from connect.models.server_error_response import ServerErrorResponse class ApiClient(object): diff --git a/connect/resources/directory.py b/connect/resources/directory.py index ecd500c..3fc3c7a 100644 --- a/connect/resources/directory.py +++ b/connect/resources/directory.py @@ -4,7 +4,9 @@ # Copyright (c) 2019 Ingram Micro. All Rights Reserved. from connect.config import Config -from connect.models import Asset, Product, TierConfig +from connect.models.asset import Asset +from connect.models.product import Product +from connect.models.tier_config import TierConfig from connect.resources.base import ApiClient diff --git a/connect/resources/fulfillment_automation.py b/connect/resources/fulfillment_automation.py index f7a00da..6fa43f4 100644 --- a/connect/resources/fulfillment_automation.py +++ b/connect/resources/fulfillment_automation.py @@ -10,8 +10,12 @@ from connect.exceptions import FailRequest, InquireRequest, SkipRequest from connect.logger import logger, function_log -from connect.models import ActivationTemplateResponse, ActivationTileResponse, Param, \ - Fulfillment, TierConfigRequest, Conversation +from connect.models.activation_template_response import ActivationTemplateResponse +from connect.models.activation_tile_response import ActivationTileResponse +from connect.models.param import Param +from connect.models.fulfillment import Fulfillment +from connect.models.tier_config_request import TierConfigRequest +from connect.models.conversation import Conversation from .automation_engine import AutomationEngine diff --git a/connect/resources/template.py b/connect/resources/template.py index 2825f6b..a514c77 100644 --- a/connect/resources/template.py +++ b/connect/resources/template.py @@ -5,7 +5,8 @@ from typing import List, Any, Dict -from connect.models import ActivationTemplateResponse, ActivationTileResponse +from connect.models.activation_template_response import ActivationTemplateResponse +from connect.models.activation_tile_response import ActivationTileResponse from .base import BaseResource diff --git a/connect/resources/tier_config_automation.py b/connect/resources/tier_config_automation.py index ffc0e2f..d382f2d 100644 --- a/connect/resources/tier_config_automation.py +++ b/connect/resources/tier_config_automation.py @@ -7,8 +7,10 @@ from connect.exceptions import FailRequest, InquireRequest, SkipRequest from connect.logger import logger, function_log -from connect.models import ActivationTemplateResponse, ActivationTileResponse, Param, \ - TierConfigRequest +from connect.models.activation_template_response import ActivationTemplateResponse +from connect.models.activation_tile_response import ActivationTileResponse +from connect.models.param import Param +from connect.models.tier_config_request import TierConfigRequest from .automation_engine import AutomationEngine diff --git a/connect/resources/usage_automation.py b/connect/resources/usage_automation.py index 7fd45e3..bafc5fd 100644 --- a/connect/resources/usage_automation.py +++ b/connect/resources/usage_automation.py @@ -13,7 +13,9 @@ from connect.exceptions import FileCreationError, FileRetrievalError from connect.logger import logger -from connect.models import UsageListing, UsageFile, UsageRecord +from connect.models.usage_listing import UsageListing +from connect.models.usage_file import UsageFile +from connect.models.usage_record import UsageRecord from .automation_engine import AutomationEngine diff --git a/connect/resources/usage_file_automation.py b/connect/resources/usage_file_automation.py index 415b02a..ee70350 100644 --- a/connect/resources/usage_file_automation.py +++ b/connect/resources/usage_file_automation.py @@ -7,7 +7,8 @@ from connect.exceptions import SkipRequest, UsageFileAction from connect.logger import logger -from connect.models import BaseModel, UsageFile +from connect.models.base import BaseModel +from connect.models.usage_file import UsageFile from .automation_engine import AutomationEngine From f07095dc2199455bfc876e01faffe77569869687 Mon Sep 17 00:00:00 2001 From: Javier San Juan Cervera Date: Mon, 30 Sep 2019 09:50:31 +0200 Subject: [PATCH 10/10] Update product.py --- connect/models/product.py | 1 - 1 file changed, 1 deletion(-) diff --git a/connect/models/product.py b/connect/models/product.py index 6f5e0b9..27342bc 100644 --- a/connect/models/product.py +++ b/connect/models/product.py @@ -74,7 +74,6 @@ def get_templates(self, config=None): :return: List of all templates associated with the product. :rtype: List[Template] """ - from connect.resources.base import ApiClient text, _ = ApiClient(config or Config.get_instance(), 'products/' + self.id + '/templates').get() return Template.deserialize(text)