예제 #1
0
def get_job(job_id, user):
    job = model.Job.query.get(job_id)
    if not job:
        return problem(404, 'Not Found', f'Job {job_id} does not exist')

    if not user_is_admin(user) and job.launched_by != user:
        return problem(403, 'Forbidden',
                       f"You don't have permissions to view job {job_id}")

    try:
        tower_client = job.server.create_tower_client()

        if job.template.tower_template_is_workflow:
            tower_job_data = tower_client.workflow_job_get(job.tower_job_id)
        else:
            tower_job_data = tower_client.template_job_get(job.tower_job_id)

        return _tower_job(job, tower_job_data) | {'_href': _job_href(job)}

    except TowerError as e:
        logger.exception(f'Failed to get job data from Tower, {e}')
        return problem(e.response.status_code, 'Error',
                       f'Failed to get job {job_id} data from Tower')

    except Exception as e:
        logger.exception(e)
        return problem(500, 'Server Error', f'Unknown server error, {e}')
예제 #2
0
def get_credentials(account_id: str, role_name: str, user: str,
                    token_info: dict):
    current_span = extract_span_from_flask_request()

    uid = user
    realm = token_info['realm']

    if realm != '/employees':
        return connexion.problem(403, 'Forbidden',
                                 'You are not authorized to use this service')
    try:
        groups = get_groups(uid)
    except Exception as e:
        current_span.log_kv({'exception': str(e)})
        logger.exception('Failed to get groups for {}'.format(uid))
        return connexion.problem(500, 'Server Error',
                                 'Failed to get groups: {}'.format(e))

    allowed = False
    for account_role in map(map_group_to_account_role, groups):
        if role_name == account_role[
                'role_name'] and account_id == account_role['account_id']:
            allowed = True
            break
    if not allowed:
        current_span.set_tag('allowed', False)
        return connexion.problem(
            403, 'Forbidden',
            'Access to requested AWS account/role was denied')

    sts = boto3.client('sts')
    arn = ROLE_ARN.format(account_id=account_id, role_name=role_name)
    role_session_name = ROLE_SESSION_NAME_INVALID_CHARS.sub('', uid)

    try:
        role = sts.assume_role(RoleArn=arn, RoleSessionName=role_session_name)
    except Exception as e:
        error_message = 'Failed to assume role: {}'.format(e)
        current_span.log_kv({'exception': str(e), 'role_arn': arn})
        if 'AccessDenied' in error_message:
            # role might not exist in target account
            logger.error(error_message)
            return connexion.problem(403, 'AWS Error', error_message)
        else:
            # something else happened
            current_span.set_tag('error', True)
            logger.exception('Failed to assume role {}'.format(arn))
            return connexion.problem(500, 'AWS Error', error_message)

    credentials = role['Credentials']
    logger.info(
        'Handing out credentials for {account_id}/{role_name} to {uid}'.format(
            account_id=account_id, role_name=role_name, uid=uid))

    return {
        'access_key_id': credentials['AccessKeyId'],
        'secret_access_key': credentials['SecretAccessKey'],
        'session_token': credentials['SessionToken'],
        'expiration': credentials['Expiration']
    }
예제 #3
0
def post_items(body: dict):

    # connexion cannot validate this kind of entries
    try:
        bson.encode(body)
    except bson.InvalidDocument as e:
        return problem(status=422,
                       title="Unprocessable Entity",
                       detail=e.args[0])
    except OverflowError as e:
        return problem(status=422,
                       title="Unprocessable Entity",
                       detail=e.args[0])

    uuid = str(uuid4())
    ts = datetime.now().isoformat()
    current_app.config["store"].add(uuid, dict(id=uuid,
                                               timestamp=ts,
                                               item=body))
    return {
        "id": uuid,
        "timestamp": ts,
        "status": "success",
        "url": request.base_url + "/" + uuid,
        "debug": current_app.config["store"].list(),
    }
예제 #4
0
    def _validating_wrapper(param):
        req_errors = validate_request(param.get('body', {}))
        if req_errors:
            return problem(status=400,
                           title="The request could not be validated.",
                           detail="There is an error in your submitted data.",
                           ext={'errors': req_errors})

        response = func(param)
        if not hasattr(response, 'original_data'):
            return response

        # FIXME
        # We need to get the "original data" somewhere and are currently "piggy-backing"
        # it on the response instance. This is somewhat problematic because it's not
        # expected behaviour and not a valid interface of Response. Needs refactoring.
        errors = validate_response(response.original_data)
        if errors:
            # Hope we never get here in production.
            return problem(
                status=500,
                title="Server was about to send an invalid response.",
                detail="This is an error of the implementation.",
                ext={'errors': errors},
            )
        return response
예제 #5
0
def get_template(template_id):
    template = model.Template.query.get(template_id)
    if not template:
        return problem(404, 'Not Found',
                       f'Template {template_id} does not exist')

    try:
        tower_client = template.server.create_tower_client()

        if template.tower_template_is_workflow:
            tower_survey_data = tower_client.workflow_get_survey(
                template.tower_template_id)
        else:
            tower_survey_data = tower_client.template_get_survey(
                template.tower_template_id)

        return template.to_dict() | {
            'tower_survey': tower_survey_data,
            '_href': _template_href(template),
        }

    except TowerError as e:
        logger.exception(f'Failed to get template data from Tower, {e}')
        return problem(
            e.response.status_code, 'Error',
            f'Failed to get template {template_id} data from Tower')

    except Exception as e:
        logger.exception(e)
        return problem(500, 'Server Error', f'Unknown server error, {e}')
예제 #6
0
def add(product):
    with dbconn() as conn:
        cur = conn.cursor()

        cur.execute(
            '''SELECT * FROM zsm_data.product_group WHERE pg_slug = %s''',
            (product['product_group'], ))
        row = cur.fetchone()
        if not row:
            return connexion.problem(
                status=404,
                title='Product group not found',
                detail='Can not find product group: {}'.format(
                    product['product_group']))

        product_group = strip_column_prefix(row._asdict())

        try:
            cur.execute(
                '''INSERT INTO zsm_data.product (p_name, p_slug, p_product_group_id) VALUES (%s, %s, %s)''',
                (
                    product['name'],
                    slugger(product['name']),
                    product_group['id'],
                ))
            conn.commit()
        except IntegrityError:
            return connexion.problem(
                status=400,
                title='Product Already Exists',
                detail='Product with name: "{}" already exists!'.format(
                    product['name']))
        return NoContent, 201
