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 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/__init__.py b/connect/models/__init__.py index b2d4878..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 @@ -36,6 +37,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 @@ -68,6 +70,7 @@ 'Contact', 'ContactInfo', 'Contract', + 'Country', 'Conversation', 'ConversationMessage', 'CustomerUiSettings', @@ -87,6 +90,7 @@ 'Product', 'ProductCategory', 'ProductConfiguration', + 'ProductConfigurationParameter', 'ProductFamily', 'ProductStats', 'ProductStatsInfo', 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..f347584 100644 --- a/connect/models/asset.py +++ b/connect/models/asset.py @@ -6,8 +6,11 @@ from typing import List, Optional from .base import BaseModel +from .configuration import Configuration from .connection import Connection +from .contract import Contract from .item import Item +from .marketplace import Marketplace from .param import Param from .product import Product from .tier_accounts import TierAccounts @@ -58,7 +61,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 +72,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 +84,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 +126,30 @@ 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 + from .fulfillment import Fulfillment + 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/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/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/product.py b/connect/models/product.py index 2043cdd..27342bc 100644 --- a/connect/models/product.py +++ b/connect/models/product.py @@ -11,8 +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): @@ -63,3 +67,32 @@ 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) diff --git a/connect/models/product_configuration_parameter.py b/connect/models/product_configuration_parameter.py new file mode 100644 index 0000000..27c6cab --- /dev/null +++ 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. """ diff --git a/connect/models/schemas.py b/connect/models/schemas.py index d3a8b35..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): @@ -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): @@ -408,6 +422,20 @@ 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) + + @post_load + def make_object(self, data): + from connect.models import ProductConfigurationParameter + return ProductConfigurationParameter(**data) + + class ProductSchema(BaseSchema): name = fields.Str() icon = fields.Str() @@ -442,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): @@ -498,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): @@ -513,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): @@ -544,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): @@ -567,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..df168f2 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. """ 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 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):