async def __call__(self): data = await self.request.json() if "@type" not in data or data["@type"] not in app_settings[ "container_types"]: raise HTTPNotFound( content={ "message": "can not create this type %s" % data["@type"] }) if "id" not in data: raise HTTPPreconditionFailed(content={"message": "We need an id"}) if not data.get("title"): data["title"] = data["id"] if "description" not in data: data["description"] = "" value = await self.context.async_contains(data["id"]) if value: # Already exist raise HTTPConflict( content={"message": "Container with id already exists"}) install_addons = data.pop("@addons", None) or [] for addon in install_addons: # validate addon list if addon not in app_settings["available_addons"]: return ErrorResponse( "RequiredParam", "Property '@addons' must refer to a valid addon", status=412, reason=error_reasons.INVALID_ID, ) owner_id = get_authenticated_user_id() container = await create_container(self.context, data.pop("id"), container_type=data.pop("@type"), owner_id=owner_id, **data) task_vars.container.set(container) annotations_container = get_adapter(container, IAnnotations) task_vars.registry.set( await annotations_container.async_get(REGISTRY_DATA_KEY)) for addon in install_addons: await addons.install(container, addon) resp = { "@type": container.type_name, "id": container.id, "title": data["title"] } headers = {"Location": posixpath.join(self.request.path, container.id)} return Response(content=resp, headers=headers)
async def __call__(self): data = await self.request.json() if '@type' not in data or data['@type'] not in app_settings[ 'container_types']: raise HTTPNotFound( content={ 'message': 'can not create this type %s' % data['@type'] }) if 'id' not in data: raise HTTPPreconditionFailed(content={'message': 'We need an id'}) if not data.get('title'): data['title'] = data['id'] if 'description' not in data: data['description'] = '' value = await self.context.async_contains(data['id']) if value: # Already exist raise HTTPConflict( content={'message': 'Container with id already exists'}) install_addons = data.pop('@addons', None) or [] for addon in install_addons: # validate addon list if addon not in app_settings['available_addons']: return ErrorResponse( 'RequiredParam', "Property '@addons' must refer to a valid addon", status=412, reason=error_reasons.INVALID_ID) owner_id = get_authenticated_user_id(self.request) container = await create_container(self.context, data.pop('id'), container_type=data.pop('@type'), owner_id=owner_id, **data) self.request._container_id = container.__name__ self.request.container = container annotations_container = get_adapter(container, IAnnotations) self.request.container_settings = await annotations_container.async_get( REGISTRY_DATA_KEY) for addon in install_addons: await addons.install(container, addon) resp = { '@type': container.type_name, 'id': container.id, 'title': data['title'] } headers = {'Location': posixpath.join(self.request.path, container.id)} return Response(content=resp, headers=headers)
async def __call__(self): data = await self.request.json() if '@type' not in data or data['@type'] not in app_settings[ 'container_types']: raise HTTPNotFound( content={ 'message': 'can not create this type %s' % data['@type'] }) if 'id' not in data: raise HTTPPreconditionFailed(content={'message': 'We need an id'}) if not data.get('title'): data['title'] = data['id'] if 'description' not in data: data['description'] = '' value = await self.context.async_contains(data['id']) if value: # Already exist raise HTTPConflict( content={'message': 'Container with id already exists'}) container = await create_content(data['@type'], id=data['id'], title=data['title'], description=data['description']) # Special case we don't want the parent pointer container.__name__ = data['id'] await self.context.async_set(data['id'], container) await container.install() self.request._container_id = container.__name__ self.request.container = container user = get_authenticated_user_id(self.request) # Local Roles assign owner as the creator user roleperm = IPrincipalRoleManager(container) roleperm.assign_role_to_principal('guillotina.Owner', user) await notify( ObjectAddedEvent(container, self.context, container.__name__, payload=data)) resp = { '@type': data['@type'], 'id': data['id'], 'title': data['title'] } headers = {'Location': posixpath.join(self.request.path, data['id'])} return Response(content=resp, headers=headers)
async def tus_patch(self, *args, **kwargs): await self.dm.load() to_upload = None if "CONTENT-LENGTH" in self.request.headers: # header is optional, we'll be okay with unknown lengths... to_upload = int(self.request.headers["CONTENT-LENGTH"]) if "UPLOAD-LENGTH" in self.request.headers: if self.dm.get("deferred_length"): size = int(self.request.headers["UPLOAD-LENGTH"]) await self.dm.update(size=size) if "UPLOAD-OFFSET" in self.request.headers: offset = int(self.request.headers["UPLOAD-OFFSET"]) else: raise HTTPPreconditionFailed( content={"reason": "No upload-offset header"}) ob_offset = self.dm.get("offset") if offset != ob_offset: raise HTTPConflict( content={ "reason": f"Current upload offset({offset}) does not match " f"object offset {ob_offset}" }) read_bytes = await self.file_storage_manager.append( self.dm, self._iterate_request_data(), offset) if to_upload and read_bytes != to_upload: # pragma: no cover # check length matches if provided raise HTTPPreconditionFailed( content={ "reason": "Upload size does not match what was provided" }) await self.dm.update(offset=offset + read_bytes) headers = { "Upload-Offset": str(self.dm.get_offset()), "Tus-Resumable": "1.0.0", "Access-Control-Expose-Headers": ",".join(["Upload-Offset", "Tus-Resumable", "Tus-Upload-Finished"]), } if self.dm.get("size") is not None and self.dm.get_offset( ) >= self.dm.get("size"): await self.file_storage_manager.finish(self.dm) await self.dm.finish() headers["Tus-Upload-Finished"] = "1" else: await self.dm.save() return Response(headers=headers)
async def tus_patch(self, *args, **kwargs): await self.dm.load() to_upload = None if 'CONTENT-LENGTH' in self.request.headers: # header is optional, we'll be okay with unknown lengths... to_upload = int(self.request.headers['CONTENT-LENGTH']) if 'UPLOAD-LENGTH' in self.request.headers: if self.dm.get('deferred_length'): size = int(self.request.headers['UPLOAD-LENGTH']) await self.dm.update(size=size) if 'UPLOAD-OFFSET' in self.request.headers: offset = int(self.request.headers['UPLOAD-OFFSET']) else: raise HTTPPreconditionFailed( content={'reason': 'No upload-offset header'}) ob_offset = self.dm.get('offset') if offset != ob_offset: raise HTTPConflict( content={ 'reason': f'Current upload offset({offset}) does not match ' f'object offset {ob_offset}' }) read_bytes = await self.file_storage_manager.append( self.dm, self._iterate_request_data(), offset) if to_upload and read_bytes != to_upload: # check length matches if provided raise HTTPPreconditionFailed( content={ 'reason': 'Upload size does not match what was provided' }) await self.dm.update(offset=offset + read_bytes) headers = { 'Upload-Offset': str(self.dm.get_offset()), 'Tus-Resumable': '1.0.0', 'Access-Control-Expose-Headers': ','.join(['Upload-Offset', 'Tus-Resumable', 'Tus-Upload-Finished']) } if self.dm.get('size') is not None and self.dm.get_offset( ) >= self.dm.get('size'): await self.file_storage_manager.finish(self.dm) await self.dm.finish() headers['Tus-Upload-Finished'] = '1' else: await self.dm.save() return Response(headers=headers)
async def post_user(context, request): data = await request.json() try: jsonschema.validate( data, app_settings['json_schema_definitions']['HydraUser']) except (jsonschema.ValidationError, jsonschema.SchemaError) as e: raise HTTPPreconditionFailed(content={ 'message': e.message }) try: data = await utils.create_user(**data) except asyncpg.exceptions.UniqueViolationError: raise HTTPConflict(content={ 'reason': 'user already exists' }) del data['password'] data['@id'] = str(request.url.with_path(f'/@users/{data["id"]}')) return data
async def _handle(self, request, retries=0): aiotask_context.set('request', request) try: return await super()._handle(request) except (ConflictError, TIDConflictError) as e: if app_settings.get('conflict_retry_attempts', 3) > retries: label = 'DB Conflict detected' if isinstance(e, TIDConflictError): label = 'TID Conflict Error detected' tid = getattr(getattr(request, '_txn', None), '_tid', 'not issued') logger.debug( f'{label}, retrying request, tid: {tid}, retries: {retries + 1})', exc_info=True) request._retry_attempt = retries + 1 request.clear_futures() return await self._handle(request, retries + 1) logger.error( 'Exhausted retry attempts for conflict error on tid: {}'.format( getattr(getattr(request, '_txn', None), '_tid', 'not issued') )) return HTTPConflict()
async def __call__(self): data = await self.request.json() if '@type' not in data or data['@type'] not in app_settings['container_types']: raise HTTPNotFound(content={ 'message': 'can not create this type %s' % data['@type'] }) if 'id' not in data: raise HTTPPreconditionFailed(content={ 'message': 'We need an id' }) if not data.get('title'): data['title'] = data['id'] if 'description' not in data: data['description'] = '' value = await self.context.async_contains(data['id']) if value: # Already exist raise HTTPConflict(content={ 'message': 'Container with id already exists' }) container = await create_content( data['@type'], id=data['id'], title=data['title'], description=data['description']) # Special case we don't want the parent pointer container.__name__ = data['id'] await self.context.async_set(data['id'], container) await container.install() self.request._container_id = container.__name__ self.request.container = container user = get_authenticated_user_id(self.request) # Local Roles assign owner as the creator user roleperm = IPrincipalRoleManager(container) roleperm.assign_role_to_principal('guillotina.Owner', user) annotations_container = get_adapter(container, IAnnotations) self.request.container_settings = await annotations_container.async_get(REGISTRY_DATA_KEY) for addon in data.get('@addons') or []: if addon not in app_settings['available_addons']: return ErrorResponse( 'RequiredParam', "Property '@addons' must refer to a valid addon", status=412, reason=error_reasons.INVALID_ID) await addons.install(container, addon) await notify(ObjectAddedEvent(container, self.context, container.__name__, payload=data)) resp = { '@type': data['@type'], 'id': data['id'], 'title': data['title'] } headers = { 'Location': posixpath.join(self.request.path, data['id']) } return Response(content=resp, headers=headers)
async def move( context: IResource, destination: Optional[Union[IResource, str]] = None, new_id: Optional[str] = None, check_permission: bool = True, ) -> None: if destination is None: destination_ob = context.__parent__ else: if isinstance(destination, str): destination_ob = None if destination.startswith("/"): container = task_vars.container.get() if container is not None: try: destination_ob = await navigate_to(container, destination) except KeyError: pass else: try: destination_ob = await get_object_by_uid(destination) except KeyError: pass else: destination_ob = destination if destination_ob is None: raise PreconditionFailed(context, "Could not find destination object") if destination_ob.__uuid__ == context.__uuid__: raise PreconditionFailed(context, "You can not move object to itself") if destination_ob.__uuid__ == context.__parent__.__uuid__ and new_id == context.id: raise PreconditionFailed(context, "Object already belongs to this parent with same id") txn = get_transaction() if txn is not None: cache_keys = txn._cache.get_cache_keys(context, "deleted") old_id = context.id if new_id is None: new_id = context.id else: id_checker = get_adapter(context, IIDChecker) if not isinstance(new_id, str) or not await id_checker(new_id, context.type_name): raise PreconditionFailed(new_id, "Invalid id") if check_permission: policy = get_security_policy() if not policy.check_permission("guillotina.AddContent", destination_ob): raise PreconditionFailed( context, "You do not have permission to add content to the destination object" ) if await destination_ob.async_contains(new_id): raise HTTPConflict(content={"reason": f'Destination already has object with the id "{new_id}"'}) original_parent = context.__parent__ await notify( BeforeObjectMovedEvent( context, original_parent, old_id, destination_ob, new_id, payload={"id": new_id, "destination": destination}, ) ) if new_id != old_id: context.id = context.__name__ = new_id context.__parent__ = destination_ob context.register() await notify( ObjectMovedEvent( context, original_parent, old_id, destination_ob, new_id, payload={"id": new_id, "destination": destination}, ) ) if txn is not None: cache_keys += txn._cache.get_cache_keys(context, "added") await txn._cache.delete_all(cache_keys)