It validates PSR-7 messages (HTTP request/response) against OpenAPI specifications

Overview

Latest Stable Version Build Status License contributions welcome

NOTICE - THE PACKAGE HAS BEEN CONTRIBUTED TO THE PHP LEAGUE

Go to https://github.com/thephpleague/openapi-psr7-validator

This package is here for existing users only.

OpenAPI PSR-7 Message (HTTP Request/Response) Validator

This package can validate PSR-7 messages against OpenAPI (3.0.x) specifications expressed in YAML or JSON.

Installation

composer require lezhnev74/openapi-psr7-validator

OpenAPI (OAS) Terms

There are some specific terms that are used in the package. These terms come from OpenAPI:

  • specification - an OpenAPI document describing an API, expressed in JSON or YAML file
  • data - actual thing that we validate against a specification, including body and metadata
  • schema - the part of the specification that describes the body of the request / response
  • keyword - properties that are used to describe the instance are called key words, or schema keywords
  • path - a relative path to an individual endpoint
  • operation - a method that we apply on the path (like get /password)
  • response - described response (includes status code, content types etc)

How To Validate

ServerRequest Message

You can validate \Psr\Http\Message\ServerRequestInterface instance like this:

$yamlFile = "api.yaml";
$jsonFile = "api.json";

$validator = (new \OpenAPIValidation\PSR7\ValidatorBuilder)->fromYamlFile($yamlFile)->getServerRequestValidator();
#or
$validator = (new \OpenAPIValidation\PSR7\ValidatorBuilder)->fromYaml(file_get_contents($yamlFile))->getServerRequestValidator();
#or
$validator = (new \OpenAPIValidation\PSR7\ValidatorBuilder)->fromJson(file_get_contents($jsonFile))->getServerRequestValidator();
#or
$validator = (new \OpenAPIValidation\PSR7\ValidatorBuilder)->fromJsonFile($jsonFile)->getServerRequestValidator();
#or
$schema = new \cebe\openapi\spec\OpenApi(); // generate schema object by hand
$validator = (new \OpenAPIValidation\PSR7\ValidatorBuilder)->fromSchema($schema)->getServerRequestValidator();

$match = $validator->validate($request);

As a result you would get and OperationAddress $match which has matched the given request. If you already know the operation which should match your request (i.e you have routing in your project), you can use RouterRequestValidator

$address = new \OpenAPIValidation\PSR7\OperationAddress('/some/operation', 'post');

$validator = (new \OpenAPIValidation\PSR7\ValidatorBuilder)->fromSchema($schema)->getRoutedRequestValidator();

$validator->validate($address, $request);

This would simplify validation a lot and give you more performance.

Request Message

You can validate \Psr\Http\Message\RequestInterface instance like this:

$yamlFile = "api.yaml";
$jsonFile = "api.json";

$validator = (new \OpenAPIValidation\PSR7\ValidatorBuilder)->fromYamlFile($yamlFile)->getRequestValidator();
#or
$validator = (new \OpenAPIValidation\PSR7\ValidatorBuilder)->fromYaml(file_get_contents($yamlFile))->getRequestValidator();
#or
$validator = (new \OpenAPIValidation\PSR7\ValidatorBuilder)->fromJson(file_get_contents($jsonFile))->getRequestValidator();
#or
$validator = (new \OpenAPIValidation\PSR7\ValidatorBuilder)->fromJsonFile($jsonFile)->getRequestValidator();
#or
$schema = new \cebe\openapi\spec\OpenApi(); // generate schema object by hand
$validator = (new \OpenAPIValidation\PSR7\ValidatorBuilder)->fromSchema($schema)->getRequestValidator();

$match = $validator->validate($request);

Response Message

Validation of \Psr\Http\Message\ResponseInterface is a bit more complicated . Because you need not only YAML file and Response itself, but also you need to know which operation this response belongs to (in terms of OpenAPI).

Example:

$yamlFile = "api.yaml";
$jsonFile = "api.json";