def delete_member(username):  # noqa: E501
    """Delete member 

    This can only be done by the logged in member. 

    Attention! 
    All information, events and pictures associated with this member will be deleted!  # noqa: E501

    :param username: The name that needs to be deleted
    :type username: str

    :rtype: Success 
    """

    deleteMember = db_session.query(
        dbModels.Member).filter(dbModels.Member.username == username).first()

    if deleteMember is None:
        return connexion.problem(404, "Not Found", "Username not found.",
                                 "Lookup")

    db_session.delete(deleteMember)

    try:
        db_session.commit()
    except:
        db_session.rollback()
        return connexion.problem(500, "Internal Server Error", "Database",
                                 "Database")

    return {"success": True, "Description": "Member has been deleted."}
예제 #8
0
def set_acknowledgement_on_host(params):
    """Acknowledge problems on a specific host."""
    host_name = params['host_name']

    host = Query([Hosts.name, Hosts.state],
                 Hosts.name.equals(host_name)).first(sites.live())
    if host is None:
        return problem(
            status=404,
            title=f'Host {host_name} does not exist.',
            detail='It is not currently monitored.',
        )

    if host.state == 0:
        return problem(status=400,
                       title=f"Host {host_name} does not have a problem.",
                       detail="The state is UP.")

    acknowledge_host_problem(
        sites.live(),
        host_name,
        sticky=bool(params.get('sticky')),
        notify=bool(params.get('notify')),
        persistent=bool(params.get('persistent')),
        user=_user_id(),
        comment=params.get('comment', 'Acknowledged'),
    )
    return http.Response(status=204)
예제 #9
0
def post_jobs(body, user):
    print(body)

    quota = get_user(user)['quota']
    if quota['remaining'] - len(body['jobs']) < 0:
        max_jobs = quota['max_jobs_per_month']
        message = f'Your monthly quota is {max_jobs} jobs. You have {quota["remaining"]} jobs remaining.'
        return problem(400, 'Bad Request', message)

    try:
        validate_jobs(body['jobs'])
    except requests.HTTPError as e:
        print(f'WARN: CMR search failed: {e}')
    except GranuleValidationError as e:
        return problem(400, 'Bad Request', str(e))

    request_time = format_time(datetime.now(timezone.utc))
    table = DYNAMODB_RESOURCE.Table(environ['JOBS_TABLE_NAME'])

    for job in body['jobs']:
        job['job_id'] = str(uuid4())
        job['user_id'] = user
        job['status_code'] = 'PENDING'
        job['request_time'] = request_time
        if not body.get('validate_only'):
            job = convert_floats_to_decimals(job)
            table.put_item(Item=job)

    return body
예제 #10
0
 def wrapper(*args, **kwargs):
     # validate size
     if kwargs["size"] > app.config["PAPI_MAX_SIZE"]:
         return problem( 400, "Bad Request",
             "Value of parameter size= must not be greater than %d"
             % app.config["PAPI_MAX_SIZE"]
         )
     # validate value of sortBy    
     sort_by, _ = split_sortby(kwargs['sortBy'])
     if sort_by.lower() not in [v.lower() for v in ALLOWED_SORT_BY_VALUES]:
         return problem( 400, "Bad Request",
             "Value '{}' of parameter sortBy= is not allowed. Use one of these values: {}".format(
                 kwargs['sortBy'], ", ".join(ALLOWED_SORT_BY_VALUES)))
     # validate filter names (depending on compliance level)
     non_filters = ('size', 'page', 'sortBy', 'body')
     if app.config['PAPI_COMPLIANCE_LEVEL'] == 0:
         for kw in kwargs:
             if kw not in non_filters and not kw.lower() in ALLOWED_FILTERS_CL0:
                 return problem(400, "Bad Request", "'{}' is not a valid filter for compliance level 0".format(kw))
     else: # compliance level > 0
         allowed_filters = ALLOWED_FILTERS_CL0 + ALLOWED_EXTRA_FILTERS
         for kw in kwargs:
             if kw not in non_filters and not kw.lower() in allowed_filters:
                 return problem(400, "Bad Request", 
                     "'{}' is not a valid filter for compliance level {}".format(kw, app.config['PAPI_COMPLIANCE_LEVEL']))
     return func(*args, **kwargs)
예제 #11
0
def delete_region(keycloak: KeycloakClient, region_id, user):
    region = model.Region.query.get(region_id)
    if not region:
        return problem(404, 'Not Found', f'Region {region_id} does not exist')

    if not keycloak.user_check_role(user, ADMIN_ROLE):
        if not keycloak.user_check_group(user, region.owner_group):
            raise Forbidden("You don't have write access to this region.")

    q = model.RegionProduct.query.filter(
        model.RegionProduct.region_id == region.id, )
    if q.count() > 0:
        for relation in q.all():
            db.session.delete(relation)
        db.session.flush()

    db.session.delete(region)

    try:
        owner_group = keycloak.group_get(region.owner_group)
        keycloak.group_delete(owner_group['id'])
        logger.info(f'Deleted owners group {owner_group["id"]}')

    except KeycloakGetError as e:
        logger.exception(e)
        return problem_from_keycloak_error(e)
    except Exception as e:
        logger.exception(e)
        return problem(500, 'Unknown Error',
                       f'Failed to delete owner group in Keycloak, {e}')

    db.session.commit()
    logger.info(
        f'Region {region.name} (id {region.id}) deleted by user {user}')
예제 #12
0
def bouncer(endpoint, *args, **kwargs):
    """
    Checks if the user making request is in the predefined list of
    allowed users or match the defined username parttern.
    """
    config = Configuration()

    if not hasattr(connexion.request, "user"):
        logger.debug("User not found in the request", extra={"allowed_users": config.allowed_users})
        return connexion.problem(403, "Forbidden", "Anonymous access is not allowed " "in this endpoint")

    if config.allowed_users is not None:
        logger.debug(
            "Checking if user is allowed", extra={"user": connexion.request.user, "allowed_users": config.allowed_users}
        )
        if connexion.request.user not in config.allowed_users:
            return connexion.problem(403, "Forbidden", "User is not allowed to access " "this endpoint")

    if config.allowed_user_pattern is not None:
        logger.debug(
            "Checking if user pattern is allowed",
            extra={"user": connexion.request.user, "allowed_user_pattern": config.allowed_user_pattern},
        )
        if re.match(config.allowed_user_pattern, connexion.request.user) is None:
            return connexion.problem(403, "Forbidden", "User is not allowed to access " "this endpoint")

    return endpoint(*args, **kwargs)
