diff --git a/connect_ext_ppr/models/deployment.py b/connect_ext_ppr/models/deployment.py index e5ff6de..df32cf3 100644 --- a/connect_ext_ppr/models/deployment.py +++ b/connect_ext_ppr/models/deployment.py @@ -73,3 +73,6 @@ class MarketplaceConfiguration(Model): marketplace = db.Column(db.String(16)) deployment = db.Column(db.ForeignKey(Deployment.id), nullable=True) deployment_request = db.Column(db.ForeignKey(DeploymentRequest.id), nullable=True) + ppr_id = db.Column(db.String, db.ForeignKey(PPRVersion.id)) + + ppr = relationship('PPRVersion', foreign_keys='MarketplaceConfiguration.ppr_id') diff --git a/connect_ext_ppr/schemas.py b/connect_ext_ppr/schemas.py index 7942727..f43d63a 100644 --- a/connect_ext_ppr/schemas.py +++ b/connect_ext_ppr/schemas.py @@ -118,3 +118,12 @@ class DeploymentRequestSchema(NonNullSchema): class Config: orm_mode = True + + +class MarketplaceSchema(NonNullSchema): + id: str + name: str + icon: str + external_id: Optional[str] + + ppr: Optional[PPRVersionReferenceSchema] diff --git a/connect_ext_ppr/service.py b/connect_ext_ppr/service.py index 8731a68..2e79750 100644 --- a/connect_ext_ppr/service.py +++ b/connect_ext_ppr/service.py @@ -7,7 +7,7 @@ from connect_ext_ppr.db import get_db_ctx_manager from connect_ext_ppr.errors import ExtensionHttpError from connect_ext_ppr.models.configuration import Configuration -from connect_ext_ppr.models.deployment import Deployment +from connect_ext_ppr.models.deployment import Deployment, MarketplaceConfiguration from connect_ext_ppr.models.file import File from connect_ext_ppr.models.ppr import PPRVersion from connect_ext_ppr.models.replicas import Product @@ -43,31 +43,50 @@ def insert_product_from_listing(db, listing_data, logger): db.commit() +def add_marketplaces_to_deployment(db, deployment, marketplaces): + """ + Asociates all the marketplaces to deployment + :param db: dbsession + :param deployment: Deployment instance + :param marketplaces: list of marketplaces' ids + """ + configs = [] + for marketplace in marketplaces: + mc = MarketplaceConfiguration( + deployment=deployment.id, + marketplace=marketplace, + ) + configs.append(mc) + db.add_all(configs) + + def add_deployments(installation, listings, config, logger): with get_db_ctx_manager(config) as db: deployments = [] + deployments_marketplaces = {} seen = set() for li in listings: insert_product_from_listing(db, li, logger) product_id = li['product']['id'] for hub in li['contract']['marketplace']['hubs']: - comb = (product_id, installation['owner']['id'], hub['hub']['id']) + hub_id = hub['hub']['id'] + comb = (product_id, installation['owner']['id'], hub_id) q = db.query(Deployment).filter_by( product_id=product_id, account_id=installation['owner']['id'], - hub_id=hub['hub']['id'], + hub_id=hub_id, ) if db.query(q.exists()).scalar(): dep = q.first() logger.info( - f"Deployment {dep.id} for hub {hub['hub']['id']} already exists.", + f"Deployment {dep.id} for hub {hub_id} already exists.", ) continue if comb not in seen: dep = Deployment( product_id=product_id, - hub_id=hub['hub']['id'], + hub_id=hub_id, vendor_id=li['vendor']['id'], account_id=installation['owner']['id'], ) @@ -77,8 +96,20 @@ def add_deployments(installation, listings, config, logger): ) deployments.append(dep) seen.add(comb) + key = f"{product_id}#{hub_id}" + deployments_marketplaces.setdefault( + key, + {'deployment': dep, 'marketplaces': []}, + ) + deployments_marketplaces[key]['marketplaces'].append( + li['contract']['marketplace']['id'], + ) db.set_verbose_all(deployments) db.commit() + + for data in deployments_marketplaces.values(): + add_marketplaces_to_deployment(db, data['deployment'], data['marketplaces']) + if deployments: db.expire_all() dep_ids = ', '.join([d.id for d in deployments]) diff --git a/connect_ext_ppr/utils.py b/connect_ext_ppr/utils.py index f0bb0ea..c0c5d33 100644 --- a/connect_ext_ppr/utils.py +++ b/connect_ext_ppr/utils.py @@ -27,6 +27,7 @@ DeploymentSchema, FileSchema, HubSchema, + MarketplaceSchema, PPRVersionReferenceSchema, ProductSchema, ) @@ -492,3 +493,16 @@ def process_ppr(wb, product, config_json, items): ws_list.append(ws) return ws_list, summary + + +def get_marketplace_schema(marketplace, ppr): + mp_schema = MarketplaceSchema( + id=marketplace['id'], + name=marketplace['name'], + icon=marketplace['icon'], + external_id=marketplace.get('external_id'), + ) + if ppr: + mp_schema.ppr = PPRVersionReferenceSchema(id=ppr.id, version=ppr.version) + + return mp_schema diff --git a/connect_ext_ppr/webapp.py b/connect_ext_ppr/webapp.py index 3785830..9553e85 100644 --- a/connect_ext_ppr/webapp.py +++ b/connect_ext_ppr/webapp.py @@ -20,12 +20,16 @@ from fastapi import Depends, Request, Response, status from sqlalchemy import exists from sqlalchemy.exc import SQLAlchemyError -from sqlalchemy.orm import joinedload +from sqlalchemy.orm import joinedload, selectinload from connect_ext_ppr.db import create_db, get_db, VerboseBaseSession from connect_ext_ppr.errors import ExtensionHttpError from connect_ext_ppr.models.configuration import Configuration -from connect_ext_ppr.models.deployment import Deployment, DeploymentRequest +from connect_ext_ppr.models.deployment import ( + Deployment, + DeploymentRequest, + MarketplaceConfiguration, +) from connect_ext_ppr.models.enums import ConfigurationStateChoices, DeploymentStatusChoices from connect_ext_ppr.models.file import File from connect_ext_ppr.models.replicas import Product @@ -36,6 +40,7 @@ DeploymentRequestSchema, DeploymentSchema, HubSchema, + MarketplaceSchema, ProductSchema, ) from connect_ext_ppr.utils import ( @@ -50,6 +55,8 @@ get_deployment_request_schema, get_deployment_schema, get_hubs, + get_marketplace_schema, + get_marketplaces, get_user_data_from_auth_token, ) @@ -312,6 +319,33 @@ def remove_configuration( return Response(status_code=204) + @router.get( + '/deployments/{deployment_id}/marketplaces', + summary="Deployment's marketplaces", + response_model=List[MarketplaceSchema], + ) + def get_marketplaces_by_deployment( + self, + deployment_id: str, + client: ConnectClient = Depends(get_installation_client), + db: VerboseBaseSession = Depends(get_db), + installation: dict = Depends(get_installation), + ): + get_deployment_by_id(deployment_id, db, installation) + + mkplc_configs = db.query(MarketplaceConfiguration).options( + selectinload(MarketplaceConfiguration.ppr), + ).filter_by(deployment=deployment_id) + + mkplc_ids = [m.marketplace for m in mkplc_configs] + + marketplaces = get_marketplaces(client, mkplc_ids) + response_list = [] + for mkplc_config in mkplc_configs: + m_data = filter_object_list_by_id(marketplaces, mkplc_config.marketplace) + response_list.append(get_marketplace_schema(m_data, mkplc_config.ppr)) + return response_list + @router.get( '/products', summary='List all products availables for account', diff --git a/tests/api/test_deployments.py b/tests/api/test_deployments.py index bbcb11b..8cd5986 100644 --- a/tests/api/test_deployments.py +++ b/tests/api/test_deployments.py @@ -1,3 +1,5 @@ +import copy + from connect.eaas.core.constants import PROXIED_CONNECT_API_ATTR_NAME @@ -155,3 +157,67 @@ def test_get_deployment_not_found( 'Object `DPL-YYY-YYY` not found.', ], } + + +def test_get_deployments_marketplaces( + mocker, + installation, + api_client, + marketplace, + deployment_factory, + marketplace_config_factory, + ppr_version_factory, +): + m1 = marketplace + m2 = copy.deepcopy(marketplace) + m2['id'] = 'MP-123-123' + m3 = copy.deepcopy(marketplace) + m3['id'] = 'MP-123-123' + marketplaces = [m1, m2] + + mocker.patch( + 'connect_ext_ppr.webapp.get_marketplaces', + return_value=marketplaces, + ) + deployment = deployment_factory(account_id=installation['owner']['id']) + another_dep = deployment_factory(account_id='PA-123-123') + ppr = ppr_version_factory(deployment=deployment) + marketplace_config_factory(deployment=deployment, marketplace_id=m1['id'], ppr_id=ppr.id) + marketplace_config_factory(deployment=deployment, marketplace_id=m2['id']) + marketplace_config_factory(deployment=another_dep, marketplace_id=m3['id']) + + response = api_client.get( + f'/api/deployments/{deployment.id}/marketplaces', + installation=installation, + ) + + assert response.status_code == 200 + + expected_response = [ + { + 'id': m['id'], + 'name': m['name'], + 'icon': m['icon'], + } for m in marketplaces + ] + + expected_response[0].update({'ppr': {'id': ppr.id, 'version': ppr.version}}) + assert response.json() == expected_response + + +def test_get_deployments_marketplaces_not_accounts_deployment( + installation, + api_client, + marketplace, + deployment_factory, + marketplace_config_factory, +): + deployment = deployment_factory(account_id="INVALID") + marketplace_config_factory(deployment=deployment, marketplace_id=marketplace['id']) + + response = api_client.get( + f'/api/deployments/{deployment.id}/marketplaces', + installation=installation, + ) + + assert response.status_code == 404 diff --git a/tests/conftest.py b/tests/conftest.py index 98253a1..c9bd37c 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -24,7 +24,11 @@ VerboseBaseSession, ) from connect_ext_ppr.models.configuration import Configuration -from connect_ext_ppr.models.deployment import Deployment, DeploymentRequest +from connect_ext_ppr.models.deployment import ( + Deployment, + DeploymentRequest, + MarketplaceConfiguration, +) from connect_ext_ppr.models.file import File from connect_ext_ppr.models.ppr import PPRVersion from connect_ext_ppr.models.replicas import Product @@ -256,6 +260,21 @@ def _build_file( return _build_file +@pytest.fixture +def marketplace_config_factory(dbsession, ppr_version_factory): + def _build_mc(deployment, marketplace_id, ppr_id=None): + mp = MarketplaceConfiguration( + deployment=deployment.id, + marketplace=marketplace_id, + ppr_id=ppr_id, + ) + dbsession.add(mp) + dbsession.commit() + dbsession.refresh(mp) + return mp + return _build_mc + + @pytest.fixture def file(file_factory): return file_factory() diff --git a/tests/test_service.py b/tests/test_service.py index e12cee8..a72e9e4 100644 --- a/tests/test_service.py +++ b/tests/test_service.py @@ -1,15 +1,17 @@ +import copy import json +from sqlalchemy import null from connect.client import ClientError import pytest -from connect_ext_ppr.models.deployment import Deployment +from connect_ext_ppr.models.deployment import Deployment, MarketplaceConfiguration from connect_ext_ppr.models.file import File from connect_ext_ppr.schemas import FileSchema, PPRCreateSchema from connect_ext_ppr.service import add_deployments, create_ppr, get_ppr_new_version -def test_add_deployments( +def test_add_one_deployments( dbsession, listing, marketplace, @@ -29,6 +31,43 @@ def test_add_deployments( assert new_dep is not None +def test_add_mutiples_deployments( + dbsession, + listing, + marketplace, + product, + installation, + logger, +): + mkplc_husb = [] + hub_id = 'HB-1111-2222' + listing['contract']['marketplace'] = marketplace + listing['contract']['marketplace']['hubs'][0]['hub']['id'] = hub_id + listing['product'] = product + + for hub in marketplace['hubs']: + mkplc_husb.append((marketplace['id'], hub['hub']['id'])) + + listing2 = copy.deepcopy(listing) + listing2['contract']['marketplace']['id'] = 'MP-1234' + listing2['contract']['marketplace']['hubs'][0]['hub']['id'] = 'HB-1234-1234' + + for hub in listing2['contract']['marketplace']['hubs']: + mkplc_husb.append((listing2['contract']['marketplace']['id'], hub['hub']['id'])) + + add_deployments(installation, [listing, listing2], {}, logger) + assert dbsession.query(Deployment).count() == 3 + assert dbsession.query(MarketplaceConfiguration).count() == 4 + + for marketplace_id, hub_id in mkplc_husb: + new_dep = dbsession.query(Deployment).filter_by(hub_id=hub_id).first() + assert new_dep is not None + assert dbsession.query(MarketplaceConfiguration).filter_by( + deployment=new_dep.id, + marketplace=marketplace_id, + ).filter(MarketplaceConfiguration.id.is_not(null())).count() == 1 + + def test_nothing_to_create( dbsession, listing,