def collection_get(self): """Model ``GET`` endpoint: retrieve multiple records. :raises: :exc:`~pyramid:pyramid.httpexceptions.HTTPNotModified` if ``If-None-Match`` header is provided and collection not modified in the interim. :raises: :exc:`~pyramid:pyramid.httpexceptions.HTTPPreconditionFailed` if ``If-Match`` header is provided and collection modified in the iterim. :raises: :exc:`~pyramid:pyramid.httpexceptions.HTTPBadRequest` if filters or sorting are invalid. """ self._add_timestamp_header(self.request.response) self._add_cache_header(self.request.response) self._raise_304_if_not_modified() self._raise_412_if_modified() headers = self.request.response.headers filters = self._extract_filters() limit = self._extract_limit() sorting = self._extract_sorting(limit) partial_fields = self._extract_partial_fields() filter_fields = [f.field for f in filters] include_deleted = self.model.modified_field in filter_fields pagination_rules, offset = self._extract_pagination_rules_from_token( limit, sorting) records, total_records = self.model.get_records( filters=filters, sorting=sorting, limit=limit, pagination_rules=pagination_rules, include_deleted=include_deleted) offset = offset + len(records) next_page = None if limit and len(records) == limit and offset < total_records: lastrecord = records[-1] next_page = self._next_page_url(sorting, limit, lastrecord, offset) headers['Next-Page'] = encode_header(next_page) if partial_fields: records = [ dict_subset(record, partial_fields) for record in records ] # Bind metric about response size. logger.bind(nb_records=len(records), limit=limit) headers['Total-Records'] = encode_header('%s' % total_records) return self.postprocess(records)
def upload(self, files=None, params=[], headers={}, status=None, randomize=True, gzipped=False): files = files or self.default_files headers = headers or self.headers.copy() content_type, body = self.app.encode_multipart(params, files) headers['Content-Type'] = core_utils.encode_header(content_type) params = {} if not randomize: params['randomize'] = 'false' if gzipped: params['gzipped'] = 'true' if len(params) > 0: endpoint_url = build_url(self.endpoint_uri, **params) else: endpoint_url = self.endpoint_uri resp = self.app.post(endpoint_url, body, headers=headers, status=status) if 200 <= resp.status_code < 300: self._add_to_cleanup(resp.json) return resp
def collection_delete(self): """Model ``DELETE`` endpoint: delete multiple records. :raises: :exc:`~pyramid:pyramid.httpexceptions.HTTPPreconditionFailed` if ``If-Match`` header is provided and collection modified in the iterim. :raises: :exc:`~pyramid:pyramid.httpexceptions.HTTPBadRequest` if filters are invalid. """ self._raise_412_if_modified() filters = self._extract_filters() limit = self._extract_limit() sorting = self._extract_sorting(limit) pagination_rules, offset = self._extract_pagination_rules_from_token(limit, sorting) records, total_records = self.model.get_records(filters=filters, sorting=sorting, limit=limit, pagination_rules=pagination_rules) deleted = self.model.delete_records(filters=filters, sorting=sorting, limit=limit, pagination_rules=pagination_rules) if deleted: lastrecord = deleted[-1] # Get timestamp of the last deleted field timestamp = lastrecord[self.model.modified_field] self._add_timestamp_header(self.request.response, timestamp=timestamp) # Add pagination header offset = offset + len(deleted) if limit and len(deleted) == limit and offset < total_records: next_page = self._next_page_url(sorting, limit, lastrecord, offset) self.request.response.headers['Next-Page'] = encode_header(next_page) else: self._add_timestamp_header(self.request.response) headers = self.request.response.headers headers['Total-Records'] = encode_header('%s' % total_records) action = len(deleted) > 0 and ACTIONS.DELETE or ACTIONS.READ return self.postprocess(deleted, action=action, old=records)
def service_unavailable(response, request): if response.content_type != 'application/json': error_msg = ("Service temporary unavailable " "due to overloading or maintenance, please retry later.") response = http_error(response, errno=ERRORS.BACKEND, message=error_msg) retry_after = request.registry.settings['retry_after_seconds'] response.headers["Retry-After"] = encode_header('%s' % retry_after) return reapply_cors(request, response)
def _add_timestamp_header(self, response, timestamp=None): """Add current timestamp in response headers, when request comes in. """ if timestamp is None: timestamp = self.timestamp # Pyramid takes care of converting. response.last_modified = timestamp / 1000.0 # Return timestamp as ETag. response.headers['ETag'] = encode_header('"%s"' % timestamp)
def send_alert(request, message=None, url=None, code='soft-eol'): """Helper to add an Alert header to the response. :param code: The type of error 'soft-eol', 'hard-eol' :param message: The description message. :param url: The URL for more information, default to the documentation url. """ if url is None: url = request.registry.settings['project_docs'] request.response.headers['Alert'] = encode_header(json.dumps({ 'code': code, 'message': message, 'url': url }))
def test_returns_a_string_if_passed_unicode_with_encoding(self): entry = six.text_type('Rémy') value = encode_header(entry, 'latin-1') self.assertEqual(type(value), str)
def test_returns_a_string_if_passed_bytes_and_encoding(self): entry = 'Rémy'.encode('latin-1') value = encode_header(entry, 'latin-1') self.assertEqual(type(value), str)
def test_returns_a_string_if_passed_bytes(self): entry = 'Toto'.encode('utf-8') value = encode_header(entry) self.assertEqual(type(value), str)
def test_returns_a_string_if_passed_a_string(self): entry = str('Toto') value = encode_header(entry) self.assertEqual(entry, value) self.assertEqual(type(value), str)
def on_new_response(event): # Add backoff in response headers. backoff = config.registry.settings['backoff'] if backoff is not None: backoff = utils.encode_header('%s' % backoff) event.response.headers['Backoff'] = backoff