예제 #13
0
def create_source(body):
    # TODO: metadata enrichment if not set?
    if app.config["PAPI_COMPLIANCE_LEVEL"] < 2:
        return problem(
            501,
            "Not implemented",
            "Compliance level {} does not allow PUT requests.".format(
                app.config["PAPI_COMPLIANCE_LEVEL"]
            ),
        )
    connector = get_connector()
    # it seems that connexion converts '@id' to 'id'??
    # to make thing consistent for further processing, we revert this
    body = fix_ids(body)
    #if not '@id' in body and 'id' in body:
    #    body['@id'] = body['id']

    # This should be handled by spec!
    #if '@id' in body and not is_valid_id(body['@id']):
    #    return problem(400, "Bad Request", 'Illegal character in id')

    try:
        data = connector.create(body)
        return data
    except CreationException as err:
        return problem( 409, "Conflict", str(err))
예제 #14
0
        def wrapped(*args, **kwargs):
            api_key = request.headers.get("X-Phabricator-API-Key")

            if api_key is None and not self.optional:
                return problem(
                    401,
                    "X-Phabricator-API-Key Required",
                    ("Phabricator api key not provided in "
                     "X-Phabricator-API-Key header"),
                    type=
                    "https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/401",
                )

            g.phabricator = PhabricatorClient(
                current_app.config["PHABRICATOR_URL"],
                api_key
                or current_app.config["PHABRICATOR_UNPRIVILEGED_API_KEY"],
            )
            if api_key is not None and not g.phabricator.verify_api_token():
                return problem(
                    403,
                    "X-Phabricator-API-Key Invalid",
                    "Phabricator api key is not valid",
                    type=
                    "https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/403",
                )

            return f(*args, **kwargs)
예제 #15
0
def _SubscriptionConfirmation(  snsMessage):
	#logger.debug( snsMessage)

	if not 'SubscribeURL' in snsMessage:
			return connexion.problem( 400, "Subscription error", "No subscription URL was provided")
	
	subUrl = snsMessage[ 'SubscribeURL']

	'''
	Check subUrl is an URL
	^http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+
	Long live regex101!!! :)
	'''
	if not re.match( '^http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+', subUrl):
		logger.error ( 'SubscribeURL is not an URL')
		return connexion.problem( 400, "Subscription error", "'%s' is not a valid SubscribeURL" % subUrl)

	try:
		r = requests.get( subUrl)
	except:
		logger.error( 'Unable to fetch: ' + subUrl)
		return connexion.problem( 404, "Subscription error", "Unable to fetch: '%s'" % subUrl)

	logger.debug( 'And got... %s' % r)
	return connexion.problem( 200, "Subscription success", "Subscription done.")
예제 #16
0
def get_consent(taxCode, callback_url, consent, accept=False):
    user_data = _get_user_data(taxCode)
    if not user_data:
        return problem(title="User Not Found",
                       status=404,
                       detail="Missing user")

    if not accept:
        token = validate_token(consent,
                               valid=False,
                               audience=app.config["entityId"])
        return problem(
            title="OK",
            status=200,
            detail="Do you want to give access to %r for the following jwt" %
            token["iss"],
            ext={
                "_link": [{
                    "url": request.url + "&accept=yes"
                }],
                "callback_url": callback_url,
                "token": token,
            },
        )

    user_data["consent"] = time()

    return problem(
        title="Redirecting to consent",
        status=302,
        detail="Redirecto to POST",
        headers={"Location": callback_url},
    )
    raise NotImplementedError
예제 #17
0
def post(h5p_data: dict):
    """ The POST method for the H5P REST API. It offers client-side H5P exercises for download as ZIP archives. """
    h5p_form: H5PForm = H5PForm.from_dict(h5p_data)
    language: Language = determine_language(h5p_form.lang)
    exercise: Exercise = DatabaseService.query(
        Exercise, filter_by=dict(eid=h5p_form.eid), first=True)
    if not exercise:
        return connexion.problem(404, Config.ERROR_TITLE_NOT_FOUND,
                                 Config.ERROR_MESSAGE_EXERCISE_NOT_FOUND)
    text_field_content: str = get_text_field_content(exercise,
                                                     h5p_form.solution_indices)
    if not text_field_content:
        return connexion.problem(422, Config.ERROR_TITLE_UNPROCESSABLE_ENTITY,
                                 Config.ERROR_MESSAGE_UNPROCESSABLE_ENTITY)
    response_dict: dict = TextService.json_template_mark_words
    response_dict = get_response(response_dict, language,
                                 TextService.json_template_drag_text, exercise,
                                 text_field_content,
                                 TextService.feedback_template)
    file_name_no_ext: str = str(h5p_form.exercise_type_path)
    file_name: str = f"{file_name_no_ext}.{FileType.ZIP}"
    target_dir: str = Config.TMP_DIRECTORY
    make_h5p_archive(file_name_no_ext, response_dict, target_dir, file_name)
    return send_from_directory(target_dir,
                               file_name,
                               mimetype=MimeType.zip.value,
                               as_attachment=True)
예제 #18
0
def land(data, api_key=None):
    """ API endpoint at /revisions/{id}/transplants to land revision. """
    # get revision_id from body
    revision_id = data['revision_id']
    try:
        landing = Landing.create(revision_id, api_key)
    except RevisionNotFoundException:
        # We could not find a matching revision.
        return problem(
            404,
            'Revision not found',
            'The requested revision does not exist',
            type='https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/404'
        )
    except LandingNotCreatedException:
        # We could not find a matching revision.
        return problem(
            502,
            'Landing not created',
            'The requested revision does exist, but landing failed.'
            'Please retry your request at a later time.',
            type='https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/502'
        )

    return {'id': landing.id}, 202
