def setUp(self): self.formatter = NINJSFormatter() init_app(self.app) self.maxDiff = None
class PublishService(BaseService): """A service for publishing to the content api. Serves mainly as a proxy to the data layer. """ formatter = NINJSFormatter() subscriber = {'config': {}} def publish(self, item, subscribers=[]): """Publish an item to content api. This must be enabled via ``PUBLISH_TO_CONTENT_API`` setting. :param item: item to publish """ if not self._filter_item(item): doc = self.formatter._transform_to_ninjs(item, self.subscriber) now = utcnow() doc.setdefault('firstcreated', now) doc.setdefault('versioncreated', now) doc.setdefault(config.VERSION, item.get(config.VERSION, 1)) doc['subscribers'] = [str(sub['_id']) for sub in subscribers] if 'evolvedfrom' in doc: parent_item = self.find_one(req=None, _id=doc['evolvedfrom']) if parent_item: doc['ancestors'] = copy(parent_item.get('ancestors', [])) doc['ancestors'].append(doc['evolvedfrom']) else: logger.warning( "Failed to find evolvedfrom item '{}' for '{}'".format( doc['evolvedfrom'], doc['guid'])) self._assign_associations(item, doc) logger.info('publishing %s to %s' % (doc['guid'], subscribers)) return self._create_doc(doc) else: return None def create(self, docs, **kwargs): ids = [] for doc in docs: ids.extend(self._create_doc(doc, **kwargs)) return ids def _create_doc(self, doc, **kwargs): """Create a new item or update existing.""" item = copy(doc) item.setdefault('_id', item.get('guid')) _id = item[config.ID_FIELD] = item.pop('guid') # merging the existing and new subscribers original = self.find_one(req=None, _id=_id) if original: item['subscribers'] = list( set(original.get('subscribers', [])) | set(item.get('subscribers', []))) self._process_associations(item, original) self._create_version_doc(item) if original: self.update(_id, item, original) return _id else: return super().create([item], **kwargs)[0] def _create_version_doc(self, item): """ Store the item in the item version collection :param item: :return: """ version_item = copy(item) version_item['_id_document'] = version_item.pop('_id') get_resource_service('items_versions').create([version_item]) # if the update is a cancel we need to cancel all versions if item.get('pubstatus', '') == 'canceled': self._cancel_versions(item.get('_id')) def _cancel_versions(self, doc_id): """ Given an id of a document set the pubstatus to canceled for all versions :param doc_id: :return: """ query = {'_id_document': doc_id} update = {'pubstatus': 'canceled'} for item in get_resource_service('items_versions').get_from_mongo( req=None, lookup=query): if item.get('pubstatus') != 'canceled': get_resource_service('items_versions').update( item['_id'], update, item) def _filter_item(self, item): """ Filter the item out if it matches any API Block filter conditions :param item: :return: True of the item is blocked, False if it is OK to publish it on the API. """ # Get the API blocking Filters req = ParsedRequest() filter_conditions = list( get_resource_service('content_filters').get( req=req, lookup={'api_block': True})) # No API blocking filters if not filter_conditions: return False filter_service = get_resource_service('content_filters') for fc in filter_conditions: if filter_service.does_match(fc, item): logger.info('API Filter block {} matched for item {}.'.format( fc, item.get(config.ID_FIELD))) return True return False def _assign_associations(self, item, doc): """Assign Associations to published item :param dict item: item being published :param dit doc: ninjs documents """ if item[ITEM_TYPE] != CONTENT_TYPE.TEXT: return for assoc, assoc_item in (item.get('associations') or {}).items(): if not assoc_item: continue doc.get('associations', {}).get(assoc)['subscribers'] = list( map(str, assoc_item.get('subscribers') or [])) def _process_associations(self, updates, original): """Update associations using existing published item and ensure that associated item subscribers are equal or subset of the parent subscribers. :param updates: :param original: :return: """ if updates[ITEM_TYPE] != CONTENT_TYPE.TEXT: return subscribers = updates.get('subscribers') or [] for assoc, update_assoc in (updates.get('associations') or {}).items(): if not update_assoc: continue if original: original_assoc = (original.get('associations') or {}).get(assoc) if original_assoc and original_assoc.get( config.ID_FIELD) == update_assoc.get(config.ID_FIELD): update_assoc['subscribers'] = list( set(original_assoc.get('subscribers') or []) | set(update_assoc.get('subscribers') or [])) update_assoc['subscribers'] = list( set(update_assoc['subscribers']) & set(subscribers))
class PublishService(BaseService): """A service for publishing to the content api. Serves mainly as a proxy to the data layer. """ formatter = NINJSFormatter() subscriber = {'config': {}} def publish(self, item, subscribers=[]): """Publish an item to content api. This must be enabled via ``PUBLISH_TO_CONTENT_API`` setting. :param item: item to publish """ if not self._filter_item(item): doc = self.formatter._transform_to_ninjs(item, self.subscriber) now = utcnow() doc.setdefault('firstcreated', now) doc.setdefault('versioncreated', now) doc.setdefault(config.VERSION, item.get(config.VERSION, 1)) doc['subscribers'] = [str(sub['_id']) for sub in subscribers] logger.info('publishing %s to %s' % (doc['guid'], subscribers)) return self._create_doc(doc) else: return None def create(self, docs, **kwargs): ids = [] for doc in docs: ids.extend(self._create_doc(doc, **kwargs)) return ids def _create_doc(self, doc, **kwargs): """Create a new item or update existing.""" item = copy(doc) item.setdefault('_id', item.get('guid')) _id = item[config.ID_FIELD] = item.pop('guid') # merging the existing and new subscribers original = self.find_one(req=None, _id=_id) if original: item['subscribers'] = list(set(original.get('subscribers', [])) | set(item.get('subscribers', []))) self._create_version_doc(item) if original: self.update(_id, item, original) return _id else: return super().create([item], **kwargs)[0] def _create_version_doc(self, item): """ Store the item in the item version collection :param item: :return: """ version_item = copy(item) version_item['_id_document'] = version_item.pop('_id') get_resource_service('items_versions').create([version_item]) def _filter_item(self, item): """ Filter the item out if it matches any API Block filter conditions :param item: :return: True of the item is blocked, False if it is OK to publish it on the API. """ # Get the API blocking Filters req = ParsedRequest() filter_conditions = list(get_resource_service('content_filters').get(req=req, lookup={'api_block': True})) # No API blocking filters if not filter_conditions: return False filter_service = get_resource_service('content_filters') for fc in filter_conditions: if filter_service.does_match(fc, item): logger.info('API Filter block {} matched for item {}.'.format(fc, item.get(config.ID_FIELD))) return True return False