class DestinationGroupsResource(Resource): schema = { 'name': { 'type': 'string', 'iunique': True, 'required': True, }, 'description': { 'type': 'string' }, 'destination_groups': { 'type': 'list', 'schema': Resource.rel('destination_groups', True) }, 'output_channels': { 'type': 'list', 'schema': { 'type': 'dict', 'schema': { 'channel': Resource.rel('output_channels', True), 'selector_codes': { 'type': 'list' } } } } } datasource = {'default_sort': [('name', -1)]} privileges = { 'POST': 'destination_groups', 'DELETE': 'destination_groups', 'PATCH': 'destination_groups' }
class TaskResource(Resource): datasource = { 'source': 'archive', 'default_sort': [('_updated', -1)], 'filter': {'task': {'$exists': True}}, 'elastic_filter': {'exists': {'field': 'task'}} # eve-elastic specific filter } item_url = item_url schema = { 'slugline': metadata_schema['slugline'], 'description_text': metadata_schema['description'], 'type': metadata_schema['type'], 'planning_item': Resource.rel('planning', True, type='string'), 'task': { 'type': 'dict', 'schema': { 'status': { 'type': 'string', 'allowed': ['todo', 'in-progress', 'done'], 'default': 'todo' }, 'due_date': {'type': 'datetime'}, 'started_at': {'type': 'datetime'}, 'finished_at': {'type': 'datetime'}, 'user': Resource.rel('users', True), 'desk': Resource.rel('desks', True), 'stage': Resource.rel('stages', True) } } }
class InternalDestinationsResource(Resource): schema = { "name": { "type": "string", "required": True }, "is_active": { "type": "boolean", "required": True }, "filter": Resource.rel("content_filters", nullable=True), "desk": Resource.rel("desks", nullable=False, required=True), "stage": Resource.rel("stages", nullable=True), "macro": { "type": "string", "nullable": True }, "send_after_schedule": { "type": "boolean" }, } privileges = { "POST": "internal_destinations", "PATCH": "internal_destinations", "DELETE": "internal_destinations" }
class HighlightsResource(Resource): ''' Highlights schema ''' schema = { 'name': { 'type': 'string', 'iunique': True, 'required': True }, 'template': Resource.rel('content_templates', nullable=True), 'desks': { 'type': 'list', 'schema': Resource.rel('desks', True) }, 'auto_insert': { 'type': 'string', 'allowed': allowed_times, 'default': TODAY_DATE, }, 'groups': { 'type': 'list', 'schema': { 'type': 'string' } } } privileges = { 'POST': 'highlights', 'PATCH': 'highlights', 'DELETE': 'highlights' }
class InternalDestinationsResource(Resource): schema = { 'name': { 'type': 'string', 'required': True }, 'is_active': { 'type': 'boolean', 'required': True }, 'filter': Resource.rel('content_filters', nullable=True), 'desk': Resource.rel('desks', nullable=False, required=True), 'stage': Resource.rel('stages', nullable=True), 'macro': { 'type': 'string', 'nullable': True }, 'send_after_schedule': { 'type': 'boolean' }, } privileges = { 'POST': 'internal_destinations', 'PATCH': 'internal_destinations', 'DELETE': 'internal_destinations' }
class MoveResource(Resource): endpoint_name = "move" resource_title = endpoint_name schema = { "task": { "type": "dict", "required": False, "schema": { "desk": Resource.rel("desks", False, required=True), "stage": Resource.rel("stages", False, required=True), }, }, "allPackageItems": { "type": "boolean", "required": False, "nullable": True }, } url = "archive/<{0}:guid>/move".format(item_url) resource_methods = ["POST"] item_methods = [] privileges = {"POST": "archive"}
class ActivityResource(Resource): endpoint_name = 'activity' resource_methods = ['GET'] item_methods = ['GET', 'PATCH'] schema = { 'name': { 'type': 'string' }, 'message': { 'type': 'string' }, 'data': { 'type': 'dict' }, 'read': { 'type': 'dict' }, 'item': Resource.rel('archive', type='string'), 'user': Resource.rel('users'), 'desk': Resource.rel('desks'), 'resource': { 'type': 'string' } } exclude = {endpoint_name, 'notification'} datasource = {'default_sort': [('_created', -1)]} superdesk.register_default_user_preference( 'email:notification', { 'type': 'bool', 'enabled': True, 'default': True, 'label': 'Send notifications via email', 'category': 'notifications', })
class TaskResource(Resource): datasource = { 'source': 'archive', 'default_sort': [('_updated', -1)], 'filter': {'task': {'$exists': True}}, 'elastic_filter': {'bool': { 'must': {'exists': {'field': 'task'}}, 'must_not': {'term': {ITEM_STATE: 'spiked'}}, }} } item_url = item_url schema = { 'slugline': metadata_schema['slugline'], 'description_text': metadata_schema['description_text'], 'type': metadata_schema['type'], 'planning_item': Resource.rel('planning', True, type='string'), 'task': { 'type': 'dict', 'schema': { 'status': { 'type': 'string', 'allowed': task_statuses, 'default': default_status }, 'due_date': {'type': 'datetime'}, 'started_at': {'type': 'datetime'}, 'finished_at': {'type': 'datetime'}, 'user': Resource.rel('users', True), 'desk': Resource.rel('desks', True), 'stage': Resource.rel('stages', True) } } } privileges = {'POST': 'tasks', 'PATCH': 'tasks', 'DELETE': 'tasks'}
class MoveResource(Resource): endpoint_name = 'move' resource_title = endpoint_name schema = { 'task': { 'type': 'dict', 'required': True, 'schema': { 'desk': Resource.rel('desks', False, required=True), 'stage': Resource.rel('stages', False, required=True) } }, 'allPackageItems': { 'type': 'boolean', 'required': False, 'nullable': True }, } url = 'archive/<{0}:guid>/move'.format(item_url) resource_methods = ['POST'] item_methods = [] privileges = {'POST': 'archive'}
class CoverageResource(Resource): schema = { 'headline': { 'type': 'string' }, 'coverage_type': { 'type': 'string', 'allowed': ['story', 'photo', 'video', 'graphics', 'live-blogging'], 'default': 'story', }, 'ed_note': { 'type': 'string' }, 'scheduled': { 'type': 'datetime' }, 'delivery': { 'type': 'string' }, 'assigned_user': Resource.rel('users', True), 'assigned_desk': Resource.rel('desks', True), 'planning_item': Resource.rel('planning', True, type='string'), } datasource = {'default_sort': [('_created', -1)]} privileges = { 'POST': 'planning', 'PATCH': 'planning', 'DELETE': 'planning' }
class StagesResource(Resource): schema = { 'name': { 'type': 'string', 'required': True, 'minlength': 1 }, 'description': { 'type': 'string', 'minlength': 1 }, 'default_incoming': { 'type': 'boolean', 'required': True, 'default': False }, 'desk': Resource.rel('desks', embeddable=True), 'outgoing': { 'type': 'list', 'schema': { 'type': 'dict', 'schema': { 'stage': Resource.rel('stages', True) } } } } privileges = {'POST': 'desks', 'DELETE': 'desks', 'PATCH': 'desks'}
class ContentViewResource(Resource): endpoint_name = 'content_view' schema = { 'name': { 'type': 'string', 'required': True, 'minlength': 1 }, 'location': { 'type': 'string', 'allowed': ['ingest', 'archive'], 'default': 'archive' }, 'description': { 'type': 'string', 'minlength': 1 }, 'desk': Resource.rel('desks', True), 'user': Resource.rel('users', True), 'filter': { 'type': 'dict' }, 'hateoas': { 'self': '/{location}/{_id}' } } privileges = { 'POST': 'archive', 'PATCH': 'archive', 'PUT': 'archive', 'DELETE': 'archive' }
class ContentFilterResource(Resource): schema = { 'name': { 'type': 'string', 'required': True, 'nullable': False, 'empty': False, 'iunique': True }, 'content_filter': { 'type': 'list', 'schema': { 'type': 'dict', 'schema': { 'expression': { 'type': 'dict', 'schema': { 'fc': { 'type': 'list', 'schema': Resource.rel('filter_conditions', True) }, 'pf': { 'type': 'list', 'schema': Resource.rel('content_filters', True) } } } } } }, 'is_global': { 'type': 'boolean', 'default': False }, 'is_archived_filter': { 'type': 'boolean', 'default': False }, 'api_block': { 'type': 'boolean', 'default': False } } additional_lookup = {'url': 'regex("[\w,.:-]+")', 'field': 'name'} privileges = { 'POST': 'content_filters', 'PATCH': 'content_filters', 'DELETE': 'content_filters' } mongo_indexes = { 'name_1': ([('name', 1)], { 'unique': True }), }
class ContentFilterResource(Resource): schema = { "name": { "type": "string", "required": True, "nullable": False, "empty": False, "iunique": True }, "content_filter": { "type": "list", "schema": { "type": "dict", "schema": { "expression": { "type": "dict", "schema": { "fc": { "type": "list", "schema": Resource.rel("filter_conditions", True) }, "pf": { "type": "list", "schema": Resource.rel("content_filters", True) }, }, } }, }, }, "is_global": { "type": "boolean", "default": False }, "is_archived_filter": { "type": "boolean", "default": False }, "api_block": { "type": "boolean", "default": False }, } additional_lookup = {"url": 'regex("[\w,.:-]+")', "field": "name"} privileges = { "POST": "content_filters", "PATCH": "content_filters", "DELETE": "content_filters" } mongo_indexes = { "name_1": ([("name", 1)], { "unique": True }), }
class ActivityResource(Resource): endpoint_name = 'activity' resource_methods = ['GET'] item_methods = ['GET', 'PATCH'] schema = { 'name': { 'type': 'string' }, 'message': { 'type': 'string' }, 'data': { 'type': 'dict' }, 'recipients': { 'type': 'list', 'schema': { 'type': 'dict', 'schema': { 'user_id': Resource.rel('users'), 'read': { 'type': 'boolean', 'default': False }, 'desk_id': Resource.rel('desks') } } }, 'item': Resource.rel('archive', type='string'), 'item_slugline': { 'type': 'string' }, 'user': Resource.rel('users'), 'user_name': { 'type': 'string' }, 'desk': Resource.rel('desks'), 'resource': { 'type': 'string' } } exclude = {endpoint_name, 'notification'} datasource = { 'default_sort': [('_created', -1)], 'filter': { '_created': { '$gte': utcnow() - datetime.timedelta(days=1) } } } superdesk.register_default_user_preference( 'email:notification', { 'type': 'bool', 'enabled': True, 'default': True, 'label': 'Send notifications via email', 'category': 'notifications', })
class PostsResource(ArchiveResource): datasource = { 'source': 'archive', 'elastic_filter_callback': private_draft_filter, 'elastic_filter': {'term': {'particular_type': 'post'}}, 'default_sort': DEFAULT_POSTS_ORDER } item_methods = ['GET', 'PATCH', 'DELETE'] schema = {} schema.update(ArchiveResource.schema) schema.update({ 'blog': Resource.rel('blogs', True), 'particular_type': { 'type': 'string', 'allowed': ['post', 'item'], 'default': 'post' }, 'post_status': { 'type': 'string', 'allowed': ['open', 'draft', 'submitted', 'comment'], 'default': 'open' }, 'lb_highlight': { 'type': 'boolean', 'default': False }, 'sticky': { 'type': 'boolean', 'default': False }, 'deleted': { 'type': 'boolean', 'default': False }, 'order': { 'type': 'float', 'default': 0.00 }, 'published_date': { 'type': 'datetime' }, 'unpublished_date': { 'type': 'datetime' }, 'publisher': Resource.rel('users', True), 'content_updated_date': { 'type': 'datetime' }, 'syndication_in': Resource.rel('syndication_in', embeddable=True, required=False, nullable=True), 'producer_post_id': { 'type': 'string', 'nullable': True } }) privileges = {'GET': 'posts', 'POST': 'posts', 'PATCH': 'posts', 'DELETE': 'posts'}
class TaskResource(Resource): datasource = { "source": "archive", "default_sort": [("_updated", -1)], "filter": { "task": { "$exists": True } }, "elastic_filter": { "bool": { "must": { "exists": { "field": "task" } }, "must_not": { "term": { ITEM_STATE: "spiked" } }, } }, } item_url = item_url schema = { "expiry": { "type": "string" }, "slugline": metadata_schema["slugline"], "description_text": metadata_schema["description_text"], "type": metadata_schema["type"], "task": { "type": "dict", "schema": { "status": { "type": "string", "allowed": task_statuses, "default": default_status }, "due_date": { "type": "datetime" }, "started_at": { "type": "datetime" }, "finished_at": { "type": "datetime" }, "user": Resource.rel("users", True), "desk": Resource.rel("desks", True), "stage": Resource.rel("stages", True), }, }, } privileges = {"POST": "tasks", "PATCH": "tasks", "DELETE": "tasks"}
def __init__(self, endpoint_name, app, service, endpoint_schema=None): self.schema = { "name": {"type": "string", "required": True, "nullable": False, "empty": False, "iunique": True}, "source": required_string, "feeding_service": {"type": "string", "required": True, "allowed": allowed_feeding_services}, "feed_parser": {"type": "string", "nullable": True, "allowed": allowed_feed_parsers}, "content_types": {"type": "list", "default": content_type, "allowed": content_type}, "allow_remove_ingested": {"type": "boolean", "default": False}, "content_expiry": {"type": "integer", "default": app.config["INGEST_EXPIRY_MINUTES"]}, "config": {"type": "dict"}, "ingested_count": {"type": "integer"}, "accepted_count": {"type": "integer"}, "tokens": {"type": "dict"}, "is_closed": {"type": "boolean", "default": False}, "update_schedule": { "type": "dict", "schema": { "hours": {"type": "integer"}, "minutes": {"type": "integer", "default": 5}, "seconds": {"type": "integer"}, }, }, "idle_time": {"type": "dict", "schema": {"hours": {"type": "integer"}, "minutes": {"type": "integer"}}}, "last_updated": {"type": "datetime"}, "last_item_update": {"type": "datetime"}, "rule_set": Resource.rel("rule_sets", nullable=True), "notifications": { "type": "dict", "schema": { "on_update": {"type": "boolean", "default": True}, "on_close": {"type": "boolean", "default": True}, "on_open": {"type": "boolean", "default": True}, "on_error": {"type": "boolean", "default": True}, }, }, "routing_scheme": Resource.rel("routing_schemes", nullable=True), "last_closed": { "type": "dict", "schema": { "closed_at": {"type": "datetime"}, "closed_by": Resource.rel("users", nullable=True), "message": {"type": "string"}, }, }, "last_opened": { "type": "dict", "schema": {"opened_at": {"type": "datetime"}, "opened_by": Resource.rel("users", nullable=True)}, }, "critical_errors": {"type": "dict", "valueschema": {"type": "boolean"}}, } self.item_methods = ["GET", "PATCH", "DELETE"] self.privileges = {"POST": "ingest_providers", "PATCH": "ingest_providers", "DELETE": "ingest_providers"} self.etag_ignore_fields = ["last_updated", "last_item_update", "last_closed", "last_opened"] super().__init__(endpoint_name, app, service, endpoint_schema=endpoint_schema)
class SearchIngestResource(superdesk.Resource): resource_methods = ['GET', 'POST'] schema = { 'guid': { 'type': 'string', 'required': True }, 'desk': Resource.rel('desks', False, nullable=True), 'stage': Resource.rel('stages', False, nullable=True) }
class SearchIngestResource(superdesk.Resource): resource_methods = ["GET", "POST"] schema = { "guid": { "type": "string", "required": True }, "desk": Resource.rel("desks", False, nullable=True), "stage": Resource.rel("stages", False, nullable=True), }
class PublishQueueResource(Resource): schema = { "item_id": {"type": "string", "required": True}, "item_version": {"type": "integer", "nullable": False}, "formatted_item": {"type": "string", "nullable": False}, "item_encoding": {"type": "string", "nullable": True}, "encoded_item_id": {"type": "objectid", "nullable": True}, "subscriber_id": Resource.rel("subscribers"), "codes": {"type": "list", "nullable": True}, "destination": { "type": "dict", "schema": { "name": {"type": "string", "required": True, "empty": False}, "format": {"type": "string", "required": True}, "delivery_type": {"type": "string", "required": True}, "config": {"type": "dict"}, }, }, PUBLISHED_IN_PACKAGE: {"type": "string"}, "published_seq_num": {"type": "integer"}, # publish_schedule is to indicate the item schedule datetime. # entries in the queue are created after schedule has elapsed. "publish_schedule": {"type": "datetime"}, "publishing_action": {"type": "string"}, "unique_name": {"type": "string", "nullable": True}, "content_type": {"type": "string"}, "headline": {"type": "string", "nullable": True}, "transmit_started_at": {"type": "datetime"}, "completed_at": {"type": "datetime"}, "state": {"type": "string", "allowed": QueueState.values(), "nullable": False}, "error_message": {"type": "string"}, # to indicate the queue item is moved to legal # True is set after state of the item is success, cancelled or failed. For other state it is false "moved_to_legal": {"type": "boolean", "default": False}, "retry_attempt": {"type": "integer", "default": 0}, "next_retry_attempt_at": {"type": "datetime"}, "ingest_provider": Resource.rel("ingest_providers", nullable=True), "associated_items": {"type": "list", "nullable": True}, "priority": { "type": "boolean", "nullable": True, }, } additional_lookup = {"url": r'regex("[\w,.:-]+")', "field": "item_id"} etag_ignore_fields = ["moved_to_legal"] datasource: Dict[str, Any] = { "default_sort": [ ("_id", -1), ], } privileges = {"POST": "publish_queue", "PATCH": "publish_queue"} collation = False
class StagesOrderResource(Resource): schema = { "desk": Resource.rel("desks", required=True, type="string"), "stages": { "type": "list", "schema": Resource.rel("stages", required=True, type="string") }, } item_methods = [] resource_methods = ["POST"] privileges = {"POST": "desks"}
class SavedActivityReportResource(Resource): """Saved Activity Report schema """ schema = { 'name': { 'type': 'string', 'required': True, 'minlength': 1 }, 'description': { 'type': 'string' }, 'is_global': { 'type': 'boolean', 'default': False }, 'owner': Resource.rel('users', nullable=True), 'operation': { 'type': 'string', 'required': True }, 'desk': Resource.rel('desks', nullable=True), 'operation_start_date': { 'type': 'datetime', 'required': True }, 'operation_end_date': { 'type': 'datetime', 'required': True }, 'subject': metadata_schema['subject'], 'category': metadata_schema['anpa_category'], 'keywords': metadata_schema['keywords'], 'urgency_start': metadata_schema['urgency'], 'urgency_end': metadata_schema['urgency'], 'priority_start': metadata_schema['priority'], 'priority_end': metadata_schema['priority'], 'subscriber': { 'type': 'string' }, 'group_by': { 'type': 'list' } } item_methods = ['GET', 'PATCH', 'PUT', 'DELETE'] resource_methods = ['GET', 'POST'] privileges = { 'POST': 'activity_report', 'PATCH': 'activity_report', 'PUT': 'activity_report', 'DELETE': 'activity_report' }
def __init__(self, endpoint_name, app, service, endpoint_schema=None): self.readonly = True if app.config.get("LDAP_SERVER", None) else False self.additional_lookup = {"url": 'regex("[\w]+")', "field": "username"} self.schema = { "username": {"type": "string", "unique": True, "required": True, "minlength": 1}, "password": {"type": "string", "minlength": 5, "readonly": self.readonly}, "first_name": {"type": "string", "readonly": self.readonly}, "last_name": {"type": "string", "readonly": self.readonly}, "display_name": {"type": "string", "readonly": self.readonly}, "email": {"unique": True, "type": "email", "required": True}, "phone": {"type": "phone_number", "readonly": self.readonly, "nullable": True}, "language": {"type": "string", "readonly": self.readonly, "nullable": True}, "user_info": {"type": "dict"}, "picture_url": {"type": "string", "nullable": True}, "avatar": Resource.rel("upload", embeddable=True, nullable=True), "role": Resource.rel("roles", True), "privileges": {"type": "dict"}, "workspace": {"type": "dict"}, "user_type": {"type": "string", "allowed": ["user", "administrator"], "default": "user"}, "is_active": {"type": "boolean", "default": True}, "is_enabled": {"type": "boolean", "default": True}, "needs_activation": {"type": "boolean", "default": True}, "desk": Resource.rel("desks"), # Default desk of the user, which would be selected when logged-in. SIGN_OFF: { # Used for putting a sign-off on the content when it's created/updated except kill "type": "string", "required": True, "regex": "^[a-zA-Z0-9]+$", }, BYLINE: {"type": "string", "required": False, "nullable": True}, } self.extra_response_fields = [ "display_name", "username", "email", "user_info", "picture_url", "avatar", "is_active", "is_enabled", "needs_activation", "desk", ] self.etag_ignore_fields = ["session_preferences", "_etag"] self.datasource = {"projection": {"password": 0}, "default_sort": [("username", 1)]} self.privileges = {"POST": "users", "DELETE": "users", "PATCH": "users"} super().__init__(endpoint_name, app=app, service=service, endpoint_schema=endpoint_schema)
class ArchiveLinkResource(Resource): endpoint_name = 'archive_link' resource_title = endpoint_name schema = { 'link_id': Resource.rel('archive', embeddable=False, type='string'), 'desk': Resource.rel('desks', embeddable=False) } url = 'archive/<{0}:target_id>/link'.format(item_url) resource_methods = ['POST'] item_methods = []
class ActivityResource(Resource): endpoint_name = "activity" resource_methods = ["GET"] item_methods = ["GET", "PATCH"] schema = { "name": {"type": "string"}, "message": {"type": "string"}, "data": {"type": "dict", "schema": {}, "allow_unknown": True}, "recipients": { "type": "list", "schema": { "type": "dict", "schema": { "user_id": Resource.rel("users"), "read": {"type": "boolean", "default": False}, "desk_id": Resource.rel("desks"), }, }, }, "item": Resource.rel("archive", type="string"), "item_slugline": {"type": "string"}, "user": Resource.rel("users"), "user_name": {"type": "string"}, "desk": Resource.rel("desks"), "resource": {"type": "string"}, } exclude = {endpoint_name, "notification"} datasource = { "default_sort": [("_created", -1)], "filter": {"_created": {"$gte": utcnow() - datetime.timedelta(days=1)}}, } superdesk.register_default_user_preference( "email:notification", { "type": "bool", "enabled": True, "default": True, "label": _("Send notifications via email"), "category": _("notifications"), }, ) superdesk.register_default_user_preference( "desktop:notification", { "type": "bool", "enabled": True, "default": False, "label": _("Allow Desktop Notifications"), "category": _("notifications"), }, )
class TokensResource(Resource): """ Tokens schema """ schema = { 'client': Resource.rel('clients', True), 'user': Resource.rel('users', True), 'token_type': {'type': 'string', 'required': True}, 'access_token': {'type': 'string', 'required': True}, 'refresh_token': {'type': 'string', 'required': True}, 'expires': {'type': 'datetime', 'required': True}, } item_methods = ['GET', 'PATCH', 'PUT', 'DELETE'] resource_methods = ['GET', 'POST', 'DELETE']
class PostsResource(ArchiveResource): datasource = { 'source': 'archive', 'elastic_filter_callback': private_draft_filter, 'elastic_filter': { 'term': { 'particular_type': 'post' } }, 'default_sort': DEFAULT_POSTS_ORDER } item_methods = ['GET', 'PATCH', 'DELETE'] schema = {} schema.update(ArchiveResource.schema) schema.update({ 'blog': Resource.rel('blogs', True), 'particular_type': { 'type': 'string', 'allowed': ['post', 'item'], 'default': 'post' }, 'post_status': { 'type': 'string', 'allowed': ['open', 'draft', 'submitted'], 'default': 'open' }, 'deleted': { 'type': 'boolean', 'default': False }, 'order': { 'type': 'number', 'default': 0 }, 'published_date': { 'type': 'datetime' }, 'unpublished_date': { 'type': 'datetime' }, 'publisher': Resource.rel('users', True), }) privileges = { 'GET': 'posts', 'POST': 'posts', 'PATCH': 'posts', 'DELETE': 'posts' }
def item_schema(extra=None): """Create schema for item. :param extra: extra fields to be added to schema """ schema = { 'old_version': { 'type': 'number', }, 'last_version': { 'type': 'number', }, 'task': {'type': 'dict'}, 'destination_groups': { 'type': 'list', 'schema': Resource.rel('destination_groups', True) }, 'publish_schedule': { 'type': 'datetime', 'nullable': True }, 'marked_for_not_publication': { 'type': 'boolean', 'default': False } } schema.update(metadata_schema) if extra: schema.update(extra) return schema
class EventsPostResource(EventsResource): schema = { 'event': Resource.rel('events', type='string', required=True), 'etag': { 'type': 'string', 'required': True }, 'pubstatus': { 'type': 'string', 'required': True, 'allowed': post_state }, # The update method used for recurring events 'update_method': { 'type': 'string', 'allowed': UPDATE_METHODS, 'mapping': not_analyzed, 'nullable': True }, } url = 'events/post' resource_title = endpoint_name = 'events_post' resource_methods = ['POST'] privileges = {'POST': 'planning_event_post'} item_methods = []
class ProductsResource(Resource): ''' Products schema ''' schema = { 'name': { 'type': 'string', 'iunique': True, 'required': True }, 'description': { 'type': 'string' }, 'codes': { 'type': 'string' }, 'content_filter': { 'type': 'dict', 'schema': { 'filter_id': Resource.rel('content_filters', nullable=True), 'filter_type': { 'type': 'string', 'allowed': ['blocking', 'permitting'], 'default': 'blocking' } }, 'nullable': True } } privileges = {'POST': 'products', 'PATCH': 'products', 'DELETE': 'products'}
class ArchiveResource(Resource): schema = { 'old_version': { 'type': 'number', }, 'last_version': { 'type': 'number', }, 'task': { 'type': 'dict' }, 'destination_groups': { 'type': 'list', 'schema': Resource.rel('destination_groups', True) } } schema.update(metadata_schema) extra_response_fields = extra_response_fields item_url = item_url datasource = { 'search_backend': 'elastic', 'aggregations': aggregations, 'projection': { 'old_version': 0, 'last_version': 0 }, 'default_sort': [('_updated', -1)], 'elastic_filter_callback': private_content_filter } etag_ignore_fields = ['highlights'] resource_methods = ['GET', 'POST'] item_methods = ['GET', 'PATCH', 'PUT'] versioning = True privileges = {'POST': 'archive', 'PATCH': 'archive', 'PUT': 'archive'}
class SystemMessagesResource(Resource): schema = { "is_active": { "type": "boolean", "default": False }, "type": { "type": "string", "allowed": ["warning", "alert", "primary/info", "success"], "required": True, }, "message_title": { "type": "string", "required": True }, "message": { "type": "string", "required": True }, "user_id": Resource.rel("users", required=True), } resource_methods = ["GET", "POST"] item_methods = ["GET", "PATCH", "DELETE"] privileges = { "POST": "system_messages", "PATCH": "system_messages", "DELETE": "system_messages" }
import superdesk from superdesk.resource import Resource from superdesk.notification import push_notification from apps.activity import add_activity from superdesk.services import BaseService comments_schema = { 'text': { 'type': 'string', 'minlength': 1, 'maxlength': 500, 'required': True, }, 'item': Resource.rel('archive', True, True, type='string'), 'user': Resource.rel('users', True), 'mentioned_users': { 'type': 'dict' } } def check_item_valid(item_id): item = app.data.find_one('archive', req=None, _id=item_id) if not item: msg = 'Invalid content item ID provided: %s' % item_id raise superdesk.SuperdeskError(payload=msg) def get_users_mentions(text):
def __init__(self, endpoint_name, app, service, endpoint_schema=None): self.readonly = True if app.config.get('LDAP_SERVER', None) else False self.additional_lookup = { 'url': 'regex("[\w]+")', 'field': 'username' } self.schema = { 'username': { 'type': 'string', 'unique': True, 'required': True, 'minlength': 1 }, 'password': { 'type': 'string', 'minlength': 5 }, 'first_name': { 'type': 'string', 'readonly': self.readonly }, 'last_name': { 'type': 'string', 'readonly': self.readonly }, 'display_name': { 'type': 'string' }, 'email': { 'unique': True, 'type': 'email', 'required': True }, 'phone': { 'type': 'string', 'nullable': True }, 'language': { 'type': 'string', 'nullable': True }, 'user_info': { 'type': 'dict' }, 'picture_url': { 'type': 'string', 'nullable': True }, 'avatar': Resource.rel('upload', embeddable=True, nullable=True), 'role': Resource.rel('roles', True), 'privileges': {'type': 'dict'}, 'workspace': { 'type': 'dict' }, 'user_type': { 'type': 'string', 'allowed': ['user', 'administrator'], 'default': 'user' }, 'is_active': { 'type': 'boolean', 'default': True }, 'is_enabled': { 'type': 'boolean', 'default': True }, 'needs_activation': { 'type': 'boolean', 'default': True }, 'desk': Resource.rel('desks'), # Default desk of the user, which would be selected when logged-in. SIGN_OFF: { # Used for putting a sign-off on the content when it's created/updated except kill 'type': 'string', 'required': False, 'regex': '^[a-zA-Z0-9]+$' }, BYLINE: { 'type': 'string', 'required': False, 'nullable': True } } self.extra_response_fields = [ 'display_name', 'username', 'email', 'user_info', 'picture_url', 'avatar', 'is_active', 'is_enabled', 'needs_activation', 'desk' ] self.etag_ignore_fields = ['session_preferences', '_etag'] self.datasource = { 'projection': {'password': 0}, 'default_sort': [('username', 1)], } self.privileges = {'POST': 'users', 'DELETE': 'users', 'PATCH': 'users'} super().__init__(endpoint_name, app=app, service=service, endpoint_schema=endpoint_schema)
import flask import superdesk import eve.io.base blogs_schema = { 'guid': metadata_schema['guid'], 'title': metadata_schema['headline'], 'description': metadata_schema['description'], 'theme': { 'type': 'dict' }, 'picture_url': { 'type': 'string', 'nullable': True }, 'picture': Resource.rel('upload', embeddable=True, nullable=True), 'original_creator': metadata_schema['original_creator'], 'version_creator': metadata_schema['version_creator'], 'versioncreated': metadata_schema['versioncreated'], 'blog_status': { 'type': 'string', 'allowed': ['open', 'closed'], 'default': 'open' }, 'particular_type': { 'type': 'string', 'allowed': ['blog'], 'default': 'blog' }, 'members': { 'type': 'list',
from superdesk.emails import send_email import liveblog.embed from bson.objectid import ObjectId import superdesk from superdesk.users.services import is_admin from superdesk.errors import SuperdeskApiError import logging logger = logging.getLogger("superdesk") blogs_schema = { "title": metadata_schema["headline"], "description": metadata_schema["description"], "picture_url": {"type": "string", "nullable": True}, "picture": Resource.rel("archive", embeddable=True, nullable=True, type="string"), "original_creator": metadata_schema["original_creator"], "version_creator": metadata_schema["version_creator"], "versioncreated": metadata_schema["versioncreated"], "posts_order_sequence": {"type": "number", "default": 0}, "blog_status": {"type": "string", "allowed": ["open", "closed"], "default": "open"}, "members": {"type": "list", "schema": {"type": "dict", "schema": {"user": Resource.rel("users", True)}}}, "blog_preferences": {"type": "dict"}, "public_url": {"type": "string"}, } class BlogsResource(Resource): datasource = {"source": "blogs", "search_backend": "elastic", "default_sort": [("_updated", -1)]} item_methods = ["GET", "PATCH", "PUT", "DELETE"]
desks_schema = { 'name': { 'type': 'string', 'iunique': True, 'required': True, }, 'description': { 'type': 'string' }, 'members': { 'type': 'list', 'schema': { 'type': 'dict', 'schema': { 'user': Resource.rel('users', True) } } }, 'incoming_stage': Resource.rel('stages', True), 'published_stage': Resource.rel('stages', False), 'spike_expiry': { 'type': 'integer' } } def init_app(app): endpoint_name = 'desks' service = DesksService(endpoint_name, backend=superdesk.get_backend()) DesksResource(endpoint_name, app=app, service=service)
'name': { 'type': 'string', 'required': True, 'nullable': False, 'empty': False, 'iunique': True }, 'description': { 'type': 'string' }, 'members': { 'type': 'list', 'schema': { 'type': 'dict', 'schema': { 'user': Resource.rel('users', True) } } }, 'incoming_stage': Resource.rel('stages', True), 'working_stage': Resource.rel('stages', True), 'content_expiry': { 'type': 'integer' }, 'source': { 'type': 'string' }, 'monitoring_settings': { 'type': 'list', 'schema': { 'type': 'dict',
import superdesk from superdesk.users.services import is_admin from superdesk.errors import SuperdeskApiError import logging logger = logging.getLogger('superdesk') blogs_schema = { 'title': metadata_schema['headline'], 'description': metadata_schema['description'], 'picture_url': { 'type': 'string', 'nullable': True }, 'picture': Resource.rel('archive', embeddable=True, nullable=True, type='string'), 'original_creator': metadata_schema['original_creator'], 'version_creator': metadata_schema['version_creator'], 'versioncreated': metadata_schema['versioncreated'], 'posts_order_sequence': { 'type': 'number', 'default': 0 }, 'blog_status': { 'type': 'string', 'allowed': ['open', 'closed'], 'default': 'open' }, 'members': { 'type': 'list', 'schema': {
'urgency': { 'type': 'integer' }, 'groups': { 'type': 'list' }, 'keywords': { 'type': 'list' }, 'body_html': { 'type': 'string' }, 'creator': { 'type': 'dict', 'schema': { 'user': Resource.rel('users', True) } }, 'media_file': { 'type': 'string' }, 'contents': { 'type': 'list' }, 'media': { 'type': 'media' }, 'task_id': { 'type': 'string' }, 'lock_user': {
EMBARGO = "embargo" metadata_schema = { # Identifiers "guid": {"type": "string", "unique": True, "mapping": not_analyzed}, "unique_id": {"type": "integer", "unique": True}, "unique_name": {"type": "string", "unique": True, "mapping": not_analyzed}, "version": {"type": "integer"}, "ingest_id": {"type": "string", "mapping": not_analyzed}, "family_id": {"type": "string", "mapping": not_analyzed}, "related_to": { # this field keeps a reference to the related item from which metadata has been copied "type": "string", "mapping": not_analyzed, }, # Audit Information "original_creator": Resource.rel("users"), "version_creator": Resource.rel("users"), "firstcreated": {"type": "datetime"}, "versioncreated": {"type": "datetime"}, # Ingest Details "ingest_provider": Resource.rel("ingest_providers"), "source": {"type": "string", "mapping": not_analyzed}, # The value is copied from the ingest_providers vocabulary "original_source": {"type": "string", "mapping": not_analyzed}, # This value is extracted from the ingest "ingest_provider_sequence": {"type": "string", "mapping": not_analyzed}, # Copyright Information "usageterms": {"type": "string", "mapping": not_analyzed, "nullable": True}, # Category Details "anpa_category": { "type": "list", "nullable": True, "mapping": {"type": "object", "properties": {"qcode": not_analyzed, "name": not_analyzed}},
# # Copyright 2013, 2014 Sourcefabric z.u. and contributors. # # For the full copyright and license information, please see the # AUTHORS and LICENSE files distributed with this source code, or # at https://www.sourcefabric.org/superdesk/license import superdesk from superdesk.resource import Resource from superdesk.services import BaseService from apps.comments import CommentsService, CommentsResource, comments_schema from superdesk.errors import SuperdeskApiError comments_schema = dict(comments_schema) comments_schema.update({'item': Resource.rel('archive', True, True, type='string')}) class ItemCommentsResource(CommentsResource): schema = comments_schema resource_methods = ['GET', 'POST', 'DELETE'] datasource = {'default_sort': [('_created', -1)]} privileges = {'POST': 'archive', 'DELETE': 'archive'} class ItemCommentsService(CommentsService): notification_key = 'item:comment' class ItemCommentsSubResource(Resource): url = 'archive/<path:item>/comments'
username = g.user.get('display_name') or g.user.get('username') url = '{}/#/liveblog/settings/{}'.format(origin, doc['_id']) title = blog['title'] admins = app.config['ADMINS'] app_name = app.config['APPLICATION_NAME'] subject = render_template("owner_email_subject.txt", app_name=app_name) text_body = render_template("owner_request.txt", app_name=app_name, link=url, name_of_user=username, title=title) html_body = render_template("owner_request.html", app_name=app_name, link=url, name_of_user=username, title=title) send_email.delay(subject=subject, sender=admins[0], recipients=recipients, text_body=text_body, html_body=html_body) request_schema = { 'blog': Resource.rel('blogs', True), 'original_creator': Resource.rel('users', True), 'message': { 'type': 'string' } } class MembershipResource(Resource): schema = request_schema datasource = { 'source': 'request_membership', 'default_sort': [('_updated', -1)] } resource_methods = ['POST', 'GET'] item_methods = ['GET', 'DELETE']
}, 'ingest_id': { 'type': 'string', 'mapping': not_analyzed }, 'family_id': { 'type': 'string', 'mapping': not_analyzed }, 'related_to': { # this field keeps a reference to the related item from which metadata has been copied 'type': 'string', 'mapping': not_analyzed }, # Audit Information 'original_creator': Resource.rel('users'), 'version_creator': Resource.rel('users'), 'firstcreated': { 'type': 'datetime' }, 'versioncreated': { 'type': 'datetime' }, # Ingest Details 'ingest_provider': Resource.rel('ingest_providers'), 'source': { # The value is copied from the ingest_providers vocabulary 'type': 'string', 'mapping': not_analyzed }, 'original_source': { # This value is extracted from the ingest
'mapping': not_analyzed }, 'version': { 'type': 'integer' }, 'ingest_id': { 'type': 'string', 'mapping': not_analyzed }, 'family_id': { 'type': 'string', 'mapping': not_analyzed }, # Audit Information 'original_creator': Resource.rel('users'), 'version_creator': Resource.rel('users'), 'firstcreated': { 'type': 'datetime' }, 'versioncreated': { 'type': 'datetime' }, # Ingest Details 'ingest_provider': Resource.rel('ingest_providers'), 'source': { # The value is copied from the ingest_providers vocabulary 'type': 'string', 'mapping': not_analyzed }, 'original_source': { # This value is extracted from the ingest
'renditions': {'type': 'dict'}, 'service': {'type': 'list', 'mapping': code_mapping}, 'slugline': {'type': 'string'}, 'source': metadata_schema['source'], 'subject': {'type': 'list', 'mapping': code_mapping}, 'keywords': metadata_schema['keywords'], 'type': metadata_schema['type'], 'urgency': {'type': 'integer'}, 'priority': {'type': 'integer'}, 'uri': metadata_schema['guid'], # we use guid value for uri, so index it in a same way 'usageterms': {'type': 'string'}, 'version': {'type': 'string', 'required': True, 'empty': False, 'nullable': False}, 'versioncreated': {'type': 'datetime', 'required': True}, 'firstcreated': {'type': 'datetime'}, 'firstpublished': {'type': 'datetime', 'required': False}, 'evolvedfrom': Resource.not_analyzed_field(), 'nextversion': Resource.not_analyzed_field(), 'subscribers': Resource.not_analyzed_field('list'), 'ednote': {'type': 'string'}, 'signal': { 'type': 'list', 'mapping': { 'type': 'object', 'properties': { 'code': not_analyzed, 'name': not_analyzed, 'scheme': not_analyzed } } }, 'genre': {'type': 'list', 'mapping': code_mapping},
def __init__(self, endpoint_name, app, service, endpoint_schema=None): self.readonly = True if app.config.get('LDAP_SERVER', None) else False self.additional_lookup = { 'url': r'regex("[\w]+")', 'field': 'username' } self.schema = { 'username': { 'type': 'string', 'unique': True, 'required': True, 'minlength': 1 }, 'password': { 'type': 'string', 'minlength': 5 }, 'password_changed_on': { 'type': 'datetime', 'nullable': True }, 'first_name': { 'type': 'string', 'readonly': self.readonly }, 'last_name': { 'type': 'string', 'readonly': self.readonly }, 'display_name': { 'type': 'string' }, 'email': { 'unique': True, 'type': 'email', 'required': True, 'coerce': lambda s: s.lower() }, 'phone': { 'type': 'string', 'nullable': True }, 'job_title': { 'type': 'string', 'required': False, }, 'biography': { 'type': 'string', 'required': False, 'nullable': True, }, 'facebook': { 'type': 'string', 'required': False, 'nullable': True, }, 'instagram': { 'type': 'string', 'required': False, 'nullable': True, }, 'twitter': { 'type': 'string', 'required': False, 'nullable': True, 'twitter': True, }, 'jid': { 'unique': True, 'type': 'string', 'required': False, }, 'language': { 'type': 'string', 'nullable': True }, 'user_info': { 'type': 'dict' }, 'picture_url': { 'type': 'string', 'nullable': True }, 'avatar': Resource.rel('upload', embeddable=True, nullable=True), 'avatar_renditions': {'type': 'dict'}, 'role': Resource.rel('roles', True), 'privileges': {'type': 'dict'}, 'workspace': { 'type': 'dict' }, 'user_type': { 'type': 'string', 'allowed': ['user', 'administrator'], 'default': 'user' }, 'is_support': { 'type': 'boolean', 'default': False }, 'is_author': { 'type': 'boolean', 'default': True }, 'is_active': { 'type': 'boolean', 'default': True }, 'is_enabled': { 'type': 'boolean', 'default': True }, 'needs_activation': { 'type': 'boolean', 'default': True }, # Default desk of the user, which would be selected when logged-in. 'desk': Resource.rel('desks', nullable=True), SIGN_OFF: { # Used for putting a sign-off on the content when it's created/updated except kill 'type': 'string', 'required': False, 'nullable': True, 'regex': '^[a-zA-Z0-9]+$' }, BYLINE: { 'type': 'string', 'required': False, 'nullable': True }, # list to hold invisible stages. # This field is updated under following scenario: # 1. stage visible flag is updated # 2. desk membership is modified # 3. new user is created 'invisible_stages': { 'type': 'list', 'required': False, 'nullable': True }, # If Slack notifications are configured and enabled for the user # the Slack username is stored here. 'slack_username': { 'type': 'string', 'required': False, 'nullable': True }, # The Slack user id is stored here, to avoid repeatedly having to look it up 'slack_user_id': { 'type': 'string', 'required': False, 'nullable': True } } self.extra_response_fields = [ 'display_name', 'username', 'email', 'user_info', 'picture_url', 'avatar', 'is_active', 'is_enabled', 'needs_activation', 'desk' ] self.etag_ignore_fields = ['session_preferences', '_etag', 'invisible_stages'] self.datasource = { 'projection': {'password': 0}, 'default_sort': [('username', 1)], } self.mongo_indexes = { 'username_1': ([('username', 1)], {'unique': True}), 'first_name_1_last_name_-1': [('first_name', 1), ('last_name', -1)], } self.privileges = {'POST': 'users', 'DELETE': 'users', 'PATCH': 'users'} super().__init__(endpoint_name, app=app, service=service, endpoint_schema=endpoint_schema)
blogs_schema = { 'title': { 'type': 'string', 'required': True, }, 'description': { 'type': 'string' }, 'language': { 'type': 'string' }, 'settings': { 'type': 'dict' }, 'original_creator': Resource.rel('users', True), 'version_creator': Resource.rel('users', True), 'state': { 'type': 'string', 'allowed': ['open', 'closed'], 'default': 'open' } } class BlogsResource(Resource): schema = blogs_schema datasource = { 'default_sort': [('_updated', -1)] }
def __init__(self, endpoint_name, app, service, endpoint_schema=None): self.schema = { 'name': { 'type': 'string', 'required': True, 'nullable': False, 'empty': False, 'iunique': True }, 'source': required_string, 'feeding_service': { 'type': 'string', 'required': True, 'allowed': allowed_feeding_services }, 'feed_parser': { 'type': 'string', 'nullable': True, 'allowed': allowed_feed_parsers }, 'content_types': { 'type': 'list', 'default': content_type, 'allowed': content_type }, 'allow_remove_ingested': { 'type': 'boolean', 'default': False }, 'content_expiry': { 'type': 'integer', 'default': app.config['INGEST_EXPIRY_MINUTES'] }, 'config': { 'type': 'dict' }, 'ingested_count': { 'type': 'integer' }, 'accepted_count': { 'type': 'integer' }, 'tokens': { 'type': 'dict' }, 'is_closed': { 'type': 'boolean', 'default': False }, 'update_schedule': { 'type': 'dict', 'schema': { 'hours': {'type': 'integer'}, 'minutes': {'type': 'integer', 'default': 5}, 'seconds': {'type': 'integer'}, } }, 'idle_time': { 'type': 'dict', 'schema': { 'hours': {'type': 'integer'}, 'minutes': {'type': 'integer'}, } }, 'last_updated': {'type': 'datetime'}, 'last_ingested_id': {'type': 'string'}, # this id is ingest provider internal value 'last_item_update': {'type': 'datetime'}, 'rule_set': Resource.rel('rule_sets', nullable=True), 'routing_scheme': Resource.rel('routing_schemes', nullable=True), 'notifications': { 'type': 'dict', 'schema': { 'on_update': {'type': 'boolean', 'default': True}, 'on_close': {'type': 'boolean', 'default': True}, 'on_open': {'type': 'boolean', 'default': True}, 'on_error': {'type': 'boolean', 'default': True} } }, 'last_closed': { 'type': 'dict', 'schema': { 'closed_at': {'type': 'datetime'}, 'closed_by': Resource.rel('users', nullable=True), 'message': {'type': 'string'} } }, 'last_opened': { 'type': 'dict', 'schema': { 'opened_at': {'type': 'datetime'}, 'opened_by': Resource.rel('users', nullable=True) } }, 'critical_errors': { 'type': 'dict', 'valueschema': { 'type': 'boolean' } }, } self.item_methods = ['GET', 'PATCH', 'DELETE'] self.privileges = {'POST': 'ingest_providers', 'PATCH': 'ingest_providers', 'DELETE': 'ingest_providers'} self.etag_ignore_fields = ['last_updated', 'last_item_update', 'last_closed', 'last_opened'] super().__init__(endpoint_name, app, service, endpoint_schema=endpoint_schema)
}, 'ingest_id': { 'type': 'string', 'mapping': not_analyzed }, 'family_id': { 'type': 'string', 'mapping': not_analyzed }, 'related_to': { # this field keeps a reference to the related item from which metadata has been copied 'type': 'string', 'mapping': not_analyzed }, # Audit Information 'original_creator': Resource.rel('users'), 'version_creator': Resource.rel('users'), 'firstcreated': { 'type': 'datetime' }, 'versioncreated': { 'type': 'datetime' }, 'firstpublished': { 'type': 'datetime', 'required': False, 'nullable': True, }, # Ingest Details 'ingest_provider': Resource.rel('ingest_providers'),
'unique_name': { 'type': 'string', 'unique': True, 'mapping': not_analyzed }, 'parent_id': { 'type': 'string', 'unique': True, 'mapping': not_analyzed }, 'version': { 'type': 'integer' }, # Audit Information 'original_creator': Resource.rel('users'), 'version_creator': Resource.rel('users'), 'firstcreated': { 'type': 'datetime' }, 'versioncreated': { 'type': 'datetime' }, # Ingest Details 'ingest_provider': Resource.rel('ingest_providers'), 'source': { # The value is copied from the ingest_providers vocabulary 'type': 'string', 'mapping': not_analyzed }, 'original_source': { # This value is extracted from the ingest
from superdesk.services import BaseService from superdesk.notification import push_notification from superdesk.activity import add_activity, ACTIVITY_UPDATE from superdesk.metadata.item import FAMILY_ID from eve.utils import ParsedRequest class DeskTypes(SuperdeskBaseEnum): authoring = "authoring" production = "production" desks_schema = { "name": {"type": "string", "required": True, "nullable": False, "empty": False, "iunique": True}, "description": {"type": "string"}, "members": {"type": "list", "schema": {"type": "dict", "schema": {"user": Resource.rel("users", True)}}}, "incoming_stage": Resource.rel("stages", True), "working_stage": Resource.rel("stages", True), "content_expiry": {"type": "integer"}, "source": {"type": "string"}, "monitoring_settings": { "type": "list", "schema": { "type": "dict", "schema": { "_id": {"type": "string", "required": True}, "type": { "type": "string", "allowed": ["search", "stage", "scheduledDeskOutput", "deskOutput", "personal"], "required": True, },
from bson.objectid import ObjectId import flask import superdesk import eve.io.base blogs_schema = { 'guid': metadata_schema['guid'], 'title': metadata_schema['headline'], 'description': metadata_schema['description'], 'theme': { 'type': 'dict' }, 'picture_url': { 'type': 'string', }, 'picture': Resource.rel('upload', True), 'original_creator': metadata_schema['original_creator'], 'version_creator': metadata_schema['version_creator'], 'versioncreated': metadata_schema['versioncreated'], 'blog_status': { 'type': 'string', 'allowed': ['open', 'closed'], 'default': 'open' }, 'particular_type': { 'type': 'string', 'allowed': ['blog'], 'default': 'blog' }, 'members': { 'type': 'list',
import superdesk from superdesk.notification import push_notification from superdesk.activity import add_activity, ACTIVITY_UPDATE from superdesk.metadata.item import FAMILY_ID from eve.utils import ParsedRequest class DeskTypes(SuperdeskBaseEnum): authoring = "authoring" production = "production" desks_schema = { "name": {"type": "string", "required": True, "nullable": False, "empty": False, "iunique": True}, "description": {"type": "string"}, "members": {"type": "list", "schema": {"type": "dict", "schema": {"user": Resource.rel("users", True)}}}, "incoming_stage": Resource.rel("stages", True), "working_stage": Resource.rel("stages", True), "content_expiry": {"type": "integer"}, "source": {"type": "string"}, "monitoring_settings": { "type": "list", "schema": { "type": "dict", "schema": { "_id": {"type": "string", "required": True}, "type": {"type": "string", "allowed": ["search", "stage", "deskOutput", "personal"], "required": True}, "max_items": {"type": "integer", "required": True}, }, }, },