From: Zhang Rong(Jon) Date: Sun, 3 Jul 2022 16:39:41 +0000 (+0800) Subject: Pagination in request and response; Fix alarm client issue X-Git-Tag: 2.0.0-rc1~34 X-Git-Url: https://gerrit.o-ran-sc.org/r/gitweb?a=commitdiff_plain;h=f7ef52a5b4ead0472b1b5828471b28c88d2a0aea;p=pti%2Fo2.git Pagination in request and response; Fix alarm client issue 1. Add two pagination files as common for pagination. 2. Give a layer of pagination in response. 3. Add 'first', 'prev', 'next', 'last' in the Link Header. Issue-ID: INF-288 Signed-off-by: Zhang Rong(Jon) Change-Id: I3047fb7a4a2b4d6480f706ee7773cbe0b69d405d --- diff --git a/o2common/domain/base.py b/o2common/domain/base.py index f419f23..128f950 100644 --- a/o2common/domain/base.py +++ b/o2common/domain/base.py @@ -40,10 +40,10 @@ class Serializer(object): def serialize(self): try: d = {c: getattr(self, c) for c in inspect(self).attrs.keys()} - if 'createtime' in d: - d['createtime'] = d['createtime'].isoformat() - if 'updatetime' in d: - d['updatetime'] = d['updatetime'].isoformat() + # if 'createtime' in d: + # d['createtime'] = d['createtime'].isoformat() + # if 'updatetime' in d: + # d['updatetime'] = d['updatetime'].isoformat() return d # return {c: getattr(self, c) for c in inspect(self).attrs.keys()} except NoInspectionAvailable: diff --git a/o2common/views/pagination_route.py b/o2common/views/pagination_route.py new file mode 100644 index 0000000..e7b738f --- /dev/null +++ b/o2common/views/pagination_route.py @@ -0,0 +1,61 @@ +# Copyright (C) 2021-2022 Wind River Systems, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from urllib.parse import urlparse +from flask import abort + +from o2common.helper import o2logging +logger = o2logging.get_logger(__name__) + +PAGE_PARAM = 'nextpage_opaque_marker' + + +def link_header(full_path: str, ret): + base_url = urlparse(full_path) + count = ret.pop('count') + page_total = ret.pop('page_total') + page_current = ret.pop('page_current') + + if page_current > page_total: + abort(400, "Page size {} bad request.".format(page_current)) + + if 0 == count: + return [], {'X-Total-Count': count} + + query = "&".join(["{}".format(q) for q in base_url.query.split( + '&') if q.split('=')[0] != PAGE_PARAM]) + if query != '': + query = query + '&' + logger.warning(query) + + link_list = [] + if (page_current > 1): + parsed = base_url._replace(query=query + PAGE_PARAM + '=1') + link_list.append('<' + parsed.geturl() + '>; rel="first"') + if (page_current > 1): + parsed = base_url._replace( + query=query + PAGE_PARAM + '=' + str(page_current - 1)) + link_list.append('<' + parsed.geturl() + '>; rel="prev"') + if (page_current < page_total): + parsed = base_url._replace( + query=query + PAGE_PARAM + '=' + str(page_current + 1)) + link_list.append('<' + parsed.geturl() + '>; rel="next"') + if (page_current < page_total): + parsed = base_url._replace( + query=query + PAGE_PARAM + '=' + str(page_total)) + link_list.append('<' + parsed.geturl() + '>; rel="last"') + if 0 == len(link_list): + return ret.pop('results'), {'X-Total-Count': count} + link = ','.join(link_list) + return ret.pop('results'), {'X-Total-Count': count, 'Link': link} diff --git a/o2common/views/pagination_view.py b/o2common/views/pagination_view.py new file mode 100644 index 0000000..6546ebe --- /dev/null +++ b/o2common/views/pagination_view.py @@ -0,0 +1,52 @@ +# Copyright (C) 2021-2022 Wind River Systems, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import math +from typing import List, Tuple + +from o2common.domain.base import Serializer + +from o2common.helper import o2logging +logger = o2logging.get_logger(__name__) + + +class Pagination: + def __init__(self, **kwargs) -> None: + # filter key should be the same with database name + self.filter_kwargs = {} + self.limit = int(kwargs['per_page']) if 'per_page' in kwargs else 30 + self.page = int(kwargs['page']) if 'page' in kwargs else 1 + if self.page < 1: + self.page = 1 + self.start = (self.page - 1) * self.limit + self.filter_kwargs['limit'] = self.limit + self.filter_kwargs['start'] = self.start + + def get_filter(self): + return self.filter_kwargs + + def get_result(self, ret: Tuple[int, List[Serializer]]): + count = ret[0] + logger.info('List count: {}'.format(count)) + ret_list = ret[1] + page_total = int(math.ceil(count/self.limit) + ) if count > self.limit else 1 + result = { + "count": count, + "page_total": page_total, + "page_current": self.page, + "per_page": self.limit, + "results": [r.serialize() for r in ret_list] + } + return result diff --git a/o2ims/adapter/alarm_repository.py b/o2ims/adapter/alarm_repository.py index ef20e6a..ef7ae26 100644 --- a/o2ims/adapter/alarm_repository.py +++ b/o2ims/adapter/alarm_repository.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import List +from typing import List, Tuple from o2ims.domain import alarm_obj from o2ims.domain.alarm_repo import AlarmDefinitionRepository, \ @@ -34,8 +34,16 @@ class AlarmEventRecordSqlAlchemyRepository(AlarmEventRecordRepository): return self.session.query(alarm_obj.AlarmEventRecord).filter_by( alarmEventRecordId=alarm_event_record_id).first() - def _list(self) -> List[alarm_obj.AlarmEventRecord]: - return self.session.query(alarm_obj.AlarmEventRecord) + def _list(self, **kwargs) -> Tuple[int, List[alarm_obj.AlarmEventRecord]]: + size = kwargs.pop('limit') if 'limit' in kwargs else None + offset = kwargs.pop('start') if 'start' in kwargs else 0 + + result = self.session.query(alarm_obj.AlarmEventRecord).filter_by( + **kwargs).order_by('alarmEventRecordId') + count = result.count() + if size is not None and size != -1: + return (count, result.limit(size).offset(offset)) + return (count, result) def _update(self, alarm_event_record: alarm_obj.AlarmEventRecord): self.session.add(alarm_event_record) @@ -80,8 +88,16 @@ class AlarmSubscriptionSqlAlchemyRepository(AlarmSubscriptionRepository): return self.session.query(alarm_obj.AlarmSubscription).filter_by( alarmSubscriptionId=subscription_id).first() - def _list(self) -> List[alarm_obj.AlarmSubscription]: - return self.session.query(alarm_obj.AlarmSubscription) + def _list(self, **kwargs) -> Tuple[int, List[alarm_obj.AlarmSubscription]]: + size = kwargs.pop('limit') if 'limit' in kwargs else None + offset = kwargs.pop('start') if 'start' in kwargs else 0 + + result = self.session.query(alarm_obj.AlarmSubscription).filter_by( + **kwargs).order_by('alarmSubscriptionId') + count = result.count() + if size is not None and size != -1: + return (count, result.limit(size).offset(offset)) + return (count, result) def _update(self, subscription: alarm_obj.AlarmSubscription): self.session.add(subscription) diff --git a/o2ims/adapter/clients/fault_client.py b/o2ims/adapter/clients/fault_client.py index b37a4d2..45a27ff 100644 --- a/o2ims/adapter/clients/fault_client.py +++ b/o2ims/adapter/clients/fault_client.py @@ -27,7 +27,6 @@ from fmclient.common.exceptions import HTTPNotFound from o2common.service.client.base_client import BaseClient from o2common.config import config from o2ims.domain import alarm_obj as alarmModel -from o2ims.domain.resource_type import ResourceTypeEnum from o2app.adapter import unit_of_work from o2common.helper import o2logging @@ -126,7 +125,7 @@ class StxFaultClientImp(object): logger.debug('alarm 1:' + str(alarms[0].to_dict())) # [print('alarm:' + str(alarm.to_dict())) for alarm in alarms if alarm] return [alarmModel.FaultGenericModel( - ResourceTypeEnum.PSERVER, self._alarmconverter(alarm)) + alarmModel.EventTypeEnum.ALARM, self._alarmconverter(alarm)) for alarm in alarms if alarm] def getAlarmInfo(self, id) -> alarmModel.FaultGenericModel: @@ -137,16 +136,17 @@ class StxFaultClientImp(object): except HTTPNotFound: event = self.fmclient.event_log.get(id) return alarmModel.FaultGenericModel( - ResourceTypeEnum.PSERVER, self._eventconverter(event, True)) + alarmModel.EventTypeEnum.ALARM, self._eventconverter(event, + True)) return alarmModel.FaultGenericModel( - ResourceTypeEnum.PSERVER, self._alarmconverter(alarm)) + alarmModel.EventTypeEnum.ALARM, self._alarmconverter(alarm)) def getEventList(self, **filters) -> List[alarmModel.FaultGenericModel]: events = self.fmclient.event_log.list(alarms=True, expand=True) logger.debug('event 1:' + str(events[0].to_dict())) # [print('alarm:' + str(event.to_dict())) for event in events if event] return [alarmModel.FaultGenericModel( - ResourceTypeEnum.PSERVER, self._eventconverter(event)) + alarmModel.EventTypeEnum.EVENT, self._eventconverter(event)) for event in events if event] def getEventInfo(self, id) -> alarmModel.FaultGenericModel: @@ -154,20 +154,18 @@ class StxFaultClientImp(object): logger.debug('get event id ' + id + ':' + str(event.to_dict())) # print(event.to_dict()) return alarmModel.FaultGenericModel( - ResourceTypeEnum.PSERVER, self._eventconverter(event)) + alarmModel.EventTypeEnum.EVENT, self._eventconverter(event)) @ staticmethod def _alarmconverter(alarm): # setattr(alarm, 'alarm_def_id', uuid.uuid3( # uuid.NAMESPACE_URL, alarm.alarm_id)) setattr(alarm, 'state', alarm.alarm_state) - setattr(alarm, 'event_log_type', alarm.alarm_type) - setattr(alarm, 'event_log_id', alarm.alarm_id) - setattr(alarm, 'alarm_def_id', uuid.uuid3( - uuid.NAMESPACE_URL, alarm.alarm_id)) - setattr(alarm, 'probable_cause_id', uuid.uuid3( - uuid.NAMESPACE_URL, alarm.probale_cause)) + setattr(alarm, 'alarm_def_id', str(uuid.uuid3( + uuid.NAMESPACE_URL, alarm.alarm_id))) + setattr(alarm, 'probable_cause_id', str(uuid.uuid3( + uuid.NAMESPACE_URL, alarm.probable_cause))) return alarm @ staticmethod @@ -177,10 +175,10 @@ class StxFaultClientImp(object): if clear: logger.debug('alarm is clear') event.state = 'clear' - setattr(event, 'alarm_def_id', uuid.uuid3( - uuid.NAMESPACE_URL, event.alarm_id)) - setattr(event, 'probable_cause_id', uuid.uuid3( - uuid.NAMESPACE_URL, event.probale_cause)) + setattr(event, 'alarm_def_id', str(uuid.uuid3( + uuid.NAMESPACE_URL, event.alarm_id))) + setattr(event, 'probable_cause_id', str(uuid.uuid3( + uuid.NAMESPACE_URL, event.probable_cause))) return event @ staticmethod diff --git a/o2ims/adapter/ocloud_repository.py b/o2ims/adapter/ocloud_repository.py index d48aa06..b26e98a 100644 --- a/o2ims/adapter/ocloud_repository.py +++ b/o2ims/adapter/ocloud_repository.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import List +from typing import List, Tuple from o2ims.domain import ocloud, subscription_obj from o2ims.domain.ocloud_repo import OcloudRepository, ResourceTypeRepository,\ @@ -58,8 +58,16 @@ class ResouceTypeSqlAlchemyRepository(ResourceTypeRepository): return self.session.query(ocloud.ResourceType).filter_by( name=resource_type_name).first() - def _list(self) -> List[ocloud.ResourceType]: - return self.session.query(ocloud.ResourceType) + def _list(self, **kwargs) -> Tuple[int, List[ocloud.ResourceType]]: + size = kwargs.pop('limit') if 'limit' in kwargs else None + offset = kwargs.pop('start') if 'start' in kwargs else 0 + + result = self.session.query(ocloud.ResourceType).filter_by( + **kwargs).order_by('resourceTypeId') + count = result.count() + if size is not None and size != -1: + return (count, result.limit(size).offset(offset)) + return (count, result) def _update(self, resourceType: ocloud.ResourceType): self.session.add(resourceType) @@ -77,8 +85,16 @@ class ResourcePoolSqlAlchemyRepository(ResourcePoolRepository): return self.session.query(ocloud.ResourcePool).filter_by( resourcePoolId=resource_pool_id).first() - def _list(self) -> List[ocloud.ResourcePool]: - return self.session.query(ocloud.ResourcePool) + def _list(self, **kwargs) -> Tuple[int, List[ocloud.ResourcePool]]: + size = kwargs.pop('limit') if 'limit' in kwargs else None + offset = kwargs.pop('start') if 'start' in kwargs else 0 + + result = self.session.query(ocloud.ResourcePool).filter_by( + **kwargs).order_by('resourcePoolId') + count = result.count() + if size is not None and size != -1: + return (count, result.limit(size).offset(offset)) + return (count, result) def _update(self, resourcePool: ocloud.ResourcePool): self.session.add(resourcePool) @@ -115,9 +131,19 @@ class ResourceSqlAlchemyRepository(ResourceRepository): return res return recursive(resource_id) - def _list(self, resourcepool_id, **kwargs) -> List[ocloud.Resource]: - return self.session.query(ocloud.Resource).filter_by( - resourcePoolId=resourcepool_id, **kwargs) + def _list(self, resourcepool_id, **kwargs) -> \ + Tuple[int, List[ocloud.Resource]]: + if 'sort' in kwargs: + kwargs.pop('sort') + size = kwargs.pop('limit') if 'limit' in kwargs else None + offset = kwargs.pop('start') if 'start' in kwargs else 0 + + result = self.session.query(ocloud.Resource).filter_by( + resourcePoolId=resourcepool_id, **kwargs).order_by('resourceId') + count = result.count() + if size is not None and size != -1: + return (count, result.limit(size).offset(offset)) + return (count, result) def _update(self, resource: ocloud.Resource): self.session.add(resource) @@ -135,8 +161,16 @@ class DeploymentManagerSqlAlchemyRepository(DeploymentManagerRepository): return self.session.query(ocloud.DeploymentManager).filter_by( deploymentManagerId=deployment_manager_id).first() - def _list(self) -> List[ocloud.DeploymentManager]: - return self.session.query(ocloud.DeploymentManager) + def _list(self, **kwargs) -> Tuple[int, List[ocloud.DeploymentManager]]: + size = kwargs.pop('limit') if 'limit' in kwargs else None + offset = kwargs.pop('start') if 'start' in kwargs else 0 + + result = self.session.query(ocloud.DeploymentManager).filter_by( + **kwargs).order_by('deploymentManagerId') + count = result.count() + if size is not None and size != -1: + return (count, result.limit(size).offset(offset)) + return (count, result) def _update(self, deployment_manager: ocloud.DeploymentManager): self.session.add(deployment_manager) @@ -154,8 +188,17 @@ class SubscriptionSqlAlchemyRepository(SubscriptionRepository): return self.session.query(subscription_obj.Subscription).filter_by( subscriptionId=subscription_id).first() - def _list(self) -> List[subscription_obj.Subscription]: - return self.session.query(subscription_obj.Subscription) + def _list(self, **kwargs) -> \ + Tuple[int, List[subscription_obj.Subscription]]: + size = kwargs.pop('limit') if 'limit' in kwargs else None + offset = kwargs.pop('start') if 'start' in kwargs else 0 + + result = self.session.query(subscription_obj.Subscription).filter_by( + **kwargs).order_by('subscriptionId') + count = result.count() + if size is not None and size != -1: + return (count, result.limit(size).offset(offset)) + return (count, result) def _update(self, subscription: subscription_obj.Subscription): self.session.add(subscription) diff --git a/o2ims/domain/alarm_obj.py b/o2ims/domain/alarm_obj.py index fa7cba2..9b3ff4f 100644 --- a/o2ims/domain/alarm_obj.py +++ b/o2ims/domain/alarm_obj.py @@ -19,6 +19,9 @@ import datetime from o2common.domain.base import AgRoot, Serializer +from o2common.helper import o2logging +logger = o2logging.get_logger(__name__) + class FaultGenericModel(AgRoot): def __init__(self, type: str, @@ -27,7 +30,10 @@ class FaultGenericModel(AgRoot): if api_response: self.id = str(api_response.uuid) self.name = self.id - self.type = type + self.alarm_type = api_response.alarm_type + self.alarm_def_name = api_response.alarm_id + self.alarm_def_id = api_response.alarm_def_id + self.probable_cause_id = api_response.probable_cause_id self.status = api_response.state # TODO: time less than second self.timestamp = datetime.datetime.strptime( @@ -42,6 +48,8 @@ class FaultGenericModel(AgRoot): self.hash = content_hash if content_hash \ else str(hash((self.id, self.timestamp, self.status))) self.content = json.dumps(api_response.to_dict()) + if EventTypeEnum.ALARM == type: + pass def is_outdated(self, newmodel) -> bool: # return self.updatetime < newmodel.updatetime @@ -58,6 +66,11 @@ class FaultGenericModel(AgRoot): self.content = newmodel.content +class EventTypeEnum(Enum): + ALARM = 'alarm' + EVENT = 'event' + + class PerceivedSeverityEnum(str, Enum): CRITICAL = 0 MAJOR = 1 diff --git a/o2ims/domain/alarm_repo.py b/o2ims/domain/alarm_repo.py index d3e7f52..d712f1c 100644 --- a/o2ims/domain/alarm_repo.py +++ b/o2ims/domain/alarm_repo.py @@ -13,7 +13,7 @@ # limitations under the License. import abc -from typing import List, Set +from typing import List, Set, Tuple from o2ims.domain import alarm_obj as obj @@ -31,8 +31,12 @@ class AlarmEventRecordRepository(abc.ABC): self.seen.add(alarm_event_record) return alarm_event_record - def list(self) -> List[obj.AlarmEventRecord]: - return self._list() + def list(self, **kwargs) -> List[obj.AlarmEventRecord]: + return self._list(**kwargs)[1] + + def list_with_count(self, **kwargs) -> \ + Tuple[int, List[obj.AlarmEventRecord]]: + return self._list(**kwargs) def update(self, alarm_event_record: obj.AlarmEventRecord): self._update(alarm_event_record) @@ -49,7 +53,7 @@ class AlarmEventRecordRepository(abc.ABC): raise NotImplementedError @abc.abstractmethod - def _list(self) -> List[obj.AlarmEventRecord]: + def _list(self, **kwargs) -> Tuple[int, List[obj.AlarmEventRecord]]: raise NotImplementedError @abc.abstractmethod @@ -155,8 +159,12 @@ class AlarmSubscriptionRepository(abc.ABC): self.seen.add(subscription) return subscription - def list(self) -> List[obj.AlarmSubscription]: - return self._list() + def list(self, **kwargs) -> List[obj.AlarmSubscription]: + return self._list(**kwargs)[1] + + def list_with_count(self, **kwargs) -> \ + Tuple[int, List[obj.AlarmSubscription]]: + return self._list(**kwargs) def update(self, subscription: obj.AlarmSubscription): self._update(subscription) @@ -172,6 +180,10 @@ class AlarmSubscriptionRepository(abc.ABC): def _get(self, subscription_id) -> obj.AlarmSubscription: raise NotImplementedError + @abc.abstractmethod + def _list(self, **kwargs) -> Tuple[int, List[obj.AlarmSubscription]]: + raise NotImplementedError + @abc.abstractmethod def _update(self, subscription: obj.AlarmSubscription): raise NotImplementedError diff --git a/o2ims/domain/commands.py b/o2ims/domain/commands.py index 4ab1b25..25c9629 100644 --- a/o2ims/domain/commands.py +++ b/o2ims/domain/commands.py @@ -18,7 +18,7 @@ from dataclasses import dataclass # from typing import List from o2ims.domain.stx_object import StxGenericModel -from o2ims.domain.alarm_obj import AlarmEvent2SMO +from o2ims.domain.alarm_obj import AlarmEvent2SMO, FaultGenericModel from o2ims.domain.subscription_obj import Message2SMO, RegistrationMessage # from o2ims.domain.resource_type import ResourceTypeEnum from o2common.domain.commands import Command @@ -29,6 +29,11 @@ class UpdateStxObject(Command): data: StxGenericModel +@dataclass +class UpdateFaultObject(Command): + data: FaultGenericModel + + @dataclass class PubMessage2SMO(Command): data: Message2SMO @@ -100,5 +105,5 @@ class UpdatePserverIfPort(UpdateResource): @dataclass -class UpdateAlarm(UpdateStxObject): +class UpdateAlarm(UpdateFaultObject): pass diff --git a/o2ims/domain/ocloud.py b/o2ims/domain/ocloud.py index ce55aee..047fb8b 100644 --- a/o2ims/domain/ocloud.py +++ b/o2ims/domain/ocloud.py @@ -47,7 +47,6 @@ class DeploymentManager(AgRoot, Serializer): self.extensions = [] def serialize(self): - print(self.__dict__) d = Serializer.serialize(self) if 'profile' in d and d['profile'] != '': diff --git a/o2ims/domain/ocloud_repo.py b/o2ims/domain/ocloud_repo.py index 811e85c..9f86c9d 100644 --- a/o2ims/domain/ocloud_repo.py +++ b/o2ims/domain/ocloud_repo.py @@ -13,7 +13,7 @@ # limitations under the License. import abc -from typing import List, Set +from typing import List, Set, Tuple from o2ims.domain import ocloud @@ -74,8 +74,12 @@ class ResourceTypeRepository(abc.ABC): self.seen.add(resource_type) return resource_type - def list(self) -> List[ocloud.ResourceType]: - return self._list() + def list(self, **kwargs) -> List[ocloud.ResourceType]: + return self._list(**kwargs)[1] + + def list_with_count(self, **kwargs) -> \ + Tuple[int, List[ocloud.ResourceType]]: + return self._list(**kwargs) def update(self, resource_type: ocloud.ResourceType): self._update(resource_type) @@ -93,6 +97,10 @@ class ResourceTypeRepository(abc.ABC): def _get_by_name(self, resource_type_name) -> ocloud.ResourceType: raise NotImplementedError + @abc.abstractmethod + def _list(self, **kwargs) -> Tuple[int, List[ocloud.ResourceType]]: + raise NotImplementedError + @abc.abstractmethod def _update(self, resource_type: ocloud.ResourceType): raise NotImplementedError @@ -112,8 +120,12 @@ class ResourcePoolRepository(abc.ABC): self.seen.add(resource_pool) return resource_pool - def list(self) -> List[ocloud.ResourcePool]: - return self._list() + def list(self, **kwargs) -> List[ocloud.ResourcePool]: + return self._list(**kwargs)[1] + + def list_with_count(self, **kwargs) -> \ + Tuple[int, List[ocloud.ResourcePool]]: + return self._list(**kwargs) def update(self, resource_pool: ocloud.ResourcePool): self._update(resource_pool) @@ -127,6 +139,10 @@ class ResourcePoolRepository(abc.ABC): def _get(self, resource_pool_id) -> ocloud.ResourcePool: raise NotImplementedError + @abc.abstractmethod + def _list(self, **kwargs) -> Tuple[int, List[ocloud.ResourcePool]]: + raise NotImplementedError + @abc.abstractmethod def _update(self, resource_pool: ocloud.ResourcePool): raise NotImplementedError @@ -147,6 +163,10 @@ class ResourceRepository(abc.ABC): return resource def list(self, resourcepool_id, **kwargs) -> List[ocloud.Resource]: + return self._list(resourcepool_id, **kwargs)[1] + + def list_with_count(self, resourcepool_id, **kwargs) -> \ + Tuple[int, List[ocloud.Resource]]: return self._list(resourcepool_id, **kwargs) def update(self, resource: ocloud.Resource): @@ -162,7 +182,8 @@ class ResourceRepository(abc.ABC): raise NotImplementedError @abc.abstractmethod - def _list(self, resourcepool_id, **kwargs) -> ocloud.Resource: + def _list(self, resourcepool_id, **kwargs) -> \ + Tuple[int, List[ocloud.Resource]]: raise NotImplementedError @abc.abstractmethod @@ -184,8 +205,12 @@ class DeploymentManagerRepository(abc.ABC): self.seen.add(deployment_manager) return deployment_manager - def list(self) -> List[ocloud.DeploymentManager]: - return self._list() + def list(self, **kwargs) -> List[ocloud.DeploymentManager]: + return self._list(**kwargs)[1] + + def list_with_count(self, **kwargs) -> \ + Tuple[int, List[ocloud.DeploymentManager]]: + return self._list(**kwargs) def update(self, deployment_manager: ocloud.DeploymentManager): self._update(deployment_manager) @@ -198,6 +223,10 @@ class DeploymentManagerRepository(abc.ABC): def _get(self, deployment_manager_id) -> ocloud.DeploymentManager: raise NotImplementedError + @abc.abstractmethod + def _list(self, **kwargs) -> Tuple[int, List[ocloud.DeploymentManager]]: + raise NotImplementedError + @abc.abstractmethod def _update(self, deployment_manager: ocloud.DeploymentManager): raise NotImplementedError diff --git a/o2ims/domain/subscription_repo.py b/o2ims/domain/subscription_repo.py index d12c00d..44d7b0e 100644 --- a/o2ims/domain/subscription_repo.py +++ b/o2ims/domain/subscription_repo.py @@ -13,7 +13,7 @@ # limitations under the License. import abc -from typing import List, Set +from typing import List, Set, Tuple from o2ims.domain import subscription_obj as subobj @@ -31,8 +31,12 @@ class SubscriptionRepository(abc.ABC): self.seen.add(subscription) return subscription - def list(self) -> List[subobj.Subscription]: - return self._list() + def list(self, **kwargs) -> List[subobj.Subscription]: + return self._list(**kwargs)[1] + + def list_with_count(self, **kwargs) -> \ + Tuple[int, List[subobj.Subscription]]: + return self._list(**kwargs) def update(self, subscription: subobj.Subscription): self._update(subscription) @@ -52,6 +56,10 @@ class SubscriptionRepository(abc.ABC): def _update(self, subscription: subobj.Subscription): raise NotImplementedError + @abc.abstractmethod + def _list(self, **kwargs) -> Tuple[int, List[subobj.Subscription]]: + raise NotImplementedError + @abc.abstractmethod def _delete(self, subscription_id): raise NotImplementedError diff --git a/o2ims/service/auditor/alarm_handler.py b/o2ims/service/auditor/alarm_handler.py index 6288531..8917264 100644 --- a/o2ims/service/auditor/alarm_handler.py +++ b/o2ims/service/auditor/alarm_handler.py @@ -114,7 +114,7 @@ def create_by(fmobj: FaultGenericModel) -> AlarmEventRecord: else: return alarm_obj.PerceivedSeverityEnum.WARNING alarm_event_record.perceivedSeverity = severity_switch(content['severity']) - alarm_event_record.probableCauseId = content['probable_cause_id'] + alarm_event_record.probableCauseId = fmobj.probable_cause_id alarm_event_record.hash = fmobj.hash # logger.info('severity: ' + content['severity']) # logger.info('perceived severity: ' diff --git a/o2ims/views/alarm_route.py b/o2ims/views/alarm_route.py index 91ca8f8..6b32eee 100644 --- a/o2ims/views/alarm_route.py +++ b/o2ims/views/alarm_route.py @@ -12,9 +12,11 @@ # See the License for the specific language governing permissions and # limitations under the License. -from flask_restx import Resource +from flask import request +from flask_restx import Resource, reqparse from o2common.service.messagebus import MessageBus +from o2common.views.pagination_route import link_header, PAGE_PARAM from o2ims.views import alarm_view from o2ims.views.api_ns import api_monitoring_v1 from o2ims.views.alarm_dto import AlarmDTO, SubscriptionDTO @@ -31,13 +33,25 @@ def configure_api_route(): # ---------- Alarm Event Record ---------- # @api_monitoring_v1.route("/alarms") +@api_monitoring_v1.param(PAGE_PARAM, + 'Page number of the results to fetch.' + + ' Default: 1', + _in='query', default=1) class AlarmListRouter(Resource): model = AlarmDTO.alarm_event_record_get @api_monitoring_v1.marshal_list_with(model) def get(self): - return alarm_view.alarm_event_records(bus.uow) + parser = reqparse.RequestParser() + parser.add_argument(PAGE_PARAM, location='args') + args = parser.parse_args() + kwargs = {} + if args.nextpage_opaque_marker is not None: + kwargs['page'] = args.nextpage_opaque_marker + + ret = alarm_view.alarm_event_records(bus.uow, **kwargs) + return link_header(request.full_path, ret) @api_monitoring_v1.route("/alarms/") @@ -68,7 +82,15 @@ class SubscriptionsListRouter(Resource): @api_monitoring_v1.doc('List alarm subscriptions') @api_monitoring_v1.marshal_list_with(model) def get(self): - return alarm_view.subscriptions(bus.uow) + parser = reqparse.RequestParser() + parser.add_argument(PAGE_PARAM, location='args') + args = parser.parse_args() + kwargs = {} + if args.nextpage_opaque_marker is not None: + kwargs['page'] = args.nextpage_opaque_marker + + ret = alarm_view.subscriptions(bus.uow, **kwargs) + return link_header(request.full_path, ret) @api_monitoring_v1.doc('Create a alarm subscription') @api_monitoring_v1.expect(expect) diff --git a/o2ims/views/alarm_view.py b/o2ims/views/alarm_view.py index 258e323..a308429 100644 --- a/o2ims/views/alarm_view.py +++ b/o2ims/views/alarm_view.py @@ -15,6 +15,7 @@ import uuid as uuid from o2common.service import unit_of_work +from o2common.views.pagination_view import Pagination from o2ims.views.alarm_dto import SubscriptionDTO from o2ims.domain.alarm_obj import AlarmSubscription @@ -23,10 +24,12 @@ from o2common.helper import o2logging logger = o2logging.get_logger(__name__) -def alarm_event_records(uow: unit_of_work.AbstractUnitOfWork): +def alarm_event_records(uow: unit_of_work.AbstractUnitOfWork, **kwargs): + pagination = Pagination(**kwargs) + filter_kwargs = pagination.get_filter() with uow: - li = uow.alarm_event_records.list() - return [r.serialize() for r in li] + li = uow.alarm_event_records.list_with_count(**filter_kwargs) + return pagination.get_result(li) def alarm_event_record_one(alarmEventRecordId: str, @@ -36,10 +39,12 @@ def alarm_event_record_one(alarmEventRecordId: str, return first.serialize() if first is not None else None -def subscriptions(uow: unit_of_work.AbstractUnitOfWork): +def subscriptions(uow: unit_of_work.AbstractUnitOfWork, **kwargs): + pagination = Pagination(**kwargs) + filter_kwargs = pagination.get_filter() with uow: - li = uow.alarm_subscriptions.list() - return [r.serialize() for r in li] + li = uow.alarm_subscriptions.list_with_count(**filter_kwargs) + return pagination.get_result(li) def subscription_one(subscriptionId: str, diff --git a/o2ims/views/ocloud_dto.py b/o2ims/views/ocloud_dto.py index 651de97..c8e5202 100644 --- a/o2ims/views/ocloud_dto.py +++ b/o2ims/views/ocloud_dto.py @@ -62,7 +62,6 @@ class ResourcePoolDTO: class ResourceDTO: - resource_list = api_ims_inventory_v1.model( "ResourceListDto", { @@ -76,6 +75,46 @@ class ResourceDTO: } ) + list_result = api_ims_inventory_v1.model( + "ResourceListPagenationDto", + { + 'count': fields.Integer(), + 'page_num': fields.Integer(), + 'results': fields.List(fields.Nested(resource_list)) + } + ) + + # def get_paginated_list(results, url, start, limit): + # start = int(start) + # limit = int(limit) + # count = len(results) + # if count < start or limit < 0: + # api_ims_inventory_v1.abort(404) + # # make response + # obj = {} + # obj['start'] = start + # obj['limit'] = limit + # obj['count'] = count + # # make URLs + # # make previous url + # if start == 1: + # obj['previous'] = '' + # else: + # start_copy = max(1, start - limit) + # limit_copy = start - 1 + # obj['previous'] = url + \ + # '?start=%d&limit=%d' % (start_copy, limit_copy) + # # make next url + # if start + limit > count: + # obj['next'] = '' + # else: + # start_copy = start + limit + # obj['next'] = url + '?start=%d&limit=%d' % (start_copy, limit) + # # finally extract result according to bounds + # # obj['results'] = results[(start - 1):(start - 1 + limit)] + # obj['result'] = fields.List(fields.Nested(ResourceDTO.resource_list)) + # return obj + def recursive_resource_mapping(iteration_number=2): resource_json_mapping = { 'resourceId': fields.String(required=True, diff --git a/o2ims/views/ocloud_route.py b/o2ims/views/ocloud_route.py index 6b6a3fc..5feb6b8 100644 --- a/o2ims/views/ocloud_route.py +++ b/o2ims/views/ocloud_route.py @@ -12,9 +12,11 @@ # See the License for the specific language governing permissions and # limitations under the License. +from flask import request from flask_restx import Resource, reqparse from o2common.service.messagebus import MessageBus +from o2common.views.pagination_route import link_header, PAGE_PARAM from o2ims.views import ocloud_view from o2ims.views.api_ns import api_ims_inventory_v1 from o2ims.views.ocloud_dto import OcloudDTO, ResourceTypeDTO,\ @@ -51,13 +53,25 @@ class OcloudsListRouter(Resource): # ---------- ResourceTypes ---------- # @api_ims_inventory_v1.route("/resourceTypes") +@api_ims_inventory_v1.param(PAGE_PARAM, + 'Page number of the results to fetch.' + + ' Default: 1', + _in='query', default=1) class ResourceTypesListRouter(Resource): model = ResourceTypeDTO.resource_type_get @api_ims_inventory_v1.marshal_list_with(model) def get(self): - return ocloud_view.resource_types(bus.uow) + parser = reqparse.RequestParser() + parser.add_argument(PAGE_PARAM, location='args') + args = parser.parse_args() + kwargs = {} + if args.nextpage_opaque_marker is not None: + kwargs['page'] = args.nextpage_opaque_marker + + ret = ocloud_view.resource_types(bus.uow, **kwargs) + return link_header(request.full_path, ret) @api_ims_inventory_v1.route("/resourceTypes/") @@ -79,13 +93,25 @@ class ResourceTypeGetRouter(Resource): # ---------- ResourcePools ---------- # @api_ims_inventory_v1.route("/resourcePools") +@api_ims_inventory_v1.param(PAGE_PARAM, + 'Page number of the results to fetch.' + + ' Default: 1', + _in='query', default=1) class ResourcePoolsListRouter(Resource): model = ResourcePoolDTO.resource_pool_get @api_ims_inventory_v1.marshal_list_with(model) def get(self): - return ocloud_view.resource_pools(bus.uow) + parser = reqparse.RequestParser() + parser.add_argument(PAGE_PARAM, location='args') + args = parser.parse_args() + kwargs = {} + if args.nextpage_opaque_marker is not None: + kwargs['page'] = args.nextpage_opaque_marker + + ret = ocloud_view.resource_pools(bus.uow, **kwargs) + return link_header(request.full_path, ret) @api_ims_inventory_v1.route("/resourcePools/") @@ -109,9 +135,18 @@ class ResourcePoolGetRouter(Resource): @api_ims_inventory_v1.route("/resourcePools//resources") @api_ims_inventory_v1.param('resourcePoolID', 'ID of the resource pool') @api_ims_inventory_v1.param('resourceTypeName', 'filter resource type', - location='args') + _in='query') @api_ims_inventory_v1.param('parentId', 'filter parentId', - location='args') + _in='query') +# @api_ims_inventory_v1.param('sort', 'sort by column name', +# _in='query') +# @api_ims_inventory_v1.param('per_page', 'The number of results per page ' + +# '(max 100). Default: 30', +# _in='query', default=30) +@api_ims_inventory_v1.param(PAGE_PARAM, + 'Page number of the results to fetch.' + + ' Default: 1', + _in='query', default=1) class ResourcesListRouter(Resource): model = ResourceDTO.resource_list @@ -121,6 +156,9 @@ class ResourcesListRouter(Resource): parser = reqparse.RequestParser() parser.add_argument('resourceTypeName', location='args') parser.add_argument('parentId', location='args') + # parser.add_argument('sort', location='args') + # parser.add_argument('per_page', location='args') + parser.add_argument(PAGE_PARAM, location='args') args = parser.parse_args() kwargs = {} if args.resourceTypeName is not None: @@ -129,8 +167,15 @@ class ResourcesListRouter(Resource): kwargs['parentId'] = args.parentId if args.parentId.lower() == 'null': kwargs['parentId'] = None + # if args.per_page is not None: + # kwargs['per_page'] = args.per_page + # base_url = base_url + 'per_page=' + args.per_page + '&' + if args.nextpage_opaque_marker is not None: + kwargs['page'] = args.nextpage_opaque_marker - return ocloud_view.resources(resourcePoolID, bus.uow, **kwargs) + ret = ocloud_view.resources(resourcePoolID, bus.uow, **kwargs) + + return link_header(request.full_path, ret) @api_ims_inventory_v1.route( @@ -156,13 +201,25 @@ class ResourceGetRouter(Resource): # ---------- DeploymentManagers ---------- # @api_ims_inventory_v1.route("/deploymentManagers") +@api_ims_inventory_v1.param(PAGE_PARAM, + 'Page number of the results to fetch.' + + ' Default: 1', + _in='query', default=1) class DeploymentManagersListRouter(Resource): model = DeploymentManagerDTO.deployment_manager_list @api_ims_inventory_v1.marshal_list_with(model) def get(self): - return ocloud_view.deployment_managers(bus.uow) + parser = reqparse.RequestParser() + parser.add_argument(PAGE_PARAM, location='args') + args = parser.parse_args() + kwargs = {} + if args.nextpage_opaque_marker is not None: + kwargs['page'] = args.nextpage_opaque_marker + + ret = ocloud_view.deployment_managers(bus.uow, **kwargs) + return link_header(request.full_path, ret) @api_ims_inventory_v1.route("/deploymentManagers/") @@ -204,7 +261,15 @@ class SubscriptionsListRouter(Resource): @api_ims_inventory_v1.doc('List subscriptions') @api_ims_inventory_v1.marshal_list_with(model) def get(self): - return ocloud_view.subscriptions(bus.uow) + parser = reqparse.RequestParser() + parser.add_argument(PAGE_PARAM, location='args') + args = parser.parse_args() + kwargs = {} + if args.nextpage_opaque_marker is not None: + kwargs['page'] = args.nextpage_opaque_marker + + ret = ocloud_view.subscriptions(bus.uow, **kwargs) + return link_header(request.full_path, ret) @api_ims_inventory_v1.doc('Create a subscription') @api_ims_inventory_v1.expect(expect) diff --git a/o2ims/views/ocloud_view.py b/o2ims/views/ocloud_view.py index 56f970d..ed55dda 100644 --- a/o2ims/views/ocloud_view.py +++ b/o2ims/views/ocloud_view.py @@ -20,12 +20,13 @@ from datetime import datetime import shutil from o2common.service import unit_of_work +from o2common.config import config +from o2common.views.pagination_view import Pagination from o2ims.domain import ocloud from o2ims.views.ocloud_dto import SubscriptionDTO from o2ims.domain.subscription_obj import Subscription from o2common.helper import o2logging -from o2common.config import config logger = o2logging.get_logger(__name__) @@ -41,10 +42,12 @@ def ocloud_one(ocloudid: str, uow: unit_of_work.AbstractUnitOfWork): return first.serialize() if first is not None else None -def resource_types(uow: unit_of_work.AbstractUnitOfWork): +def resource_types(uow: unit_of_work.AbstractUnitOfWork, **kwargs): + pagination = Pagination(**kwargs) + filter_kwargs = pagination.get_filter() with uow: - li = uow.resource_types.list() - return [r.serialize() for r in li] + li = uow.resource_types.list_with_count(**filter_kwargs) + return pagination.get_result(li) def resource_type_one(resourceTypeId: str, @@ -54,10 +57,12 @@ def resource_type_one(resourceTypeId: str, return first.serialize() if first is not None else None -def resource_pools(uow: unit_of_work.AbstractUnitOfWork): +def resource_pools(uow: unit_of_work.AbstractUnitOfWork, **kwargs): + pagination = Pagination(**kwargs) + filter_kwargs = pagination.get_filter() with uow: - li = uow.resource_pools.list() - return [r.serialize() for r in li] + li = uow.resource_pools.list_with_count(**filter_kwargs) + return pagination.get_result(li) def resource_pool_one(resourcePoolId: str, @@ -69,8 +74,9 @@ def resource_pool_one(resourcePoolId: str, def resources(resourcePoolId: str, uow: unit_of_work.AbstractUnitOfWork, **kwargs): - - filter_kwargs = {} # filter key should be the same with database name + pagination = Pagination(**kwargs) + # filter key should be the same with database name + filter_kwargs = pagination.get_filter() if 'resourceTypeName' in kwargs: resource_type_name = kwargs['resourceTypeName'] with uow: @@ -83,14 +89,15 @@ def resources(resourcePoolId: str, uow: unit_of_work.AbstractUnitOfWork, restype_id = '' if res_type is None else res_type.resourceTypeId filter_kwargs['resourceTypeId'] = restype_id - # li = uow.resources.list(resourcePoolId) - # return [r.serialize() for r in li if r.resourceTypeId == restype_id] if 'parentId' in kwargs: filter_kwargs['parentId'] = kwargs['parentId'] + if 'sort' in kwargs: + filter_kwargs['sort'] = kwargs['sort'] with uow: - li = uow.resources.list(resourcePoolId, **filter_kwargs) - return [r.serialize() for r in li] + ret = uow.resources.list_with_count(resourcePoolId, **filter_kwargs) + + return pagination.get_result(ret) def resource_one(resourceId: str, uow: unit_of_work.AbstractUnitOfWork): @@ -99,10 +106,12 @@ def resource_one(resourceId: str, uow: unit_of_work.AbstractUnitOfWork): return first.serialize() if first is not None else None -def deployment_managers(uow: unit_of_work.AbstractUnitOfWork): +def deployment_managers(uow: unit_of_work.AbstractUnitOfWork, **kwargs): + pagination = Pagination(**kwargs) + filter_kwargs = pagination.get_filter() with uow: - li = uow.deployment_managers.list() - return [r.serialize() for r in li] + li = uow.deployment_managers.list_with_count(**filter_kwargs) + return pagination.get_result(li) def deployment_manager_one(deploymentManagerId: str, @@ -177,10 +186,12 @@ def _gen_kube_config(dmId: str, kubeconfig: dict) -> dict: return '/configs/'+kube_config_name -def subscriptions(uow: unit_of_work.AbstractUnitOfWork): +def subscriptions(uow: unit_of_work.AbstractUnitOfWork, **kwargs): + pagination = Pagination(**kwargs) + filter_kwargs = pagination.get_filter() with uow: - li = uow.subscriptions.list() - return [r.serialize() for r in li] + li = uow.subscriptions.list_with_count(**filter_kwargs) + return pagination.get_result(li) def subscription_one(subscriptionId: str, diff --git a/requirements-test.txt b/requirements-test.txt index 4abb891..d60633c 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -8,6 +8,7 @@ pytest pytest-cov pytest-icdiff mock +mock-alchemy>=0.1.0,<0.2.0 tenacity pyOpenSSL diff --git a/tests/conftest.py b/tests/conftest.py index 68da7c4..b653ae8 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -27,6 +27,7 @@ from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker, clear_mappers from tenacity import retry, stop_after_delay from unittest.mock import MagicMock +from mock_alchemy.mocking import UnifiedAlchemyMagicMock from o2app.bootstrap import bootstrap from o2ims.views import configure_namespace @@ -46,6 +47,13 @@ def mock_uow(): return session, uow +@pytest.fixture +def mock_alchemy_uow(): + session = UnifiedAlchemyMagicMock() + uow = unit_of_work.SqlAlchemyUnitOfWork(session_factory=session) + return session, uow + + @pytest.fixture def mock_flask_uow(mock_uow): session, uow = mock_uow diff --git a/tests/unit/test_alarm.py b/tests/unit/test_alarm.py index 5bcde82..0bc8ee2 100644 --- a/tests/unit/test_alarm.py +++ b/tests/unit/test_alarm.py @@ -52,11 +52,17 @@ def test_view_alarm_event_records(mock_uow): alarm_event_record1 = MagicMock() alarm_event_record1.serialize.return_value = { "alarmEventRecordId": alarm_event_record_id1} - session.return_value.query.return_value = [alarm_event_record1] - alarm_event_record_list = alarm_view.alarm_event_records(uow) - assert str(alarm_event_record_list[0].get( - "alarmEventRecordId")) == alarm_event_record_id1 + order_by = MagicMock() + order_by.count.return_value = 1 + order_by.limit.return_value.offset.return_value = [alarm_event_record1] + session.return_value.query.return_value.filter_by.return_value.\ + order_by.return_value = order_by + + result = alarm_view.alarm_event_records(uow) + assert result['count'] == 1 + ret_list = result['results'] + assert str(ret_list[0].get("alarmEventRecordId")) == alarm_event_record_id1 def test_view_alarm_event_record_one(mock_uow): @@ -81,6 +87,50 @@ def test_view_alarm_event_record_one(mock_uow): "alarmEventRecordId")) == alarm_event_record_id1 +def test_view_alarm_subscriptions(mock_uow): + session, uow = mock_uow + + subscription_id1 = str(uuid.uuid4()) + sub1 = MagicMock() + sub1.serialize.return_value = { + "alarmSubscriptionId": subscription_id1, + } + + order_by = MagicMock() + order_by.count.return_value = 1 + order_by.limit.return_value.offset.return_value = [sub1] + session.return_value.query.return_value.filter_by.return_value.\ + order_by.return_value = order_by + + result = alarm_view.subscriptions(uow) + assert result['count'] == 1 + ret_list = result['results'] + assert str(ret_list[0].get("alarmSubscriptionId")) == subscription_id1 + + +def test_view_alarm_subscription_one(mock_uow): + session, uow = mock_uow + + subscription_id1 = str(uuid.uuid4()) + session.return_value.query.return_value.filter_by.return_value.first.\ + return_value.serialize.return_value = None + + # Query return None + subscription_res = alarm_view.subscription_one( + subscription_id1, uow) + assert subscription_res is None + + session.return_value.query.return_value.filter_by.return_value.first.\ + return_value.serialize.return_value = { + "alarmSubscriptionId": subscription_id1, + } + + subscription_res = alarm_view.subscription_one( + subscription_id1, uow) + assert str(subscription_res.get( + "alarmSubscriptionId")) == subscription_id1 + + def test_alarm_dictionary(mock_uow): session, uow = mock_uow alarm_dict1 = alarm_obj.AlarmDictionary('test1') @@ -97,7 +147,11 @@ def test_alarm_dictionary(mock_uow): def test_flask_get_list(mock_flask_uow): session, app = mock_flask_uow - session.query.return_value = [] + order_by = MagicMock() + order_by.count.return_value = 0 + order_by.limit.return_value.offset.return_value = [] + session.return_value.query.return_value.filter_by.return_value.\ + order_by.return_value = order_by apibase = config.get_o2ims_monitoring_api_base() with app.test_client() as client: diff --git a/tests/unit/test_ocloud.py b/tests/unit/test_ocloud.py index 56d4e57..8be1e42 100644 --- a/tests/unit/test_ocloud.py +++ b/tests/unit/test_ocloud.py @@ -14,7 +14,6 @@ import uuid from unittest.mock import MagicMock -# from o2dms.domain import dms from o2ims.domain import ocloud, subscription_obj from o2ims.domain import resource_type as rt @@ -135,11 +134,17 @@ def test_view_resource_types(mock_uow): restype1 = MagicMock() restype1.serialize.return_value = { "resourceTypeId": resource_type_id1} - session.return_value.query.return_value = [restype1] - resource_type_list = ocloud_view.resource_types(uow) - assert str(resource_type_list[0].get( - "resourceTypeId")) == resource_type_id1 + order_by = MagicMock() + order_by.count.return_value = 1 + order_by.limit.return_value.offset.return_value = [restype1] + session.return_value.query.return_value.filter_by.return_value.\ + order_by.return_value = order_by + + result = ocloud_view.resource_types(uow) + assert result['count'] == 1 + ret_list = result['results'] + assert str(ret_list[0].get("resourceTypeId")) == resource_type_id1 def test_view_resource_type_one(mock_uow): @@ -169,11 +174,17 @@ def test_view_resource_pools(mock_uow): respool1 = MagicMock() respool1.serialize.return_value = { "resourcePoolId": resource_pool_id1} - session.return_value.query.return_value = [respool1] - resource_pool_list = ocloud_view.resource_pools(uow) - assert str(resource_pool_list[0].get( - "resourcePoolId")) == resource_pool_id1 + order_by = MagicMock() + order_by.count.return_value = 1 + order_by.limit.return_value.offset.return_value = [respool1] + session.return_value.query.return_value.filter_by.return_value.\ + order_by.return_value = order_by + + result = ocloud_view.resource_pools(uow) + assert result['count'] == 1 + ret_list = result['results'] + assert str(ret_list[0].get("resourcePoolId")) == resource_pool_id1 def test_view_resource_pool_one(mock_uow): @@ -207,9 +218,16 @@ def test_view_resources(mock_uow): "resourceId": resource_id1, "resourcePoolId": resource_pool_id1 } - session.return_value.query.return_value.filter_by.return_value = [res1] - resource_list = ocloud_view.resources(resource_pool_id1, uow) + order_by = MagicMock() + order_by.count.return_value = 1 + order_by.limit.return_value.offset.return_value = [res1] + session.return_value.query.return_value.filter_by.return_value.\ + order_by.return_value = order_by + + result = ocloud_view.resources(resource_pool_id1, uow) + assert result['count'] == 1 + resource_list = result['results'] assert str(resource_list[0].get("resourceId")) == resource_id1 assert str(resource_list[0].get("resourcePoolId")) == resource_pool_id1 @@ -244,11 +262,18 @@ def test_view_deployment_managers(mock_uow): dm1.serialize.return_value = { "deploymentManagerId": deployment_manager_id1, } - session.return_value.query.return_value = [dm1] - deployment_manager_list = ocloud_view.deployment_managers(uow) - assert str(deployment_manager_list[0].get( - "deploymentManagerId")) == deployment_manager_id1 + order_by = MagicMock() + order_by.count.return_value = 1 + order_by.limit.return_value.offset.return_value = [dm1] + session.return_value.query.return_value.filter_by.return_value.\ + order_by.return_value = order_by + + result = ocloud_view.deployment_managers(uow) + assert result['count'] == 1 + ret_list = result['results'] + assert str(ret_list[0].get("deploymentManagerId") + ) == deployment_manager_id1 def test_view_deployment_manager_one(mock_uow): @@ -313,11 +338,17 @@ def test_view_subscriptions(mock_uow): sub1.serialize.return_value = { "subscriptionId": subscription_id1, } - session.return_value.query.return_value = [sub1] - subscription_list = ocloud_view.subscriptions(uow) - assert str(subscription_list[0].get( - "subscriptionId")) == subscription_id1 + order_by = MagicMock() + order_by.count.return_value = 1 + order_by.limit.return_value.offset.return_value = [sub1] + session.return_value.query.return_value.filter_by.return_value.\ + order_by.return_value = order_by + + result = ocloud_view.subscriptions(uow) + assert result['count'] == 1 + ret_list = result['results'] + assert str(ret_list[0].get("subscriptionId")) == subscription_id1 def test_view_subscription_one(mock_uow): @@ -345,7 +376,11 @@ def test_view_subscription_one(mock_uow): def test_flask_get_list(mock_flask_uow): session, app = mock_flask_uow - session.query.return_value = [] + order_by = MagicMock() + order_by.count.return_value = 0 + order_by.limit.return_value.offset.return_value = [] + session.return_value.query.return_value.filter_by.return_value.\ + order_by.return_value = order_by apibase = config.get_o2ims_api_base() with app.test_client() as client: