コード例 #1
0
ファイル: test_api.py プロジェクト: Vioasselin/trefle
async def test_remuneration_endpoint(client):
    resp = await client.get("/schema")
    spec = create_spec(json.loads(resp.body))

    resp = await client.post(
        "/remuneration",
        body={
            "beneficiaire.age": 20,
            "formation.region": 27,
            "formation.codes_financeur": [2],
        },
    )
    assert resp.status == HTTPStatus.OK
    assert "remunerations" in json.loads(resp.body)
    remunerations = json.loads(resp.body)["remunerations"]
    assert remunerations
    print(remunerations[0])
    assert "remuneration" in remunerations[0]
    assert "Version" in resp.headers

    validator = ResponseValidator(spec)
    request = MockRequest("http://trefle.pole-emploi.fr", "post",
                          "/remuneration")
    response = MockResponse(resp.body, resp.status.value)
    result = validator.validate(request, response)
    result.raise_for_errors()
コード例 #2
0
ファイル: test_api.py プロジェクト: Vioasselin/trefle
async def test_simulate_endpoint(client):
    resp = await client.get("/schema")
    spec = create_spec(json.loads(resp.body))

    resp = await client.post(
        "/financement",
        body={
            "beneficiaire.solde_cpf": 10,
            "beneficiaire.remuneration": 1400,
            "beneficiaire.droit_prive": True,
            "beneficiaire.contrat": "cdi",
            "formation.eligible_cpf": True,
            "formation.heures": 100,
            "beneficiaire.entreprise.commune": "2A004",
            "beneficiaire.entreprise.idcc": 2706,
        },
    )
    assert resp.status == HTTPStatus.OK
    assert "financements" in json.loads(resp.body)
    financements = json.loads(resp.body)["financements"]
    print(financements[0])
    assert financements
    assert financements[0].get("eligible")
    assert "Version" in resp.headers

    validator = ResponseValidator(spec)
    request = MockRequest("http://trefle.pole-emploi.fr", "post",
                          "/financement")
    response = MockResponse(resp.body, resp.status.value)
    result = validator.validate(request, response)
    result.raise_for_errors()
コード例 #3
0
ファイル: extensions.py プロジェクト: asteven/nameko-openapi
 def load_spec(self, spec_file):
     log.debug('%s.load_spec: %s' % (self.__class__.__name__, spec_file))
     # TODO: supporting loading from url instead of just file
     # TODO: How to handle/interpret/respect spec.servers[].url's?
     # TODO: Or should this be generated/injected into the spec_dict on startup?
     #spec_file = '/home/sar/vcs/nameko-openapi/petstore.yaml'
     spec_dict = yaml.safe_load(open(spec_file))
     self.spec = openapi_core.create_spec(spec_dict)
     self.request_validator = RequestValidator(self.spec)
     self.response_validator = ResponseValidator(self.spec)
     self._loaded.send(self.spec)
コード例 #4
0
def validate(req, resp, path, query):
    """Validate a query and its response."""
    settings = utils.setup.Settings()
    warnings = []
    if settings.openapi:
        # check that the query complies to the api spec
        validator = RequestValidator(settings.openapi)
        result = validator.validate(req)
        settings.query_warnings += map(str, result.errors)
        warnings.extend(['OpenApi: ' + str(x) for x in result.errors])

        # check that the response complies to the api spec
        validator = ResponseValidator(settings.openapi)
        result = validator.validate(req, resp)
        settings.warnings += map(str, result.errors)
        warnings.extend(['OpenAPI: ' + str(x) for x in result.errors])

    if settings.use_json_schemas:
        if path != '/' and query is not None:
            # validate query against jsons schemas
            q_warns = utils.jsonschemas.validate(query, 'query', settings)
            warnings.extend(q_warns)
            settings.query_warnings += q_warns

        # validate response against jsons schemas
        warns = utils.jsonschemas.validate(resp.data, 'response', settings, path)
        warnings.extend(warns)
        settings.warnings += warns
    for warning in warnings:
        logging.warning(warning)

    return json.loads(resp.data)
コード例 #5
0
def test_healthz(client):
    """Test the /healthz endpoint."""

    path = '/v1/healthz'
    rv = client.get(path)

    # Validate request and response against OpenAPI spec
    with app.test_request_context(path):
        with open(app.config['OPENAPI_SPEC']) as stream:
            spec = create_spec(safe_load(stream))

        openapi_response = FlaskOpenAPIResponse(rv)
        openapi_request = FlaskOpenAPIRequest(request)
        validator = ResponseValidator(spec)
        result = validator.validate(openapi_request, openapi_response)
        result.raise_for_errors()

    assert rv.content_type == "text/plain"
    assert rv.status_code == 200
    assert b'OK' in rv.data
コード例 #6
0
ファイル: test_api.py プロジェクト: Vioasselin/trefle
async def test_simulate_endpoint_with_invalid_data(client):
    resp = await client.get("/schema")
    spec = create_spec(json.loads(resp.body))

    resp = await client.post(
        "/financement",
        body={
            "beneficiaire.remuneration": "1400",
            "beneficiaire.droit_prive": "invalide",
            "beneficiaire.entreprise.idcc": 2706,
        },
    )
    assert resp.status == HTTPStatus.UNPROCESSABLE_ENTITY
    assert "application/json" in resp.headers["Content-Type"]

    validator = ResponseValidator(spec)
    request = MockRequest("http://trefle.pole-emploi.fr", "post",
                          "/financement")
    response = MockResponse(resp.body, resp.status.value)
    result = validator.validate(request, response)
    result.raise_for_errors()
コード例 #7
0
    def __init__(
        self,
        spec: Union[Spec, dict],
        *,
        server_url: Optional[str] = None,
        client: Union[ModuleType, Requestable] = httpx,
        request_class: Type[ClientOpenAPIRequest] = ClientOpenAPIRequest,
        response_factory: Callable[[Any],
                                   OpenAPIResponse] = ClientOpenAPIResponse,
        headers: Optional[dict] = None,
    ):
        if not isinstance(spec, Spec):
            spec = create_spec(spec)
        self.spec = spec
        self.client = client
        self.request_class = request_class
        self.response_factory = response_factory
        self.common_headers = headers or {}

        if server_url is None:
            server_url = self.spec.servers[0].url
        else:
            server_url = server_url.rstrip("/")
            for server in self.spec.servers:
                if server_url == server.url:
                    break
            else:
                self.spec.servers.append(Server(server_url))
        self.server_url = server_url
        self.validator = ResponseValidator(self.spec)

        for path_spec in spec.paths.values():
            for op_spec in path_spec.operations.values():
                setattr(
                    self,
                    snakecase(op_spec.operation_id),
                    self._get_operation(op_spec).__get__(self),
                )
コード例 #8
0
ファイル: extensions.py プロジェクト: asteven/nameko-openapi
class OpenApiSpecManager(SharedExtension):

    _loaded = Event()

    def setup(self):
        log.info('   ###   OpenApiSpecManager.setup')
        super().setup()

    def load_spec(self, spec_file):
        log.debug('%s.load_spec: %s' % (self.__class__.__name__, spec_file))
        # TODO: supporting loading from url instead of just file
        # TODO: How to handle/interpret/respect spec.servers[].url's?
        # TODO: Or should this be generated/injected into the spec_dict on startup?
        #spec_file = '/home/sar/vcs/nameko-openapi/petstore.yaml'
        spec_dict = yaml.safe_load(open(spec_file))
        self.spec = openapi_core.create_spec(spec_dict)
        self.request_validator = RequestValidator(self.spec)
        self.response_validator = ResponseValidator(self.spec)
        self._loaded.send(self.spec)

    def wait_for_spec(self):
        """Allow other extensions to wait until the spec is loaded."""
        return self._loaded.wait()

    def get_operation_by_id(self, operation_id):
        self.wait_for_spec()
        for path_name, path in six.iteritems(self.spec.paths):
            for http_method, operation in six.iteritems(path.operations):
                if operation.operation_id == operation_id:
                    return operation

    def validate_request(self, request, raise_for_errors=True):
        result = self.request_validator.validate(request)
        if raise_for_errors:
            result.raise_for_errors()
        return result

    def validate_response(self,
                          response,
                          openapi_request,
                          raise_for_errors=True):
        result = self.response_validator.validate(openapi_request, response)
        if raise_for_errors:
            result.raise_for_errors()
        return result