예제 #19
0
def add_region_product(keycloak: KeycloakClient, region_id, body, user):
    region = model.Region.query.get(region_id)
    if not region:
        return problem(404, 'Not Found', f'Region {region_id} does not exist')

    if not keycloak.user_check_role(user, ADMIN_ROLE):
        if not keycloak.user_check_group(user, region.owner_group):
            raise Forbidden("You don't have write access to this region.")

    product = model.Product.query.get(body['id'])
    if not product:
        return problem(404, 'Not Found',
                       f'Product {body["id"]} does not exist')

    q = model.RegionProduct.query.filter(
        sqlalchemy.and_(
            model.RegionProduct.region_id == region.id,
            model.RegionProduct.product_id == product.id,
        ))
    if q.count() == 0:
        relation = model.RegionProduct(
            region_id=region.id,
            product_id=product.id,
            enabled=body.get('enabled', True),
        )
        db.session.add(relation)
        db.session.commit()
        logger.info(
            f'Added Product {product.name} (id {product.id}) to Region {region.name} '
            f'(id {region.id}) by user {user}')
    elif 'enabled' in body:
        for relation in q.all():
            relation.enabled = body['enabled']
        db.session.commit()
예제 #20
0
def get_status():
    """Ritorna lo stato dell'applicazione.

    Ritorna lo stato dell'applicazione.  # noqa: E501


    :rtype: Problem
    """
    @after_this_request
    def cache_no_store(response):
        """Add the 'no-store' cache value to avoid clients and
        intermediaries to store this response.
        """
        response.headers["Cache-Control"] = "no-store"
        return response

    if current_app.config["store"].ping():
        return problem(
            status=200,
            title="Success",
            detail="Il servizio funziona correttamente",
            ext={"result": "ok"},
        )

    return problem(
        status=503,
        title="Service Unavailable",
        detail="Questo errore viene ritornato randomicamente.",
        headers={"Retry-After": "1"},
    )
예제 #21
0
def common_error_handler(exception: BaseException) -> flask.Response:
    """Used to capture connexion exceptions and add link to the type field."""
    if isinstance(exception, ProblemException):

        link = EXCEPTIONS_LINK_MAP.get(exception.status)
        if link:
            response = problem(
                status=exception.status,
                title=exception.title,
                detail=exception.detail,
                type=link,
                instance=exception.instance,
                headers=exception.headers,
                ext=exception.ext,
            )
        else:
            response = problem(
                status=exception.status,
                title=exception.title,
                detail=exception.detail,
                type=exception.type,
                instance=exception.instance,
                headers=exception.headers,
                ext=exception.ext,
            )
    else:
        if not isinstance(exception, werkzeug.exceptions.HTTPException):
            exception = werkzeug.exceptions.InternalServerError()

        response = problem(title=exception.name,
                           detail=exception.description,
                           status=exception.code)

    return FlaskApi.get_response(response)
예제 #22
0
def update_region(keycloak: KeycloakClient, vault: Vault, region_id, body,
                  user):
    region = model.Region.query.get(region_id)
    if not region:
        return problem(404, 'Not Found', f'Region {region_id} does not exist')

    if not keycloak.user_check_role(user, ADMIN_ROLE):
        if not keycloak.user_check_group(user, region.owner_group):
            raise Forbidden("You don't have write access to this region.")

    try:
        if body.get('users_group'):
            keycloak.group_get(body['users_group'])
    except KeycloakGetError as e:
        logger.exception(e)
        return problem(
            400, 'Users group does not exist',
            f'Users group {body["users_group"]} does not exist in Keycloak, '
            'you have to create group first or use existing group.')

    if 'quota' in body:
        if body['quota']:
            if region.quota is None:
                region.quota = model.Quota(**body['quota'])
            else:
                for k, v in body['quota'].items():
                    setattr(region.quota, k, v)
        else:
            region.quota = None
        del body['quota']

    openstack_credentials = dpath.get(body,
                                      'openstack/credentials',
                                      default=region.openstack_credentials)
    if not isinstance(openstack_credentials, str):
        vault.write(region.openstack_credentials, openstack_credentials)
        dpath.delete(body, 'openstack/credentials')

    satellite_credentials = dpath.get(body,
                                      'satellite/credentials',
                                      default=region.satellite_credentials)
    if not isinstance(satellite_credentials, str):
        vault.write(region.satellite_credentials, satellite_credentials)
        dpath.delete(body, 'satellite/credentials')

    dns_server_key = dpath.get(body,
                               'dns_server/key',
                               default=region.dns_server_key)
    if not isinstance(dns_server_key, str):
        vault.write(region.dns_server_key, dns_server_key)
        dpath.delete(body, 'dns_server/key')

    region.update_from_dict(body)

    db.session.commit()
    logger.info(
        f'Region {region.name} (id {region.id}) updated by user {user}')

    return region.to_dict() | {'_href': _region_href(region)}
예제 #23
0
def images(taxon=None, service=None, limit=None, offset=None):
    """Specimen link retrieval."""
    from ..elc import subreq

    desc_obj = dict()
    t0 = time()
    sub_query = 'Specimen images for {0:s} retrieved through {1:s}'.format(
        taxon.capitalize(), 'iDigBio' if service == 'idigbio' else 'ePANDDA')

    try:
        options = params.set_options(req_args=connexion.request.args,
                                     endpoint='images')

    except ValueError as err:
        return connexion.problem(status=err.args[0],
                                 title=Status(err.args[0]).name,
                                 detail=err.args[1],
                                 type='about:blank')

    # Call parse function to check for parameter errors

    try:
        payload = params.parse(req_args=connexion.request.args,
                               options=options,
                               db='pbdb',
                               endpoint='images')

    except ValueError as err:
        return connexion.problem(status=err.args[0],
                                 title=Status(err.args[0]).name,
                                 detail=err.args[1],
                                 type='about:blank')

    # Query media aggregators

    try:
        media = subreq.images_req(payload=payload,
                                  options=options)

    except ValueError as err:
        return connexion.problem(status=err.args[0],
                                 title=Status(err.args[0]).name,
                                 detail=err.args[1],
                                 type='about:blank')

    # Build returned metadata object

    desc_obj.update(aux.build_meta(options))

    desc_obj.update(aux.build_meta_sub(source=sub_query,
                                       t0=t0,
                                       sub_tag='media_links',
                                       options=options,
                                       data=media))

    # Return data structure to client

    return jsonify(metadata=desc_obj, records=media)