$validator = (new \OpenAPIValidation\PSR7\ValidatorBuilder)->fromYamlFile($yamlFile)->getResponseValidator();
#or
$validator = (new \OpenAPIValidation\PSR7\ValidatorBuilder)->fromYaml(file_get_contents($yamlFile))->getResponseValidator();
#or
$validator = (new \OpenAPIValidation\PSR7\ValidatorBuilder)->fromJson(file_get_contents($jsonFile))->getResponseValidator();
#or
$validator = (new \OpenAPIValidation\PSR7\ValidatorBuilder)->fromJsonFile($jsonFile)->getResponseValidator();
#or
$schema = new \cebe\openapi\spec\OpenApi(); // generate schema object by hand
$validator = (new \OpenAPIValidation\PSR7\ValidatorBuilder)->fromSchema($schema)->getResponseValidator();

$operation = new \OpenAPIValidation\PSR7\OperationAddress('/password/gen', 'get') ;

$validator->validate($operation, $request);

Reuse Schema After Validation

\OpenAPIValidation\PSR7\ValidatorBuilder reads and compiles schema in memory as instance of \cebe\openapi\spec\OpenApi. Validators use this instance to perform validation logic. You can reuse this instance after the validation like this:

$validator = (new \OpenAPIValidation\PSR7\ValidatorBuilder)->fromYamlFile($yamlFile)->getServerRequestValidator();
# or
$validator = (new \OpenAPIValidation\PSR7\ValidatorBuilder)->fromYamlFile($yamlFile)->getResponseValidator();

/** @var \cebe\openapi\spec\OpenApi */
$openApi = $validator->getSchema();

Request Message

\Psr\Http\Message\RequestInterface validation is not implemented.

PSR-15 Middleware

PSR-15 middleware can be used like this:

$yamlFile = 'api.yaml';
$jsonFile = 'api.json';

$psr15Middleware = (new \OpenAPIValidation\PSR15\ValidationMiddlewareBuilder)->fromYamlFile($yamlFile)->getValidationMiddleware();
#or
$psr15Middleware = (new \OpenAPIValidation\PSR15\ValidationMiddlewareBuilder)->fromYaml(file_get_contents($yamlFile))->getValidationMiddleware();
#or
$psr15Middleware = (new \OpenAPIValidation\PSR15\ValidationMiddlewareBuilder)->fromJsonFile($jsonFile)->getValidationMiddleware();
#or
$psr15Middleware = (new \OpenAPIValidation\PSR15\ValidationMiddlewareBuilder)->fromJson(file_get_contents($jsonFile))->getValidationMiddleware();
#or
$schema = new \cebe\openapi\spec\OpenApi(); // generate schema object by hand
$validator = (new \OpenAPIValidation\PSR7\ValidationMiddlewareBuilder)->fromSchema($schema)->getValidationMiddleware();

SlimFramework Middleware

Slim framework uses slightly different middleware interface, so here is an adapter which you can use like this:

$yamlFile = 'api.yaml';
$jsonFile = 'api.json';

$psr15Middleware = (new \OpenAPIValidation\PSR15\ValidationMiddlewareBuilder)->fromYamlFile($yamlFile)->getValidationMiddleware();
#or
$psr15Middleware = (new \OpenAPIValidation\PSR15\ValidationMiddlewareBuilder)->fromYaml(file_get_contents($yamlFile))->getValidationMiddleware();
#or
$psr15Middleware = (new \OpenAPIValidation\PSR15\ValidationMiddlewareBuilder)->fromJsonFile($jsonFile)->getValidationMiddleware();
#or
$psr15Middleware = (new \OpenAPIValidation\PSR15\ValidationMiddlewareBuilder)->fromJson(file_get_contents($jsonFile))->getValidationMiddleware();
#or
$schema = new \cebe\openapi\spec\OpenApi(); // generate schema object by hand
$validator = (new \OpenAPIValidation\PSR7\ValidationMiddlewareBuilder)->fromSchema($schema)->getValidationMiddleware();

$slimMiddleware = new \OpenAPIValidation\PSR15\SlimAdapter($psr15Middleware);

/** @var \Slim\App $app */
$app->add($slimMiddleware);

Caching Layer / PSR-6 Support

PSR-7 Validator has a built-in caching layer (based on PSR-6 interfaces) which saves time on parsing OpenAPI specs. It is optional. You enable caching if you pass a configured Cache Pool Object to the static constructor like this:

// Configure a PSR-6 Cache Pool
$cachePool = new ArrayCachePool();

