Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LITE-28130 Add capability to Install/Update product in CBC using PLM API #34

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 3 additions & 0 deletions CBCClientHowTo.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,14 @@ endpoint = '******'
client_id = '*****'
# CBC OAuth Key
client_secret = '*****'
# CBC Extension App ID
app_id = '*****'

client = CBCClient(
endpoint=endpoint,
oauth_key=client_id,
oauth_secret=client_secret,
app_id=app_id,
)

```
Expand Down
1 change: 1 addition & 0 deletions connect_ext_ppr/client/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ def execute_request(
raise ClientError(
message=f'{type(e).__name__} : {str(e)}',
status_code=response.status_code,
response=response,
cause=e,
)
else:
Expand Down
5 changes: 5 additions & 0 deletions connect_ext_ppr/client/exception.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
from requests import Response


class ClientError(RuntimeError):
def __init__(
self,
message: str,
status_code: int = None,
response: Response = None,
cause: Exception = None,
):
self.message = message
self.response = response
self.status_code = status_code
self.cause = cause
21 changes: 12 additions & 9 deletions connect_ext_ppr/client/ns.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from functools import cached_property

from connect_ext_ppr.client.mixin import (
ActionMixin,
GetMixin,
Expand Down Expand Up @@ -65,7 +67,15 @@ class Service(
NSBase,
ActionMixin,
):
def _get_service_path(self):
def __init__(self, client, aps_type: str, path: str):
super().__init__(
client=client,
path=path,
)
self.aps_type = aps_type

@cached_property
def service_path(self):
aps_type_object = self.client.execute_request(
method='GET',
path=f'{self.path}/aps/2/resources/?implementing({self.aps_type})',
Expand All @@ -79,13 +89,6 @@ def _get_service_path(self):
service_id = aps_type_object[0]['aps']['id']
return f'{self.path}/aps/2/resources/{service_id}'

def __init__(self, client, aps_type: str, path: str):
super().__init__(
client=client,
path=path,
)
self.aps_type = aps_type

def __getattr__(self, name):
if '_' in name:
name = name.replace('_', '-')
Expand All @@ -101,7 +104,7 @@ def collection(self, name: str):

return Collection(
self.client,
f'{self._get_service_path()}/{name}',
f'{self.service_path}/{name}',
)

def get(self, **kwargs):
Expand Down
31 changes: 0 additions & 31 deletions connect_ext_ppr/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,11 @@

from connect_ext_ppr.errors import ExtensionValidationError
from connect_ext_ppr.db import get_db_ctx_manager
from connect_ext_ppr.models.cbc_extenstion import HubCredential
from connect_ext_ppr.models.deployment import Deployment
from connect_ext_ppr.models.replicas import Product
from connect_ext_ppr.utils import _parse_json_schema_error
from connect_ext_ppr.constants import PPR_SCHEMA

from sqlalchemy import text


def insert_product_from_listing(db, listing_data, logger):
product_data = listing_data['product']
Expand Down Expand Up @@ -93,31 +90,3 @@ def update_product(data, config, logger):
product.version = data['version']
db.add(product)
db.commit()


def get_hub_credentials(hub_id, db):

query = text(
'SELECT DISTINCT h.hub_id, g.app_instance_id, h.controller_uri, '
'c.oauth_key, c.oauth_secret '
'FROM hub_instances h '
'INNER JOIN global_app_configuration g ON g.hub_uuid = h.extension_resource_uid '
'INNER JOIN configuration c ON c.product_id = g.hub_uuid '
'WHERE h.hub_id = :hub_id',
)

query = query.columns(
HubCredential.hub_id,
HubCredential.app_id,
HubCredential.controller_url,
HubCredential.oauth_key,
HubCredential.oauth_secret,
)

return db.query(
HubCredential,
).from_statement(
query,
).params(
hub_id=hub_id,
).first()
Empty file.
37 changes: 37 additions & 0 deletions connect_ext_ppr/services/cbc_extension.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
from connect_ext_ppr.models.cbc_extenstion import HubCredential

from sqlalchemy import text


HUB_CREDENTIAL_QUERY = '''
SELECT DISTINCT h.hub_id,
g.app_instance_id,
h.controller_uri,
c.oauth_key,
c.oauth_secret
FROM hub_instances h
INNER JOIN global_app_configuration g ON g.hub_uuid = h.extension_resource_uid
INNER JOIN configuration c ON c.product_id = g.hub_uuid
WHERE h.hub_id = :hub_id
'''


def get_hub_credentials(hub_id, db):

query = text(HUB_CREDENTIAL_QUERY)

query = query.columns(
HubCredential.hub_id,
HubCredential.app_id,
HubCredential.controller_url,
HubCredential.oauth_key,
HubCredential.oauth_secret,
)

return db.query(
HubCredential,
).from_statement(
query,
).params(
hub_id=hub_id,
).first()
83 changes: 83 additions & 0 deletions connect_ext_ppr/services/cbc_hub.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
from functools import cached_property

from connect_ext_ppr.client import CBCClient
from connect_ext_ppr.client.exception import ClientError
from connect_ext_ppr.models.cbc_extenstion import HubCredential


class CBCService:

PLM_TYPE = 'http://com.odin.platform/inhouse-products/application'
SUBSCRIPTION_TYPE = 'http://parallels.com/aps/types/pa/subscription'

def __init__(self, hub_credential: HubCredential, verify_certificate: bool = False):
self.hub_credential = hub_credential
self.__validate_hub_credentials_object()

self.client = CBCClient(
endpoint=hub_credential.controller_url,
oauth_key=hub_credential.oauth_key,
oauth_secret=hub_credential.oauth_secret,
app_id=hub_credential.app_id,
verify_certificate=verify_certificate,
)
self.__validate_client()

def __validate_hub_credentials_object(self):
if not self.hub_credential:
raise ValueError('`hub_credential` must be not be empty.')
if not isinstance(self.hub_credential, HubCredential):
raise TypeError('`hub_credential` must be object of HubCredential.')
if not self.hub_credential.controller_url:
raise ValueError('`hub_credential.controller_url` must be not be empty.')
if not self.hub_credential.oauth_key:
raise ValueError('`hub_credential.oauth_key` must be not be empty.')
if not self.hub_credential.oauth_secret:
raise ValueError('`hub_credential.oauth_secret` must be not be empty.')

def __validate_client(self):
try:
self.client.execute_request(
method='GET',
path=f'{self.hub_credential.controller_url}/aps',
)
except ClientError:
raise ValueError('hub_credential are not valid!')

@cached_property
def primary_subscription_id(self):
subscriptions = self.subscription_service.get(
subscriptionId=1,
)
return subscriptions[0]['aps']['id']

@cached_property
def plm_service(self):
return self.client(self.PLM_TYPE)

@cached_property
def subscription_service(self):
return self.client(self.SUBSCRIPTION_TYPE)

def get_product_details(self, product_id: str):
return self.plm_service.appDetails[product_id].get(
fulfillmentSystem='connect',
)

def install_product(self, product_id: str):
self.plm_service.appDetails[product_id].action(
name='import',
payload={
'subscriptionId': self.primary_subscription_id,
'fulfillmentSystem': 'connect',
},
)

def update_product(self, product_id: str):
return self.plm_service.appDetails[product_id].action(
name='upgrade',
payload={
'subscriptionId': self.primary_subscription_id,
'fulfillmentSystem': 'connect',
},
)
12 changes: 10 additions & 2 deletions tests/client/test_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ def test_service_discovery_collection_with_underscore(


@responses.activate
def test_service_discovery_collection_without_underscore(
def test_service_discovery_collection(
cbc_endpoint,
cbc_client,
flat_catalog_type,
Expand All @@ -104,10 +104,18 @@ def test_service_discovery_collection_without_underscore(
json=flat_catalog_type_objects,
)

collection = cbc_client(flat_catalog_type).flatcatalog
service = cbc_client(flat_catalog_type)

# First time - calls service discovery API
collection = service.flatcatalog

assert collection.path == f'{cbc_endpoint}/aps/2/resources/{service_id}/flatcatalog'

# 2nd time - no call to service discovery API
collection = service.subscriptions

assert collection.path == f'{cbc_endpoint}/aps/2/resources/{service_id}/subscriptions'


@responses.activate
def test_service_discovery_collection_wrong_collection_value_type(
Expand Down
Loading