예제 #24
0
def mobile(taxon=None, bbox=None):
    """Lightweight custom response."""
    from ..elc import config, subreq

    return_obj = list()

    # Set runtime options

    try:
        options = params.set_options(req_args=connexion.request.args,
                                     endpoint='occ')

    except ValueError as err:
        return connexion.problem(status=err.args[0],
                                 title=Status(err.args[0]).name,
                                 detail=err.args[1],
                                 type='about:blank')

    # This query only applies to Neotoma and PBDB

    for db in ['neotoma', 'pbdb']:

        # Configure parameter payload for api subquery

        try:
            payload = params.parse(req_args=connexion.request.args,
                                   options=options,
                                   db=db,
                                   endpoint='occ')

        except ValueError as err:
            return connexion.problem(status=err.args[0],
                                     title=Status(err.args[0]).name,
                                     detail=err.args[1],
                                     type='about:blank')

        # Database API call

        url_path = ''.join([config.get('resource_api', db),
                            config.get('db_occ_endpt', db)])

        try:
            return_obj = subreq.mobile_req(return_obj=return_obj,
                                           url_path=url_path,
                                           payload=payload,
                                           db=db)

        except ValueError as err:
            return connexion.problem(status=err.args[0],
                                     title=Status(err.args[0]).name,
                                     detail=err.args[1],
                                     type='about:blank')

    # Return composite data structure to client

    return jsonify(return_obj)
예제 #25
0
def land(data, api_key=None):
    """API endpoint at POST /landings to land revision."""
    # get revision_id from body
    revision_id = data['revision_id']
    diff_id = data['diff_id']
    logger.info(
        {
            'path': request.path,
            'method': request.method,
            'data': data,
            'msg': 'landing requested by user'
        }, 'landing.invoke')
    try:
        landing = Landing.create(revision_id, diff_id, api_key)
    except RevisionNotFoundException:
        # We could not find a matching revision.
        logger.info({
            'revision': revision_id,
            'msg': 'revision not found'
        }, 'landing.failure')
        return problem(
            404,
            'Revision not found',
            'The requested revision does not exist',
            type='https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/404'
        )
    except DiffNotFoundException:
        # We could not find a matching diff
        logger.info({
            'diff': diff_id,
            'msg': 'diff not found'
        }, 'landing.failure')
        return problem(
            404,
            'Diff not found',
            'The requested diff does not exist',
            type='https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/404'
        )
    except LandingNotCreatedException as exc:
        # We could not find a matching revision.
        logger.info(
            {
                'revision': revision_id,
                'exc': exc,
                'msg': 'error creating landing',
            }, 'landing.error')
        return problem(
            502,
            'Landing not created',
            'The requested revision does exist, but landing failed.'
            'Please retry your request at a later time.',
            type='https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/502'
        )

    return {'id': landing.id}, 202
예제 #26
0
def convert(amount, input_currency, output_currency=""):
    try:
        converter = Converter(amount, input_currency, output_currency)
        if converter.check_parameters():
            return converter.convert()
        else:
            return problem(400, "Bad Request", "Wrong parameters",
                           "about:blank")
    except FileNotFoundError:
        return problem(400, "Error", "Cannot find file with currency symbols.",
                       "about:blank")
예제 #27
0
def bulk_set_acknowledgement_on_host_service(params):
    """Bulk Acknowledge specific services on specific host"""
    live = sites.live()
    body = params['body']
    host_name = body['host_name']
    entries = body.get('entries', [])

    query = Query([Services.description, Services.state],
                  And(
                      Services.host_name.equals(host_name),
                      Or(*[
                          Services.description.equals(service_description)
                          for service_description in entries
                      ])))

    services = query.to_dict(live)

    not_found = []
    for service_description in entries:
        if service_description not in services:
            not_found.append(service_description)

    if not_found:
        return problem(
            status=400,
            title=
            f"Services {', '.join(not_found)} not found on host {host_name}",
            detail='Currently not monitored')

    up_services = []
    for service_description in entries:
        if services[service_description] == 0:
            up_services.append(service_description)

    if up_services:
        return problem(
            status=400,
            title=f"Services {', '.join(up_services)} do not have a problem",
            detail="The states of these services are OK")

    for service_description in entries:
        acknowledge_service_problem(
            sites.live(),
            host_name,
            service_description,
            sticky=body.get('sticky', False),
            notify=body.get('notify', False),
            persistent=body.get('persistent', False),
            user=str(config.user.id),
            comment=body.get('comment', 'Acknowledged'),
        )
    return http.Response(status=204)
예제 #28
0
def get(urn: str) -> Union[Response, ConnexionResponse]:
    """The GET method for the valid references REST API. It provides references for the desired text."""
    try:
        reff: List[str] = CustomCorpusService.get_custom_corpus_reff(
            urn) if CustomCorpusService.is_custom_corpus_urn(
                urn) else CorpusService.get_standard_corpus_reff(urn)
    except ValueError:
        return connexion.problem(400, Config.ERROR_TITLE_BAD_REQUEST,
                                 Config.ERROR_MESSAGE_BAD_REQUEST)
    if not reff:
        return connexion.problem(404, Config.ERROR_TITLE_NOT_FOUND,
                                 Config.ERROR_MESSAGE_CORPUS_NOT_FOUND)
    return NetworkService.make_json_response(reff)
예제 #29
0
 def _execute(name=None):
     try:
         return func(name), httplib.OK
     except error.NotFoundError:
         return connexion.problem(
             httplib.NOT_FOUND, '{} not found'.format(resource),
             'Requested {} `{}` not found.'.format(
                 resource.lower(), name))
     except error.ToBeDoneError:
         return connexion.problem(
             httplib.NOT_IMPLEMENTED,
             '{} handler not implemented'.format(operation),
             'Requested operation `{}` on {} not implemented.'.format(
                 operation.lower(), resource.lower()))