// Pass it as a 2nd argument
$validator = (new \OpenAPIValidation\PSR7\ValidatorBuilder)
    ->fromYamlFile($yamlFile)
    ->setCache($cachePool)
    ->getResponseValidator();
# or
\OpenAPIValidation\PSR15\ValidationMiddleware::fromYamlFile($yamlFile, $cachePool);

You can use ->setCache($pool, $ttl) call for both PSR-7 and PSR-15 builder in order to set proper expiration ttl in seconds (or explicit null)

If you want take control over the cache key for schema item, or your cache does not support cache key generation by itself you can ->overrideCacheKey('my_custom_key') to ensure cache uses key you want.

Standalone OpenAPI Validator

The package contains a standalone validator which can validate any data against an OpenAPI schema like this:

$spec = <<<SPEC
schema:
  type: string
  enum:
  - a
  - b
SPEC;
$data = "c";

$spec   = cebe\openapi\Reader::readFromYaml($spec);
# (optional) reference resolving
$spec->resolveReferences(new ReferenceContext($spec, "/"));
$schema = new cebe\openapi\spec\Schema($spec->schema);

try {
    (new \OpenAPIValidation\Schema\SchemaValidator())->validate($data, $schema);
} catch(\OpenAPIValidation\Schema\Exception\KeywordMismatch $e) {
    // you can evaluate failure details
    // $e->keyword() == "enum"
    // $e->data() == "c"
    // $e->dataBreadCrumb()->buildChain() -- only for nested data
}

Custom Type Formats

As you know, OpenAPI allows you to add formats to types:

schema:
  type: string
  format: binary

This package contains a bunch of built-in format validators:

  • string type:
    • byte
    • date
    • date-time
    • email
    • hostname
    • ipv4
    • ipv6
    • uri
    • uuid (uuid4)
  • number type
    • float
    • double

You can also add your own formats. Like this:

# A format validator must be a callable
# It must return bool value (true if format matched the data, false otherwise)

# A callable class:
$customFormat = new class()
{
    function __invoke($value): bool
    {
        return $value === "good value";
    }
};

# Or just a closure:
$customFormat = function ($value): bool {
    return $value === "good value";
};

# Register your callable like this before validating your data
\OpenAPIValidation\Schema\TypeFormats\FormatsContainer::registerFormat('string', 'custom', $customFormat);

Exceptions

The package throws a list of various exceptions which you can catch and handle. There are some of them:

  • Schema related:
    • \OpenAPIValidation\Schema\Exception\KeywordMismatch - Indicates that data was not matched against a schema's keyword
      • \OpenAPIValidation\Schema\Exception\TypeMismatch - Validation for type keyword failed against a given data. For example type:string and value is 12
      • \OpenAPIValidation\Schema\Exception\FormatMismatch - data mismatched a given type format. For example type: string, format: email won't match not-email.
  • PSR7 Messages related:
    • \OpenAPIValidation\PSR7\Exception\NoContentType - HTTP message(request/response) contains no Content-Type header. General HTTP errors.
    • \OpenAPIValidation\PSR7\Exception\NoPath - path is not found in the spec
    • \OpenAPIValidation\PSR7\Exception\NoOperation - operation os not found in the path
    • \OpenAPIValidation\PSR7\Exception\NoResponseCode - response code not found under the operation in the spec
    • Validation exceptions (check parent exception for possible root causes):
      • \OpenAPIValidation\PSR7\Exception\ValidationFailed - generic exception for failed PSR-7 message
      • \OpenAPIValidation\PSR7\Exception\Validation\InvalidBody - body does not match schema
      • \OpenAPIValidation\PSR7\Exception\Validation\InvalidCookies - cookies does not match schema or missing required cookie
      • \OpenAPIValidation\PSR7\Exception\Validation\InvalidHeaders - header does not match schema or missing required header
      • \OpenAPIValidation\PSR7\Exception\Validation\InvalidPath - path does not match pattern or pattern values does not match schema
      • \OpenAPIValidation\PSR7\Exception\Validation\InvalidQueryArgs - query args does not match schema or missing required argument
      • \OpenAPIValidation\PSR7\Exception\Validation\InvalidSecurity - request does not match security schema or invalid security headers
    • Request related:
      • \OpenAPIValidation\PSR7\Exception\MultipleOperationsMismatchForRequest - request matched multiple operations in the spec, but validation failed for all of them.