コード例 #9
0
def validate(req, resp, path, query):
    """Validate a query and its response."""
    settings = utils.setup.Settings()
    warnings = []
    if settings.openapi:
        # check that the query complies to the api spec
        validator = RequestValidator(settings.openapi)
        result = validator.validate(req)
        settings.query_warnings += map(str, result.errors)
        warnings.extend(['OpenApi: ' + str(x) for x in result.errors])

        # check that the response complies to the api spec
        validator = ResponseValidator(settings.openapi)
        result = validator.validate(req, resp)
        settings.warnings += map(str, result.errors)
        for error in result.errors:
            if isinstance(error, InvalidSchemaValue):
                warning = f'OpenAPI:\n\tAt object {error.value}\n\t'
                warning += "\n\t".join(prettify_schemaerror(error))
                warnings.append(warning)
            else:
                warnings.append(f'OpenAPI: {str(error)}')

    if settings.use_json_schemas:
        if path != '/' and query is not None:
            # validate query against jsons schemas
            q_warns = utils.jsonschemas.validate(query, 'query', settings)
            warnings.extend(q_warns)
            settings.query_warnings += q_warns

        # validate response against jsons schemas
        warns = utils.jsonschemas.validate(resp.data, 'response', settings,
                                           path)
        warnings.extend(warns)
        settings.warnings += warns
    for warning in warnings:
        logging.warning(warning)

    return json.loads(resp.data)
コード例 #10
0
        async def wrapper(request: Request, **kwargs) -> Response:
            openapi_request = await StarletteOpenAPIRequest(request)
            validated_request = RequestValidator(
                self.spec,
                custom_formatters=self.custom_formatters,
                custom_media_type_deserializers=self.
                custom_media_type_deserializers,
            ).validate(openapi_request)
            try:
                validated_request.raise_for_errors()
            except InvalidSecurity as ex:
                raise HTTPException(HTTPStatus.FORBIDDEN,
                                    "Invalid security.") from ex
            except OpenAPIError as ex:
                raise HTTPException(HTTPStatus.BAD_REQUEST,
                                    "Bad request") from ex

            response = endpoint_fn(request, **kwargs)
            if iscoroutine(response):
                response = await response
            if isinstance(response, dict):
                response = JSONResponse(response)
            elif not isinstance(response, Response):
                raise ValueError(
                    f"The endpoint function `{endpoint_fn.__name__}` must return"
                    " either a dict or a Starlette Response instance.")

            # TODO: pass a list of operation IDs to specify which responses not to validate
            if self.validate_responses:
                ResponseValidator(
                    self.spec,
                    custom_formatters=self.custom_formatters,
                    custom_media_type_deserializers=self.
                    custom_media_type_deserializers,
                ).validate(
                    openapi_request,
                    StarletteOpenAPIResponse(response)).raise_for_errors()
            return response
コード例 #11
0
    def register():
        spec_dict = read_yaml_file(filepath)

        validate_spec(spec_dict)
        spec = create_spec(spec_dict)

        def spec_view(request):
            return FileResponse(
                filepath,
                request=request,
                content_type='text/yaml'
            )
        config.add_view(route_name=route_name, view=spec_view)
        config.add_route(route_name, route)

        custom_formatters = config.registry.settings.get('pyramid_openapi3_formatters')

        config.registry.settings['pyramid_openapi3'] = {
           "filepath": filepath,
           "spec_route_name": route_name,
           "spec": spec,
           "request_validator": RequestValidator(spec, custom_formatters),
           "response_validator": ResponseValidator(spec, custom_formatters),
        }