예제 #30
0
def relaunch_job(job_id, user):
    job = model.Job.query.get(job_id)
    if not job:
        return problem(404, 'Not Found', f'Job {job_id} does not exist')

    if not user_is_admin(user) and job.launched_by != user:
        return problem(403, 'Forbidden',
                       f"You don't have permissions to relaunch job {job_id}")

    try:
        tower_client = job.server.create_tower_client()

        if job.template.tower_template_is_workflow:
            tower_job_data = tower_client.workflow_job_relaunch(
                job.tower_job_id)
        else:
            tower_job_data = tower_client.template_job_relaunch(
                job.tower_job_id)

        relaunched_job = model.Job(
            template_id=job.template_id,
            tower_job_id=tower_job_data['id'],
            launched_by=user,
        )
        db.session.add(relaunched_job)
        db.session.commit()

        return _tower_job(relaunched_job, tower_job_data) | {
            '_href': _job_href(job)
        }

    except TowerError as e:
        logger.exception(f'Failed to relaunch job, {e}')

        problem_ext = None
        try:
            problem_ext = e.response.json()
            if 'detail' in problem_ext:
                del problem_ext['detail']
        except Exception:
            pass  # simply ignore

        return problem(e.response.status_code,
                       'Error',
                       f'Failed to relaunch job {job_id}',
                       ext=problem_ext)

    except Exception as e:
        logger.exception(e)
        return problem(500, 'Server Error', f'Unknown server error, {e}')
예제 #31
0
def call_eureka():
    if not EUREKA_URL:
        return connexion.problem(500, "Internal Server error",
                                 "Eureka url not set", "errors.eureka")
    if not ExternalServices.eureka_on:
        ExternalServices.init_eureka()
        if not ExternalServices.eureka_on:
            return connexion.problem(
                500, "Internal Server error",
                "Unable to connect to eureka, the server might not be running",
                "errors.eureka")
        return "Connected to eureka", 200

    return "Already connected to eureka", 200
예제 #32
0
def get_attribute_consent(taxCode):
    """Receives a token and asks for permission if
       required.
    Return
    :param taxCode:
    :return:
    """
    content_type = request.headers.get("Content-Type", "")
    assert content_type.lower() == "application/jose"

    user_data = _get_user_data(taxCode)
    if not user_data:
        return problem(title="User Not Found",
                       status=404,
                       detail="Missing user")

    try:
        claims = validate_token(request.data, audience=app.config["entityId"])
    except jwt.exceptions.InvalidTokenError as e:
        return invalid_token_handler(e)
    except Exception as e:
        raise ValueError(e, request.data)

    assert claims["v"] == "0.0.1", claims
    assert claims["attributes"], claims

    if user_data["consent"] + 30 < time():
        return problem(
            status=403,
            title="ConsentRequired",
            detail="User consent is required for this attribute. Last given: %r"
            % datetime.fromtimestamp(user_data["consent"]),
            type="https://spid.gov.it/aa/problem/consent-required",
            ext={"consent_url": "https://aa/v1/consents/{taxCode}/"},
        )

    aa = [{
        "attribute": a,
        "value": user_data[a]
    } for a in claims["attributes"] if a in user_data]

    token = create_token({"v": "0.0.1", "attributes": aa})
    token["iss"] = app.config["entityId"]
    token["aud"] = claims["iss"]

    signed_token = sign_request(token, app_config=app.config)

    return Response(response=signed_token,
                    status=200,
                    mimetype="application/jose")
예제 #33
0
파일: hello.py 프로젝트: fredstro/connexion
def with_problem():
    return problem(type='http://www.example.com/error',
                   title='Some Error',
                   detail='Something went wrong somewhere',
                   status=418,
                   instance='instance1',
                   headers={'x-Test-Header': 'In Test'})
예제 #34
0
def get_fragment_svg(fragment_id, width, height):
    """2D drawing of fragment in SVG format

    Args:
        fragment_id (str): Fragment identifier
        width (int): Width of SVG in pixels
        height (int): Height of SVG in pixels

    Returns:
        flask.Response|connexion.lifecycle.ConnexionResponse: SVG document|problem
    """
    fragments_db_filename = current_app.config['fragments']
    with FragmentsDb(fragments_db_filename) as fragmentsdb:
        try:
            fragment = fragmentsdb[fragment_id]
            if not fragment['mol']:
                title = 'Not Found'
                description = 'Fragment with identifier \'{0}\' has no molblock'.format(fragment_id)
                ext = {'identifier': fragment_id}
                return connexion.problem(404, title, description, ext=ext)
            mol = fragment['mol']
            svg = mol2svg(mol, width, height)
            return flask.Response(svg, mimetype='image/svg+xml')
        except LookupError:
            return fragment_not_found(fragment_id)
예제 #35
0
def post(emergency_shutoff, changed_by, transaction):
    if shutoff_exists(emergency_shutoff["product"], emergency_shutoff["channel"]):
        return problem(400, "Bad Request", "Invalid Emergency shutoff data", ext={"data": "Emergency shutoff for product/channel already exists."})
    inserted_shutoff = dbo.emergencyShutoffs.insert(
        changed_by=changed_by, transaction=transaction, product=emergency_shutoff["product"], channel=emergency_shutoff["channel"]
    )
    return Response(status=201, content_type="application/json", response=json.dumps(inserted_shutoff))
예제 #36
0
파일: hello.py 프로젝트: jkliff/connexion
def with_problem():
    return problem(
        type="http://www.example.com/error",
        title="Some Error",
        detail="Something went wrong somewhere",
        status=418,
        instance="instance1",
    )
예제 #37
0
def get_by_id(product, channel):
    shutoffs = dbo.emergencyShutoffs.select(where=dict(product=product, channel=channel))
    if not shutoffs:
        return problem(status=404, title="Not Found", detail="Requested emergency shutoff wasn't found", ext={"exception": "Requested shutoff does not exist"})

    headers = {"X-Data-Version": shutoffs[0]["data_version"]}

    return Response(response=json.dumps(shutoffs[0]), headers=headers, mimetype="application/json")
예제 #38
0
def schedule_deletion(sc_emergency_shutoff, changed_by, transaction):
    if "csrf_token" in sc_emergency_shutoff:
        del sc_emergency_shutoff["csrf_token"]
    change_type = sc_emergency_shutoff.get("change_type")
    if change_type != "delete":
        return problem(400, "Bad Request", "Invalid or missing change_type")

    view = ScheduledChangesView("emergency_shutoff", dbo.emergencyShutoffs)
    return view._post(sc_emergency_shutoff, transaction, changed_by, change_type)
