async def handler(self, request): """Main handler function for aiohttp.""" if request.method in WRITING_VERBS: try: async with locked(self.resource): request._db_write_enabled = True txn = request.conn.transaction_manager.begin(request) # We try to avoid collisions on the same instance of # plone.server view_result = await self.view() if isinstance(view_result, ErrorResponse) or \ isinstance(view_result, UnauthorizedResponse): # If we don't throw an exception and return an specific # ErrorReponse just abort await abort(txn, request) else: await commit(txn, request) except Unauthorized as e: await abort(txn, request) view_result = generate_unauthorized_response(e, request) except ConflictError as e: view_result = generate_error_response( e, request, 'ConflictDB', 409) except Exception as e: await abort(txn, request) view_result = generate_error_response( e, request, 'ServiceError') else: try: view_result = await self.view() except Unauthorized as e: view_result = generate_unauthorized_response(e, request) except Exception as e: view_result = generate_error_response(e, request, 'ViewError') # If we want to close the connection after the request if SHARED_CONNECTION is False and hasattr(request, 'conn'): request.conn.close() futures_to_wait = request._futures.values() if futures_to_wait: await asyncio.gather(futures_to_wait) # Make sure its a Response object to send to renderer if not isinstance(view_result, Response): view_result = Response(view_result) elif view_result is None: # Always provide some response to work with view_result = Response({}) # Apply cors if its needed cors_headers = apply_cors(request) cors_headers.update(view_result.headers) view_result.headers = cors_headers return await self.rendered(view_result)
async def __call__(self): headers = {} allowed_headers = ['Content-Type' ] + app_settings['cors']['allow_headers'] headers['Access-Control-Allow-Headers'] = ','.join(allowed_headers) headers['Access-Control-Allow-Methods'] = ','.join( app_settings['cors']['allow_methods']) headers['Access-Control-Max-Age'] = str( app_settings['cors']['max_age']) headers['Access-Control-Allow-Origin'] = ','.join( app_settings['cors']['allow_origin']) headers['Access-Control-Allow-Credentials'] = 'True' headers['Access-Control-Expose-Headers'] = \ ', '.join(app_settings['cors']['allow_headers']) oauth_utility = getUtility(IOAuth) if 'client_id' in self.request.GET: client_id = self.request.GET['client_id'] else: client_id = oauth_utility._client_id if hasattr(self.request, '_site_id'): scope = self.request._site_id else: scope = self.request.GET['scope'] result = await oauth_utility.auth_code([scope], client_id) resp = {'auth_code': result} return Response(response=resp, headers=headers, status=200)
async def __call__(self): if self.key is _marker: # No option to write the root of registry return ErrorResponse('InvalidRequest', 'Needs the registry key') data = await self.request.json() if 'value' in data: value = data['value'] else: value = data assert '.' in self.key, 'Registry key must be dotted.iface.name.fieldname' # noqa iface, name = self.key.rsplit('.', 1) iface = resolve(iface) field = iface[name] try: new_value = getMultiAdapter((value, field), IJSONToValue) except ComponentLookupError: return ErrorResponse('DeserializationError', 'Cannot deserialize type {}'.format( str(self.field)), status=501) try: self.request.site_settings[self.key] = new_value except DeserializationError as e: return ErrorResponse('DeserializationError', str(e), exc=e, status=400) return Response(response={}, status=204)
async def tus_options(self): resp = Response(headers=multidict.MultiDict({ 'Tus-Resumable': '1.0.0', 'Tus-Version': '1.0.0', 'Tus-Max-Size': '1073741824', 'Tus-Extension': 'creation,expiration' })) return resp
async def __call__(self): """Apply CORS on the OPTIONS view.""" headers = await self.preflight() resp = await self.render() if isinstance(resp, Response): headers.update(resp.headers) resp.headers = headers return resp return Response(response=resp, headers=headers, status=200)
async def tus_create(self): # This only happens in tus-java-client, redirect this POST to a PATCH if self.request.headers.get('X-HTTP-Method-Override') == 'PATCH': return await self.tus_patch() file = self.field.get(self.context) if file is None: file = S3File(contentType=self.request.content_type) self.field.set(self.context, file) if 'CONTENT-LENGTH' in self.request.headers: file._current_upload = int(self.request.headers['CONTENT-LENGTH']) else: file._current_upload = 0 if 'UPLOAD-LENGTH' in self.request.headers: file._size = int(self.request.headers['UPLOAD-LENGTH']) else: raise AttributeError('We need upload-length header') if 'UPLOAD-MD5' in self.request.headers: file._md5hash = self.request.headers['UPLOAD-MD5'] if 'UPLOAD-EXTENSION' in self.request.headers: file._extension = self.request.headers['UPLOAD-EXTENSION'] if 'TUS-RESUMABLE' not in self.request.headers: raise AttributeError('Its a TUS needs a TUS version') if 'UPLOAD-METADATA' not in self.request.headers: file.filename = uuid.uuid4().hex else: filename = self.request.headers['UPLOAD-METADATA'] file.filename = base64.b64decode( filename.split()[1]).decode('utf-8') await file.initUpload(self.context) if file.size < MIN_UPLOAD_SIZE: file._one_tus_shoot = True else: file._one_tus_shoot = False # Location will need to be adapted on aiohttp 1.1.x resp = Response( headers=multidict.MultiDict({ 'Location': IAbsoluteURL(self.context, self.request)() + '/@tusupload/' + self.field.__name__, # noqa 'Tus-Resumable': '1.0.0', 'Access-Control-Expose-Headers': 'Location,Tus-Resumable' }), status=201) return resp
async def tus_head(self): file = self.field.get(self.context) if file is None: raise KeyError('No file on this context') head_response = { 'Upload-Offset': str(file.actualSize()), 'Tus-Resumable': '1.0.0', 'Access-Control-Expose-Headers': 'Upload-Offset,Upload-Length,Tus-Resumable' } if file.size: head_response['Upload-Length'] = str(file._size) resp = Response(headers=multidict.MultiDict(head_response)) return resp
async def __call__(self): user = get_authenticated_user(self.request) serializer = queryMultiAdapter((user, self.request), IResourceSerializeToJson) if serializer: data = serializer() else: data = {} data.update({ 'id': user.id, 'roles': user._roles, 'groups': getattr(user, '_groups', []) }) return Response(data)
async def tus_patch(self): file = self.field.get(self.context) if 'CONTENT-LENGTH' in self.request.headers: to_upload = int(self.request.headers['CONTENT-LENGTH']) else: raise AttributeError('No content-length header') if 'UPLOAD-OFFSET' in self.request.headers: file._current_upload = int(self.request.headers['UPLOAD-OFFSET']) else: raise AttributeError('No upload-offset header') try: data = await self.request.content.readexactly(to_upload) except asyncio.IncompleteReadError as e: data = e.partial if file.one_tus_shoot: # One time shoot if file._block > 1: raise AttributeError('You should push 5Mb blocks AWS') await file.oneShotUpload(self.context, data) expiration = datetime.now() + timedelta(days=365) else: count = 0 while data: resp = await file.appendData(data) count += 1 try: data = await self.request.content.readexactly(CHUNK_SIZE ) # noqa except asyncio.IncompleteReadError as e: data = e.partial expiration = file._resumable_uri_date + timedelta(days=7) if file._size <= file._current_upload: await file.finishUpload(self.context) resp = Response(headers=multidict.MultiDict({ 'Upload-Offset': str(file.actualSize()), 'Tus-Resumable': '1.0.0', 'Upload-Expires': expiration.isoformat(), 'Access-Control-Expose-Headers': 'Upload-Offset,Upload-Expires,Tus-Resumable' })) return resp
async def __call__(self): data = await self.request.json() if '@type' not in data and data['@type'] != 'Site': return ErrorResponse('NotAllowed', 'can not create this type %s' % data['@type'], status=401) if 'title' not in data and not data['title']: return ErrorResponse('NotAllowed', 'We need a title', status=401) if 'id' not in data: return ErrorResponse('NotAllowed', 'We need an id', status=401) if 'description' not in data: data['description'] = '' if data['id'] in self.context: # Already exist return ErrorResponse('NotAllowed', 'Duplicate id', status=401) site = create_content('Site', id=data['id'], title=data['title'], description=data['description']) # Special case we don't want the parent pointer site.__name__ = data['id'] self.context[data['id']] = site site.install() self.request._site_id = site.__name__ user = get_authenticated_user_id(self.request) # Local Roles assign owner as the creator user roleperm = IPrincipalRoleManager(site) roleperm.assignRoleToPrincipal('plone.Owner', user) await notify(ObjectFinallyCreatedEvent(site)) # await notify(ObjectAddedEvent(site, self.context, site.__name__)) resp = {'@type': 'Site', 'id': data['id'], 'title': data['title']} headers = {'Location': self.request.path + data['id']} return Response(response=resp, headers=headers)
async def __call__(self): data = await self.get_data() behaviors = data.get('@behaviors', None) for behavior in behaviors or (): self.context.add_behavior(behavior) deserializer = queryMultiAdapter((self.context, self.request), IResourceDeserializeFromJson) if deserializer is None: return ErrorResponse('DeserializationError', 'Cannot deserialize type {}'.format( self.context.portal_type), status=501) try: await deserializer(data) except DeserializationError as e: return ErrorResponse('DeserializationError', str(e), status=400) await notify(ObjectFinallyModifiedEvent(self.context)) return Response(response={}, status=204)
async def __call__(self): """ data input : { 'interface': 'INTERFACE' }""" if not hasattr(self.request, 'site_settings'): return ErrorResponse('BadRequest', _("Not in a site request")) data = await self.request.json() interface = data.get('interface', None) initial_values = data.get('initial_values', {}) if interface is None: return ErrorResponse('InvalidRequest', 'Non existent Interface') registry = self.request.site_settings iObject = import_class(interface) registry.register_interface(iObject) config = registry.for_interface(iObject) # Initialize values # If its defined on the zope.schema default will not be overwritten # you will need to PATCH for key, field in getFields(iObject).items(): if key in initial_values and not getattr(config, key, False): # We don't have a value setattr(config, key, initial_values[key]) return Response(response={}, status=201)
async def __call__(self): """To create a content.""" data = await self.get_data() type_ = data.get('@type', None) id_ = data.get('id', None) behaviors = data.get('@behaviors', None) if not type_: return ErrorResponse('RequiredParam', _("Property '@type' is required")) # Generate a temporary id if the id is not given if not id_: new_id = None else: new_id = id_ user = get_authenticated_user_id(self.request) # Create object try: obj = create_content_in_container(self.context, type_, new_id, id=new_id, creators=(user, ), contributors=(user, )) except PreconditionFailed as e: return ErrorResponse('PreconditionFailed', str(e), status=412) except ConflictIdOnContainer as e: return ErrorResponse('ConflictId', str(e), status=409) except ValueError as e: return ErrorResponse('CreatingObject', str(e), status=400) for behavior in behaviors or (): obj.add_behavior(behavior) # Update fields deserializer = queryMultiAdapter((obj, self.request), IResourceDeserializeFromJson) if deserializer is None: return ErrorResponse('DeserializationError', 'Cannot deserialize type {}'.format( obj.portal_type), status=501) try: await deserializer(data, validate_all=True) except DeserializationError as e: return ErrorResponse('DeserializationError', str(e), exc=e, status=400) # Local Roles assign owner as the creator user roleperm = IPrincipalRoleManager(obj) roleperm.assignRoleToPrincipal('plone.Owner', user) await notify(ObjectFinallyCreatedEvent(obj)) absolute_url = queryMultiAdapter((obj, self.request), IAbsoluteURL) headers = { 'Access-Control-Expose-Headers': 'Location', 'Location': absolute_url() } serializer = queryMultiAdapter((obj, self.request), IResourceSerializeToJson) return Response(response=serializer(), headers=headers, status=201)
async def __call__(self): return Response({'foo': 'bar'})
async def __call__(self): data = await self.request.json() mailer = queryUtility(IMailer) await mailer.send(**data) return Response(response={'messages_sent': 1}, status=200)
async def tus_patch(self): file = self.field.get(self.context) if 'CONTENT-LENGTH' in self.request.headers: to_upload = int(self.request.headers['CONTENT-LENGTH']) else: raise AttributeError('No content-length header') if 'UPLOAD-OFFSET' in self.request.headers: file._current_upload = int(self.request.headers['UPLOAD-OFFSET']) else: raise AttributeError('No upload-offset header') try: data = await self.request.content.readexactly(to_upload) except asyncio.IncompleteReadError as e: data = e.partial count = 0 while data: old_current_upload = file._current_upload resp = await file.appendData(data) # The amount of bytes that are readed if resp.status in [200, 201]: # If we finish the current upload is the size of the file readed_bytes = file._current_upload - old_current_upload else: # When it comes from gcloud the current_upload is one number less readed_bytes = file._current_upload - old_current_upload + 1 # Cut the data so there is only the needed data data = data[readed_bytes:] bytes_to_read = len(data) if resp.status in [200, 201]: # If we are finished lets close it await file.finishUpload(self.context) data = None if bytes_to_read == 0: # We could read all the info break if bytes_to_read < 262144: # There is no enough data to send to gcloud break if resp.status in [400]: # Some error break if resp.status == 308: # We continue resumable count = 0 try: data += await self.request.content.readexactly(bytes_to_read) # noqa except asyncio.IncompleteReadError as e: data += e.partial else: count += 1 if count > MAX_RETRIES: raise AttributeError('MAX retries error') expiration = file._resumable_uri_date + timedelta(days=7) resp = Response(headers=multidict.MultiDict({ 'Upload-Offset': str(file.actualSize()), 'Tus-Resumable': '1.0.0', 'Upload-Expires': expiration.isoformat(), 'Access-Control-Expose-Headers': 'Upload-Offset,Upload-Expires,Tus-Resumable' })) return resp