コード例 #12
0
def validate_request(req, path_pattern, path_params, spec, url):
    print('Validating URL: {}'.format(url))
    counters['requests'] += 1
    openapi_request = RequestsOpenAPIRequest(req, path_pattern, path_params)
    validator = RequestValidator(spec)
    result = validator.validate(openapi_request)
    request_errors = result.errors

    r = req.prepare()
    s = requests.Session()
    res = s.send(r)

    openapi_response = RequestsOpenAPIResponse(res)
    validator = ResponseValidator(spec)
    result = validator.validate(openapi_request, openapi_response)
    response_errors = result.errors

    print('Request errors: {}'.format(request_errors))
    print('Response errors: {}'.format(response_errors))
    if request_errors or response_errors:
        errors_count = len(request_errors) + len(response_errors)
        print(
            color(' [FAIL] {:d} errors found '.format(errors_count),
                  fg='white',
                  bg='red',
                  style='bold'))
        print("Response body: {}".format(res.text))
    else:
        errors_count = 0
        print(
            color(' [PASS] No errors found ',
                  fg='white',
                  bg='green',
                  style='bold'))
    print()
    return errors_count
コード例 #13
0
ファイル: openapi.py プロジェクト: prusa3d/hackaton2019
def response_validator_yaml(filename):
    """Initialization response_validator for openapi.yaml."""
    with open(filename, "r") as openapi:
        spec = create_spec(yaml.load(openapi, ExtendedSafeLoader))
        print("spec:", spec)
        return ResponseValidator(spec)
コード例 #14
0
ファイル: openapi.py プロジェクト: prusa3d/hackaton2019
def response_validator_json(filename):
    """Initialization response_validator for openapi.json."""
    with open(filename, "r") as openapi:
        spec = create_spec(json.load(openapi))
        return ResponseValidator(spec)
コード例 #15
0
class Client:
    def __init__(
        self,
        spec: Union[Spec, dict],
        *,
        server_url: Optional[str] = None,
        client: Union[ModuleType, Requestable] = httpx,
        request_class: Type[ClientOpenAPIRequest] = ClientOpenAPIRequest,
        response_factory: Callable[[Any],
                                   OpenAPIResponse] = ClientOpenAPIResponse,
        headers: Optional[dict] = None,
    ):
        if not isinstance(spec, Spec):
            spec = create_spec(spec)
        self.spec = spec
        self.client = client
        self.request_class = request_class
        self.response_factory = response_factory
        self.common_headers = headers or {}

        if server_url is None:
            server_url = self.spec.servers[0].url
        else:
            server_url = server_url.rstrip("/")
            for server in self.spec.servers:
                if server_url == server.url:
                    break
            else:
                self.spec.servers.append(Server(server_url))
        self.server_url = server_url
        self.validator = ResponseValidator(self.spec)

        for path_spec in spec.paths.values():
            for op_spec in path_spec.operations.values():
                setattr(
                    self,
                    snakecase(op_spec.operation_id),
                    self._get_operation(op_spec).__get__(self),
                )

    @staticmethod
    def _get_operation(op_spec):
        # TODO: extract args and kwargs from operation parameters
        def operation(
            self,
            *args,
            body_: Optional[Union[dict, list]] = None,
            headers_: Optional[dict] = None,
            **kwargs,
        ):
            request_headers = self.common_headers.copy()
            request_headers.update(headers_ or {})
            request = self.request_class(self.server_url, op_spec)
            request.prepare(*args,
                            body_=body_,
                            headers_=request_headers,
                            **kwargs)
            request_params = {
                "method": request.method,
                "url": request.url,
                "headers": request.headers,
            }
            if request.body:
                request_params["json" if "json" in
                               request.mimetype else "data"] = request.body
            api_response = self.client.request(**request_params)
            api_response.raise_for_status()
            response = self.response_factory(api_response)
            self.validator.validate(request, response).raise_for_errors()
            return response

        operation.__doc__ = op_spec.summary or op_spec.operation_id
        if op_spec.description:
            operation.__doc__ += f"\n\n{op_spec.description}"
        return operation

    @classmethod
    def from_file(cls, path: Union[Path, str], **kwargs):
        """Creates an instance of the class by loading the spec from a local file."""
        spec = get_spec_from_file(path)
        return cls(spec, **kwargs)