예제 #39
0
def schedule_deletion(sc_emergency_shutoff, changed_by, transaction):
    if 'csrf_token' in sc_emergency_shutoff:
        del sc_emergency_shutoff['csrf_token']
    change_type = sc_emergency_shutoff.get('change_type')
    if change_type != 'delete':
        return problem(400, "Bad Request", "Invalid or missing change_type")

    view = ScheduledChangesView('emergency_shutoff', dbo.emergencyShutoffs)
    return view._post(sc_emergency_shutoff, transaction, changed_by, change_type)
예제 #40
0
파일: rules.py 프로젝트: mozbhearsum/balrog
def get_rule(id_or_alias, with_csrf_headers=False):
    rule = dbo.rules.getRule(id_or_alias)
    if not rule:
        return problem(status=404, title="Not Found", detail="Requested rule wasn't found", ext={"exception": "Requested rule does not exist"})

    headers = {"X-Data-Version": rule["data_version"]}
    if with_csrf_headers:
        headers.update(get_csrf_headers())
    return Response(response=json.dumps(rule), mimetype="application/json", headers=headers)
예제 #41
0
def post(emergency_shutoff, changed_by, transaction):
    if shutoff_exists(emergency_shutoff['product'], emergency_shutoff['channel']):
        return problem(
            400, 'Bad Request', 'Invalid Emergency shutoff data',
            ext={'data': 'Emergency shutoff for product/channel already exists.'})
    inserted_shutoff = dbo.emergencyShutoffs.insert(
        changed_by=changed_by, transaction=transaction, product=emergency_shutoff['product'], channel=emergency_shutoff['channel'])
    return Response(status=201,
                    content_type="application/json",
                    response=json.dumps(inserted_shutoff))
예제 #42
0
파일: api.py 프로젝트: zalando/lizzy
def exception_to_connexion_problem(func, *args, **kwargs):
    try:
        return func(*args, **kwargs)
    except ObjectNotFound as exception:
        problem = connexion.problem(404, 'Not Found',
                                    "Stack not found: {}".format(exception.uid),
                                    headers=_make_headers())
        metrics.count('generic.{}.stack_not_found'.format(func.__name__))
        return problem
    except ExecutionError as error:
        sentry_client.captureException()
        metrics.count('generic.{}.senza_cmd_error'.format(func.__name__))
        return connexion.problem(500,
                                 title='Execution Error',
                                 detail=error.output,
                                 headers=_make_headers())
    except Exception:
        sentry_client.captureException()
        raise
예제 #43
0
def get_release_single_locale(release, platform, locale, with_csrf_header=False):
    try:
        locale = dbo.releases.getLocale(release, platform, locale)
    except KeyError as e:
        return problem(404, "Not Found", json.dumps(e.args))
    data_version = dbo.releases.getReleases(name=release)[0]["data_version"]
    headers = {"X-Data-Version": data_version}
    if with_csrf_header:
        headers.update(get_csrf_headers())
    return Response(response=json.dumps(locale), mimetype="application/json", headers=headers)
예제 #44
0
def delete(product, channel, data_version, changed_by, transaction, **kwargs):
    if not shutoff_exists(product, channel):
        return problem(status=404,
                       title="Not Found",
                       detail="Shutoff wasn't found",
                       ext={"exception": "Shutoff does not exist"})
    where = dict(product=product, channel=channel)
    dbo.emergencyShutoffs.delete(where=where, changed_by=changed_by,
                                 old_data_version=data_version,
                                 transaction=transaction)
    return Response(status=200)
예제 #45
0
def get(animal_id):
    gc = global_cell.GlobalCell()
    q = {'animal_id': animal_id}
    res = [i['id'] for i in gc.find(q)]
    if not res:
        return connexion.problem(
            404,
            'not found',
            'no global cells found for {}'.format(animal_id),
            __name__.split('.')[-1]
        )
    return res
예제 #46
0
파일: releases.py 프로젝트: catlee/balrog
def get_release_history(release):
    history_table = dbo.releases.history
    order_by = [history_table.timestamp.desc()]
    history_helper = HistoryHelper(hist_table=history_table,
                                   order_by=order_by,
                                   get_object_callback=lambda: _get_release(release),
                                   history_filters_callback=_get_filters,
                                   process_revisions_callback=process_release_revisions)
    try:
        return history_helper.get_history()
    except (ValueError, AssertionError) as e:
        log.warning("Bad input: %s", json.dumps(e.args))
        return problem(400, "Bad Request", "Invalid input", ext={"data": e.args})
예제 #47
0
def get(cell_id):
    c = cell.Cell()
    q = {'id': cell_id}
    res = c.find_one(q)
    if not res:
        return connexion.problem(
            404,
            'not found',
            'id {} does not exist'.format(cell_id),
            __name__.split('.')[-1]
        )
    del res['_id']
    return res
예제 #48
0
def get(run_id):
    r = run.Run()
    q = {'id': run_id}
    res = r.find_one(q)
    if not res:
        return connexion.problem(
            404,
            'not found',
            'id {} does not exist'.format(run_id),
            __name__.split('.')[-1]
        )
    del res['_id']
    return res
def get(cell_metric_id):
    pcmi = per_cell_metric.PerCellMetricInfo()
    q = {'id': cell_metric_id}
    res = pcmi.find_one(q)
    if not res:
        return connexion.problem(
            404,
            'not found',
            'id {} does not exist'.format(cell_metric_id),
            __name__.split('.')[-1]
        )
    del res['_id']
    return res
예제 #50
0
def get(trial_id):
    t = trial.Trial()
    q = {'id': trial_id}
    res = t.find_one(q)
    if not res:
        return connexion.problem(
            404,
            'not found',
            'id {} does not exist'.format(trial_id),
            __name__.split('.')[-1]
        )
    del res['_id']
    return res
def get(trial_id):
    ret = raw_eye_tracking.RawEyeTracking()
    q = {'trial_id': trial_id}
    res = ret.find_one(q)
    if not res:
        return connexion.problem(
            404,
            'not found',
            'raw eye tracking data for trial {} not found'.format(trial_id),
            __name__.split('.')[-1]
        )
    if res:
        del res['_id']
    return res
