def validate_metadata_encoding(request_body: bytes) -> None: """ Validate that the given application metadata can be base64 decoded. Args: request_body: The body of the request. Raises: Fail: Application metadata is given and it cannot be base64 decoded. """ if not request_body: return request_text = request_body.decode() request_json = json.loads(request_text) if 'application_metadata' not in request_json: return application_metadata = request_json.get('application_metadata') if application_metadata is None: return try: decode_base64(encoded_data=application_metadata) except binascii.Error as exc: raise Fail(status_code=HTTPStatus.UNPROCESSABLE_ENTITY) from exc
def validate_metadata_size(request_body: bytes) -> None: """ Validate that the given application metadata is a string or 1024 * 1024 bytes or fewer. Args: request_body: The body of the request. Raises: MetadataTooLarge: Application metadata is given and it is too large. """ if not request_body: return request_text = request_body.decode() request_json = json.loads(request_text) application_metadata = request_json.get('application_metadata') if application_metadata is None: return decoded = decode_base64(encoded_data=application_metadata) max_metadata_bytes = 1024 * 1024 - 1 if len(decoded) <= max_metadata_bytes: return raise MetadataTooLarge
def validate_image_size(request_body: bytes) -> None: """ Validate the file size of the image given to a VWS endpoint. Args: request_body: The body of the request. Raises: ImageTooLarge: The image is given and is not under a certain file size threshold. """ if not request_body: return request_text = request_body.decode() image = json.loads(request_text).get('image') if image is None: return decoded = decode_base64(encoded_data=image) if len(decoded) <= 2359293: return raise ImageTooLarge
def validate_image_color_space(request_body: bytes) -> None: """ Validate the color space of the image given to a VWS endpoint. Args: request_body: The body of the request. Raises: BadImage: The image is given and is not in either the RGB or greyscale color space. """ if not request_body: return request_text = request_body.decode() image = json.loads(request_text).get('image') if image is None: return decoded = decode_base64(encoded_data=image) image_file = io.BytesIO(decoded) pil_image = Image.open(image_file) if pil_image.mode in ('L', 'RGB'): return raise BadImage
def validate_image_format(request_body: bytes) -> None: """ Validate the format of the image given to a VWS endpoint. Args: request_body: The body of the request. Raises: BadImage: The image is given and is not either a PNG or a JPEG. """ if not request_body: return request_text = request_body.decode() image = json.loads(request_text).get('image') if image is None: return decoded = decode_base64(encoded_data=image) image_file = io.BytesIO(decoded) pil_image = Image.open(image_file) if pil_image.format in ('PNG', 'JPEG'): return raise BadImage
def validate_image_is_image(request_body: bytes) -> None: """ Validate that the given image data is actually an image file. Args: request_body: The body of the request. Raises: BadImage: Image data is given and it is not an image file. """ if not request_body: return request_text = request_body.decode() image = json.loads(request_text).get('image') if image is None: return decoded = decode_base64(encoded_data=image) image_file = io.BytesIO(decoded) try: Image.open(image_file) except OSError as exc: raise BadImage from exc
def validate_image_encoding(request_body: bytes) -> None: """ Validate that the given image data can be base64 decoded. Args: request_body: The body of the request. Raises: Fail: Image data is given and it cannot be base64 decoded. """ if not request_body: return request_text = request_body.decode() if 'image' not in json.loads(request_text): return image = json.loads(request_text).get('image') try: decode_base64(encoded_data=image) except binascii.Error as exc: raise Fail(status_code=HTTPStatus.UNPROCESSABLE_ENTITY) from exc
def get_query_match_response_text( request_headers: Dict[str, str], request_body: bytes, request_method: str, request_path: str, databases: Set[VuforiaDatabase], query_processes_deletion_seconds: Union[int, float], query_recognizes_deletion_seconds: Union[int, float], ) -> str: """ Args: request_path: The path of the request. request_headers: The headers sent with the request. request_body: The body of the request. request_method: The HTTP method of the request. databases: All Vuforia databases. query_recognizes_deletion_seconds: The number of seconds after a target has been deleted that the query endpoint will still recognize the target for. query_processes_deletion_seconds: The number of seconds after a target deletion is recognized that the query endpoint will return a 500 response on a match. Returns: The response text for a query endpoint request. Raises: MatchingTargetsWithProcessingStatus: There is at least one matching target which has the status 'processing'. ActiveMatchingTargetsDeleteProcessing: There is at least one active target which matches and was recently deleted. """ body_file = io.BytesIO(request_body) _, pdict = cgi.parse_header(request_headers['Content-Type']) parsed = cgi.parse_multipart( fp=body_file, pdict={ 'boundary': pdict['boundary'].encode(), }, ) [max_num_results] = parsed.get('max_num_results', ['1']) [include_target_data] = parsed.get('include_target_data', ['top']) include_target_data = include_target_data.lower() [image_bytes] = parsed['image'] assert isinstance(image_bytes, bytes) image = io.BytesIO(image_bytes) gmt = ZoneInfo('GMT') now = datetime.datetime.now(tz=gmt) processing_timedelta = datetime.timedelta( seconds=query_processes_deletion_seconds, ) recognition_timedelta = datetime.timedelta( seconds=query_recognizes_deletion_seconds, ) database = get_database_matching_client_keys( request_headers=request_headers, request_body=request_body, request_method=request_method, request_path=request_path, databases=databases, ) assert isinstance(database, VuforiaDatabase) matching_targets = [ target for target in database.targets if _images_match(image=target.image, another_image=image) ] not_deleted_matches = [ target for target in matching_targets if target.active_flag and not target.delete_date and target.status == TargetStatuses.SUCCESS.value ] deletion_not_recognized_matches = [ target for target in matching_targets if target.active_flag and target.delete_date and (now - target.delete_date) < recognition_timedelta ] matching_targets_with_processing_status = [ target for target in matching_targets if target.status == TargetStatuses.PROCESSING.value ] active_matching_targets_delete_processing = [ target for target in matching_targets if target.active_flag and target.delete_date and (now - target.delete_date) < (recognition_timedelta + processing_timedelta) and target not in deletion_not_recognized_matches ] if matching_targets_with_processing_status: raise MatchingTargetsWithProcessingStatus if active_matching_targets_delete_processing: raise ActiveMatchingTargetsDeleteProcessing matches = not_deleted_matches + deletion_not_recognized_matches results: List[Dict[str, Any]] = [] for target in matches: target_timestamp = target.last_modified_date.timestamp() if target.application_metadata is None: application_metadata = None else: application_metadata = base64.b64encode( decode_base64(encoded_data=target.application_metadata), ).decode('ascii') target_data = { 'target_timestamp': int(target_timestamp), 'name': target.name, 'application_metadata': application_metadata, } if include_target_data == 'all': result = { 'target_id': target.target_id, 'target_data': target_data, } elif include_target_data == 'top' and not results: result = { 'target_id': target.target_id, 'target_data': target_data, } else: result = { 'target_id': target.target_id, } results.append(result) results = results[: int(max_num_results)] body = { 'result_code': ResultCodes.SUCCESS.value, 'results': results, 'query_id': uuid.uuid4().hex, } value = json_dump(body) return value