Testing

You can run the tests with:

vendor/bin/phpunit

Contribution Guide

Feel free to open an Issue or add a Pull request. There is a certain code style that this package follows: doctrine/coding-standard.

To conform to this style please use a git hook, shipped with this package at .githooks/pre-commit.

How to use it:

  1. Clone the package locally and navigate to the folder
  2. Create a symlink to the hook like this: ln -s -f ../../.githooks/pre-commit .git/hooks/pre-commit
  3. Add execution rights: chmod +x .git/hooks/pre-commit
  4. Now commit any new changes and the code will be checked and formatted accordingly.
  5. If there are any issues with your code, check the log here: .phpcs-report.txt

Credits

People:

Resources:

  • Icons made by Freepik, licensed by CC 3.0 BY
  • cebe/php-openapi package for Reading OpenAPI files
  • slim3-psr15 package for Slim middleware adapter

License

The MIT License (MIT). Please see License.md file for more information.

TODO

  • Support Discriminator Object (note: apparently, this is not so straightforward, as discriminator can point to any external scheme)
Comments
  • Issues using readFromYamlFile() on Windows

    Issues using readFromYamlFile() on Windows

    There is an issue with the underlying cebe/php-openapi library for Windows users that prevents us from calling readFromYamlFile(). @scaytrase has opened an issue on their repo so hopefully it'll be fixed soon. However, this bug is fixed in the underlying library the examples in the README file will not work as described for Windows users.

    In order to read the YAML file I had to do something slightly different to the example:

    $yamlFile = "api.yaml";
    
    $validator = new \OpenAPIValidation\PSR7\ResponseValidator(
        \cebe\openapi\Reader::readFromYaml(file_get_contents($yamlFile))
    );
    
    $operation = new \OpenAPIValidation\PSR7\OperationAddress('/password/gen', 'get') ;
    
    $validator->validate($operation, $request);
    

    Would you like me to submit a PR to update the README examples to use file_get_contents() as a temporary solution?


    I have also found that I am unable to run the test suite for the same reason. For reference, this is the error I get:

    OpenAPIValidationTests\PSR7\CompleteTest::test_request_green cebe\openapi\exceptions\UnresolvableReferenceException: Can not resolve references for a specification given as a relative path.

    I tried updating the failing test to use file_get_contents() but I get another error:

    OpenAPIValidationTests\PSR7\CompleteTest::test_request_green OpenAPIValidation\PSR7\Exception\Request\RequestBodyMismatch: OpenAPI spec does not match the body of the request [/complete/{param1}/{param2},post]: Argument 1 passed to OpenAPIValidation\Schema\Validator::__construct() must be an instance of cebe\openapi\spec\Schema, instance of cebe\openapi\spec\Reference given

    I'll have a crack at getting the test suite to run but I thought I'd bring this up in case it's affecting anyone else.

    bug 
    opened by brayniverse 24
  • Collaboration

    Collaboration

    Hey there! I think there is a great chance for you folks to collaborate here. I am opening up the conversation here as it has the most stars, but it could be anywhere. They are all good implementations.

    • @mieszkomalawski - https://gitlab.com/mmalawski/openapi-validator
    • @hkarlstrom - https://github.com/hkarlstrom/openapi-validation-middleware

    These packages all do the same thing.

    1. Read a JSON or YAML file
    2. Allow general PSR-7/PSR-15 middleware to be created for this file
    3. Offer at least request validation (maybe response too)
    4. Offer errors in various formats (RFC 7807 or others)
    5. Support custom formats

    I think you could all team up on a new @thephpleague project. I bet they'd take you if you all worked together, I can provide some guidance if needed, and then you just archive things on GitHub, mark them abandoned on Packagist, and the mega-package lives on forever despite whatever hurdles life throw at you individually.

    LMK if you like the sound of this. OpenAPI can be just too much for single maintainers to handle solo. 🤷‍♂

    opened by philsturgeon 15
  • Validation returns OperationAddress - just wondering why?

    Validation returns OperationAddress - just wondering why?

    I'm trying a simple bit of validation:

    $myJsonSpec = '{...}';
    $serverRequest = // PSR-7 message constructed by hand
    $validator = \OpenAPIValidation\PSR7\ServerRequestValidator::fromJson($myJsonSpec);
    $result = $validator->validate($serverRequest);
    

    If all validates correctly I get this object returned:

    OpenAPIValidation\PSR7\OperationAddress

    For example:

    object(OpenAPIValidation\PSR7\OperationAddress)#47 (2) {
      ["method":protected]=>
      string(3) "get"
      ["path":protected]=>
      string(15) "/ccodes/{ccode}"
    }
    

    The method and the path are keys to the operation, so I'm wondering whether it would be more useful to return the operation? The operationId could then be used to fire off further parsing of the server request (I'm looking at generated code to do this). I may be missing the reason for returning OperationAddress though.

    Without knowing the operationId at this point, further work needs to be done to decode the method and path to find it. It may already be available somewhere I'm not just looking.

    Also, the validate() method either returns this object, or throws an exception. The exception is just for one of what may be many validation errors. Is there a way to find all the validation errors rather than just reporting on one? Looking at the validator, I can see it scatters _errors across many of the nodes of its internal data structures, so I'm guessing it does scan for multiple validation errors at once.

    question/idea 
    opened by judgej 15
  • Caching of parsed OpenAPI definitions

    Caching of parsed OpenAPI definitions

    This is more a question than anything. Hopefully it is covered somewhere.

    So, for every inbound request to an API, the payload needs to be validated against an OpenAPI definition. That definition needs to be parsed/lexed/whatever into some form that can be used to do the validation. That will take time and resources to do.

    So, is that parsed form of the OpenAPI definition cached? Can it even be cached? Does the full parsing happen on every API call, maybe it's not as resource intensive as I think it may be (but judging by the speed of client-based OpenAPI editors once a 3.0 definition goes above a few hundred lines, I would assume it is fairly intense).

    I haven't tried it yet, but it looks ideal for a project I'm working on now.

    enhancement question/idea 
    opened by judgej 14
  • Exceptions consistency pt. 2

    Exceptions consistency pt. 2

    Second part of exception consistency (fixes #18)

    I've got some spare time, so here is some improvements:

    • [x] Generic validation exception signature (all validators now declare exceptions they will throw, incl. top-level request and response validators)
    • [x] Consistent exception hierarchy
    • [x] More precise exception rethrow control - switch-case rethrow will not catch unintended exceptions now
    • [x] Make inner validators throw needed exceptions directly (get rid of magic error codes and additional switch-cases)
    • [x] Update docs with new exceptions (I noticed there was no BreadCrumb usage example in the readme, probably this PR is a good place to add it)
    opened by scaytrase 13
  • Get the name of the invalid property

    Get the name of the invalid property

    I'm trying to use this library to generate error messages that are visible to the end-user. Thus I need a way to find out the name of the property that's not valid.

    As far as I can tell there is no way to this right now. I'm thinking of something like that:

    try {
     // Validation
    } catch (OpenAPIValidation\PSR7\Exception\Validation\InvalidBody $e) {
        $property = $e->getInvalidPropertyName();
        echo "The field '$property' is not valid. Please correct it and try again."
    }
    

    Is something like that possible and if not are there any plans to implement this?

    Thanks

    opened by Aaronmacaron 11
  • Validating non string query parameters

    Validating non string query parameters

    Hello,

    I'm trying to validate a request containing two query parameters.

    image

    Upon doing so i am receiving a RequestQueryArgumentMismatch due to the query arguments of the request not being a type 'integer', and a 'string' given.

    As of version 3.0 of openapi, query parameters can be specified as an integer along with other non string data types, i would have thought the PSR-7 validator would be able to look at a query parameter and check if it is an integer within a string.

    https://swagger.io/docs/specification/describing-parameters/

    meditation-required 
    opened by willchambers99 9
  • Relative paths in servers objects

    Relative paths in servers objects

    When url field in server object containts relative path, for example "/v2" or "/", validator can't match request with operation definition.

    For example, if we have definition:

    servers:
      - url: '/v1'
    paths:
      /operations:
        post:
    ...
    

    and try to validate POST request "http://localhost/v1/operations", we get an exception "OpenAPI spec contains no such operation [/operations,post]". But if we specify server url as "http://localhost/v1", everything works fine.

    If I understand it correctly, there is no possibility to match any request, when server path is relative (or if "servers" section is omitted altogether, and default value is one server with url="/").

    From OpenAPI documentaion it is not very clear what to do in this case https://swagger.io/docs/specification/api-host-and-base-path/#relative-urls https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#serverObject

    The URLs in the servers array can be relative, such as /v2. In this case, the URL is resolved against the server that hosts the given OpenAPI definition.

    Should validator ignore server name from request, when path is relative? Or somehow get "server that hosts the given OpenAPI definition" and match it to server name from request?

    Do you have any suggestions?

    meditation-required 
    opened by TarasBK 9
  • Exception consistency (pt.1). Stateless schema validator

    Exception consistency (pt.1). Stateless schema validator

    Key changes:

    • Validator instance is now statless. It can be re-used to validate other data against other schema.
    • Consistent \Schema\Exception
      • new exception class - InvalidSchema - for converting defensive respect validations. It's considered Runtime, so not reflected in sigatures as defencive check
      • new exception class - SchemaMismatch - as a base class for all other validation exceptions
      • new exception hierarchy
        • SchemaMismatch
          • supports breadcrumbs on this level
        • ValidationKeywordFailed (I find this naming strange, but didn't touch it)
        • TypeMismatch (keyword === type)
        • FormatMismatch (keyword === type)
    • Validator interface

    Also I've updated all signatures in \Schema namespace to reflect real exceptions that are expected as validation result.

    Fixes #14, partially #18

    opened by scaytrase 9
  • Replace abstract Validator with ValidatorBuilder

    Replace abstract Validator with ValidatorBuilder

    Fixes #11 (Improves)

    Key notes:

    • Totally decoupled ServerRequestValidator and ResponseValidator
    • Validators are created using builder
    • Schemas are created with designated schema factories. The should be probably moved to Schema-level namespace and reused from other components
    • ~Tried to make psr/cache optional (suggested) in composer.json, since there is a way without using its classes for PSR-7 validator for now. But we should go for it further and refactor other cache usages or revert this change~

    Also:

    • No additional unit tests are written at the moment, neither old refactored. I'll create them after architectural agreements are reached
    opened by scaytrase 8
  • Composite validation rules' related exceptions are lost

    Composite validation rules' related exceptions are lost

    Hi,

    The InvalidSchema exception (and maybe others) will only display the wrapping error and omit the related ones when the error is a composite one (such as Respect/Validation's AllOf).

    The issue with this is we don't know what the errors actually are, which makes it really hard to troubleshoot, e.g:

    OpenAPIValidation\PSR7\Exception\Response\ResponseBodyMismatch: OpenAPI spec does not match the body of the response [/api/test,get,200]: Schema(or data) validation failed: All of the required rules must pass for "{\"data\":\"test\"}"
    

    This doesn't tell you what the actual (related) exceptions are.

    Hope that makes sense!

    Thanks,

    Yannick

    meditation-required 
    opened by osteel 7
  • Implement complete support for Security schemas

    Implement complete support for Security schemas

    As described here https://swagger.io/docs/specification/authentication/ possible security schemas are:

    • [x] http
    • [x] apiKey
    • [ ] openIdConnect
    • [ ] oauth2

    See \OpenAPIValidation\PSR7\Validators\SecurityValidator This ticket is to track activities about that

    opened by lezhnev74 0
  • Needless dependency on symfony/yaml

    Needless dependency on symfony/yaml

    Hi,

    Related to https://github.com/lezhnev74/openapi-psr7-validator/issues/65 and idea of moving this package to php league.

    I personally believe that in order to spread good practices and allow as many folks as possible to use openapi validation, we should focus on having as little dependencies as possible.

    Currently, this package uses cebe/php-openapi which forces installation of symfony/yaml component. This indirectly forces a minimal version of Symfony (version >=4 I believe) and cuts off some potential developers. IMO input source (json/yaml) should be separated from parsing library. It should be up to developer to parse yaml/json (symfony/yaml could be a suggestion in composer.json) and then pass array or stdClass to cebe/php-openapi. This may seem like a bit of extra effort from developer but it will result in less dependencies and higher adoption.

    opened by mieszkomalawski 4
  • Missing inner exception message/detail when using AnyOf/OneOf

    Missing inner exception message/detail when using AnyOf/OneOf

    Not sure that could be considered as a bug or maybe an improvement...

    When checking the solution applied for #57 I realised that this library discards the exceptions from the inner validations done with OneOf and AnyOf:

    Here, for example src/Schema/Keywords/OneOf.php:

            foreach ($oneOf as $schema) {
                try {
                    $schemaValidator->validate($data, $schema, $this->dataBreadCrumb);
                    $matchedCount++;
                } catch (SchemaMismatch $e) {
                    // that did not match... its ok         <<<<<<<<< HERE >>>>>>>>>>
                }
            }
    
            if ($matchedCount !== 1) {
                throw KeywordMismatch::fromKeyword('oneOf', $data, sprintf('Data must match exactly one schema, but matched %d', $matchedCount));
            }
    

    so if we have in definition something like:

          properties:
            some_property_here:
              oneOf:
                - type: object
                - type: string
                  maxLength: 10
                  minLength: 1
    

    And send a request with:

    {
        "some_property_here": "String with more than 10 characters"
    }
    

    One would expected to see the message: Keyword validation failed: Data must match exactly one schema, but matched 0 from the OneOf (which is ok and displaying correctly) but also the reason why it didn't match: Length of '35' must be shorter or equal to 10 but this last message gets discarded when we catch SchemaMismatch $e and don't do anything with it...

    So maybe there's a way to display those failed constraints and messages to get more specific errors?

    The idea I had was to use the previous feature from exceptions as you're doing already in some places. So here (with some line breaks to make it more readable):

            $prev = null;
            foreach ($oneOf as $schema) {
                try {
                    $schemaValidator->validate($data, $schema, $this->dataBreadCrumb);
                    $matchedCount++;
                } catch (SchemaMismatch $e) {
                    $prev = $e; // that did not match... its ok
                }
            }
    
    ...
    
                throw KeywordMismatch::fromKeyword(
                    'oneOf',
                    $data,
                    sprintf('Data must match exactly one schema, but matched %d', $matchedCount),
                    $prev // <<< HERE, there's already this argument in `fromKeyword` method
                );
    

    Of course you need to consider that this catch block might get executed more than once, and you would need to somehow get/group all those inner exceptions (with my example I'm overriding $prev everytime)... idk, maybe we could start with at least 1 specific exception (which is better than none) and implement this multiple exception handling later 🤷‍♂ 🙂

    opened by vsouz4 4
  • Validation of callbacks

    Validation of callbacks

    Some way to validate the request and response of callbacks would be great.

    I think the validation per se would work, but there needs to be some other way to resolve the specs. Right now, the specs are loaded based on a passed OperationAddress which doesn't fit for callbacks.

    enhancement 
    opened by digilist 6
  • Add parameters serialisation support

    Add parameters serialisation support

    Currently, the package does not support serialization of parameters.

    This ticket is to track efforts for adding serialization support to the package.

    Docs:

    • https://swagger.io/docs/specification/serialization/
    • https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#support-for-x-www-form-urlencoded-request-bodies

    Notes:

    • application/x-www-form-urlencoded body messages cannot be fully supported without serialization support
    • #38
    enhancement 
    opened by lezhnev74 1
Releases(0.15.2)
Owner
Dmitry Lezhnev
Inspired Software Craftsman. Husband. Traveler.
Dmitry Lezhnev
OpenAPI Spec to API in 3, 2, 1... done!

yii2-app-api OpenAPI Spec to API in 3, 2, 1... done! Yii Framework Application Template for quickly building API-first applications. Based on yii2-ope

Carsten Brandt 95 Oct 24, 2022
PSR-7 HTTP message library

PSR-7 Message Implementation This repository contains a full PSR-7 message implementation, several stream decorators, and some helpful functionality l

Guzzle 7.6k Jan 4, 2023
💫 Vega is a CLI mode HTTP web framework written in PHP support Swoole, WorkerMan / Vega 是一个用 PHP 编写的 CLI 模式 HTTP 网络框架,支持 Swoole、WorkerMan

Mix Vega 中文 | English Vega is a CLI mode HTTP web framework written in PHP support Swoole, WorkerMan Vega 是一个用 PHP 编写的 CLI 模式 HTTP 网络框架,支持 Swoole、Work

Mix PHP 46 Apr 28, 2022
Async HTTP proxy connector, tunnel any TCP/IP-based protocol through an HTTP CONNECT proxy server, built on top of ReactPHP.

clue/reactphp-http-proxy Async HTTP proxy connector, tunnel any TCP/IP-based protocol through an HTTP CONNECT proxy server, built on top of ReactPHP.

Christian Lück 43 Dec 25, 2022
Slim Framework flash messages service provider

Slim Framework Flash Messages This repository contains a Slim Framework Flash messages service provider. This enables you to define transient messages

Slim Framework 143 Dec 11, 2022
A Slim Framework Flash messages service provider

Slim Framework Flash Messages This repository contains a Slim Framework Flash messages service provider. This enables you to define transient messages

Slim Framework 143 Dec 11, 2022
Middleware to execute request handlers discovered by a router

middlewares/request-handler Middleware to execute request handlers discovered by a router.

Middlewares 39 Dec 9, 2022
PSR-15 Adapter for InertiaJS

inertia-psr15 Before using this library, is important to know what is Inertia.js, what is it for and how it works, in the official Inertia.js website

Mohamed Cherif Bouchelaghem 28 Oct 26, 2022
Strict PSR-7 implementation used by the Slim Framework

Strict PSR-7 implementation used by the Slim Framework, but you may use it separately with any framework compatible with the PSR-7 standard.

Slim Framework 96 Nov 14, 2022
Spiral Framework is a High-Performance PHP/Go Full-Stack framework and group of over sixty PSR-compatible components

Spiral HTTP Application Skeleton Spiral Framework is a High-Performance PHP/Go Full-Stack framework and group of over sixty PSR-compatible components.

Spiral Scout 152 Dec 18, 2022
A set of PSR-7 object decorators providing useful convenience methods

Slim-Http Slim PSR-7 Object Decorators Installation It's recommended that you use Composer to install this library. $ composer require slim/http This

Slim Framework 136 Sep 29, 2022
An asynchronous event driven PHP socket framework. Supports HTTP, Websocket, SSL and other custom protocols. PHP>=5.3.

Workerman What is it Workerman is an asynchronous event-driven PHP framework with high performance to build fast and scalable network applications. Wo

walkor 10.2k Dec 31, 2022
This package provides some basic methods to implement a self updating functionality for your Laravel application. Already bundled are some methods to provide a self-update mechanism via Github or some private repository via http.

This package provides some basic methods to implement a self updating functionality for your Laravel 5 application. Already bundled are some methods to provide a self-update mechanism via Github.

Holger Lösken 311 Dec 31, 2022
High performance HTTP Service Framework for PHP based on Workerman.

webman High performance HTTP Service Framework for PHP based on Workerman. Manual https://www.workerman.net/doc/webman Benchmarks https://www.techempo

walkor 1.3k Jan 2, 2023
基于 mirai-api-http 的 PHP 机器人框架

miraiez 开始使用 请在 mirai-api-http 的配置文件中启用 http 和 webhook 适配器 并将 webhook 适配器的回调地址设置为 webhook.php 的所在地址 例如 http://localhost/webhook.php 完成上述步骤后,请修改 config

null 20 Jan 9, 2023
jojo, another http server written in PHP 8.0

جوجو | jojo جوجو، وب‌سروری در ابعاد جوجه برای کارهای کوچک داستان نوشتن جوجو وب‌سروری که تنظیمات مودم TP-link TD-8811 توی اتاقم رو serve میکنه اسمش mic

Amirhossein Baghaie 6 Dec 25, 2022
This package provides a high performance HTTP server to speed up your Laravel/Lumen application based on Swoole.

This package provides a high performance HTTP server to speed up your Laravel/Lumen application based on Swoole.

Swoole Taiwan 3.9k Jan 8, 2023
PHP Coroutine HTTP client - Swoole Humanization Library

PHP Coroutine HTTP client - Swoole Humanization Library

swlib 973 Dec 17, 2022
MaxPHP HTTP project skeleton.

轻量 • 简单 • 快速 一款基于swoole的组件化的轻量PHP框架,可以用作API开发,方便快速。 主要特性 组件和框架核心分离 基于 Psr7 的 HTTP-Message 基于 Psr11 的容器 基于 Psr14 的事件 基于 Psr15 的中间件 基于 Psr16 的缓存组件,支持 Fi

chengyao 9 Dec 15, 2022