예제 #52
0
파일: rules.py 프로젝트: catlee/balrog
def get_rule_history(rule_id):
    history_table = dbo.rules.history
    order_by = [history_table.timestamp.desc()]
    history_helper = HistoryHelper(hist_table=history_table,
                                   order_by=order_by,
                                   get_object_callback=lambda: dbo.rules.getRule(rule_id),
                                   history_filters_callback=_get_filters,
                                   obj_not_found_msg='Requested rule does not exist')
    try:
        return history_helper.get_history(response_key='rules')
    except (ValueError, AssertionError) as msg:
        log.warning("Bad input: %s", msg)
        return problem(400, "Bad Request", "Error occurred when trying to fetch"
                                           " Rule's revisions having rule_id {0}".format(rule_id),
                       ext={"exception": str(msg)})
예제 #53
0
def get(session_id):
    s = session.Session()
    q = {'id': session_id}
    res = s.find_one(q)
    if not res:
        return connexion.problem(
            404,
            'not found',
            'session id {} not found'.format(session_id),
            __name__.split('.')[-1]
        )
    res['animal'] = res['animal']['id']
    del res['_id']
    del res['runs']
    return res
예제 #54
0
def get_release(release, with_csrf_header=False):
    release = _get_release(release)
    if not release:
        return problem(404, "Not Found", "Release name: %s not found" % release)
    headers = {"X-Data-Version": release["data_version"]}
    if with_csrf_header:
        headers.update(get_csrf_headers())
    if request.args.get("pretty"):
        indent = 4
        separators = (",", ": ")
    else:
        indent = None
        separators = None
    # separators set manually due to https://bugs.python.org/issue16333 affecting Python 2
    return Response(response=json.dumps(release["data"], indent=indent, separators=separators, sort_keys=True), mimetype="application/json", headers=headers)
예제 #55
0
def _get_histories(table, obj, process_revisions_callback=None):
    history_table = table
    order_by = [history_table.timestamp.desc()]
    history_helper = HistoryHelper(hist_table=history_table,
                                   order_by=order_by,
                                   get_object_callback=lambda: obj,
                                   history_filters_callback=_get_filters,
                                   obj_not_found_msg='No history found',
                                   process_revisions_callback=process_revisions_callback)
    try:
        return history_helper.get_history()
    except (ValueError, AssertionError) as msg:
        log.warning("Bad input: %s", msg)
        return problem(400, "Bad Request", "Error occurred when trying to fetch histories",
                       ext={"exception": str(msg)})
def get(run_id, cell_id, channel_id=None, trial_id=None):
    ftc = functional_time_course.FunctionalTimeCourse()
    q = {'run_id': run_id, 'cell_id': cell_id}
    if trial_id:
        q['trial_id'] = trial_id
    res = ftc.find_one(q)
    if not res:
        return connexion.problem(
            404,
            'not found',
            'time course data not found',
            __name__.split('.')[-1]
        )
    if res:
        del res['_id']
    return res
예제 #57
0
def get_fragments(fragment_ids=None, pdb_codes=None):
    """Retrieve fragments based on their identifier or PDB code.

    Args:
        fragment_ids (List[str]): List of fragment identifiers
        pdb_codes (List[str]): List of PDB codes

    Returns:
        list[dict]: List of fragment information

    Raises:
        werkzeug.exceptions.NotFound: When one of the fragments_ids or pdb_code could not be found
    """
    fragments_db_filename = current_app.config['fragments']
    with FragmentsDb(fragments_db_filename) as fragmentsdb:
        fragments = []
        missing_ids = []
        if fragment_ids:
            for frag_id in fragment_ids:
                try:
                    fragments.append(fragmentsdb[frag_id])
                except LookupError:
                    missing_ids.append(frag_id)

        if pdb_codes:
            for pdb_code in pdb_codes:
                try:
                    for fragment in fragmentsdb.by_pdb_code(pdb_code):
                        fragments.append(fragment)
                except LookupError:
                    missing_ids.append(pdb_code)
        # TODO if fragment_ids and pdb_codes are both None then return paged list of all fragments
        if missing_ids:
            title = 'Not found'
            label = 'identifiers'
            if pdb_codes:
                label = 'PDB codes'
            description = 'Fragments with {1} \'{0}\' not found'.format(','.join(missing_ids), label)
            # connexion.problem is using json.dumps instead of flask custom json encoder, so performing convert myself
            # TODO remove mol2string conversion when https://github.com/zalando/connexion/issues/266 is fixed
            for fragment in fragments:
                if fragment['mol']:
                    fragment['mol'] = MolToMolBlock(fragment['mol'])
            ext = {'absent_identifiers': missing_ids, 'fragments': fragments}
            return connexion.problem(404, title, description, ext=ext)
        return fragments
예제 #58
0
def common_error_handler(exception):
    '''TODO: add description

    :param extension:  TODO
    :type exception: Exception

    :rtype: TODO:
    '''

    if not isinstance(exception, werkzeug.exceptions.HTTPException):
        exception = werkzeug.exceptions.InternalServerError()

    return connexion.problem(
        title=exception.name,
        detail=exception.description,
        status=exception.code,
    )
예제 #59
0
    def validate_schema(self, data, url):
        # type: (dict, AnyStr) -> Union[ConnexionResponse, None]
        if self.is_null_value_valid and is_null(data):
            return None
        try:
            self.validator.validate(data)
        except JsonSchemaValidationError as exception:
            # Add field name to the error response
            exception_field = ''
            for i in exception.path:
                exception_field = i + ': '
            if exception.__cause__ is not None:
                exception_message = str(exception.__cause__.message) + ' ' + exception_field + str(exception.message)
            else:
                exception_message = exception_field + str(exception.message)
            logger.error("{url} validation error: {error}".
                         format(url=url, error=exception_message))
            return problem(400, 'Bad Request', exception_message)

        return None
예제 #60
0
    def validate_schema(self, data, url):
        # type: (dict, AnyStr) -> Union[ConnexionResponse, None]
        if self.is_null_value_valid and is_null(data):
            return None
        try:
            self.validator.validate(data)
        except jsonschema.ValidationError as exception:
            # Add field name to the error response
            exception_field = ""
            for i in exception.path:
                exception_field = i + ": "
            if exception.__cause__ is not None:
                exception_message = exception.__cause__.message + " " + exception_field + exception.message
            else:
                exception_message = exception_field + exception.message
            # Some exceptions could contain unicode characters - if we don't replace them
            # we could end up with a UnicodeEncodeError.
            logger.error("{url} validation error: {error}".format(url=url, error=exception_message.encode("utf-8", "replace")))
            return problem(400, "Bad Request", exception_message)

        return None