Skip to content

Commit

Permalink
LITE-28130 Add capability to Install/Update product in CBC using PLM API
Browse files Browse the repository at this point in the history
  • Loading branch information
rahulmondal committed Jul 20, 2023
1 parent bbe6dd2 commit 51e15a3
Show file tree
Hide file tree
Showing 15 changed files with 814 additions and 58 deletions.
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

0 comments on commit 51e15a3

Please sign in to comment.