Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
44 / 44
100.00% covered (success)
100.00%
3 / 3
CRAP
100.00% covered (success)
100.00%
1 / 1
JsonBodyValidator
100.00% covered (success)
100.00%
44 / 44
100.00% covered (success)
100.00%
3 / 3
14
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 validateBody
100.00% covered (success)
100.00%
28 / 28
100.00% covered (success)
100.00%
1 / 1
8
 getOpenAPISpec
100.00% covered (success)
100.00%
15 / 15
100.00% covered (success)
100.00%
1 / 1
5
1<?php
2
3namespace MediaWiki\Rest\Validator;
4
5use FormatJson;
6use MediaWiki\Rest\LocalizedHttpException;
7use MediaWiki\Rest\RequestInterface;
8use Wikimedia\Message\ListParam;
9use Wikimedia\Message\ListType;
10use Wikimedia\Message\MessageValue;
11use Wikimedia\ParamValidator\ParamValidator;
12
13/**
14 * Do-nothing body validator
15 */
16class JsonBodyValidator implements BodyValidator {
17
18    /**
19     * @var array[]
20     */
21    private $bodyParamSettings;
22
23    /**
24     * @param array[] $bodyParamSettings
25     */
26    public function __construct( array $bodyParamSettings ) {
27        $this->bodyParamSettings = $bodyParamSettings;
28    }
29
30    /**
31     * @inheritDoc
32     * @return array
33     */
34    public function validateBody( RequestInterface $request ) {
35        $jsonStream = $request->getBody();
36        $status = FormatJson::parse( "$jsonStream", FormatJson::FORCE_ASSOC );
37
38        if ( !$status->isOK() ) {
39            throw new LocalizedHttpException(
40                new MessageValue( 'rest-json-body-parse-error', [ "$status" ] ),
41                400
42            );
43        }
44
45        $data = $status->value;
46
47        if ( !is_array( $data ) ) {
48            throw new LocalizedHttpException( new MessageValue( 'rest-bad-json-body' ), 400 );
49        }
50
51        $uncheckedBodyKeys = array_fill_keys( array_keys( $data ), true );
52        foreach ( $this->bodyParamSettings as $name => $settings ) {
53            if ( !empty( $settings[ParamValidator::PARAM_REQUIRED] ) && !isset( $data[$name] ) ) {
54                throw new LocalizedHttpException(
55                    new MessageValue( 'rest-missing-body-field', [ $name ] ), 400
56                );
57            }
58
59            if ( !isset( $data[$name] ) ) {
60                $data[$name] = $settings[ParamValidator::PARAM_DEFAULT] ?? null;
61            }
62
63            unset( $uncheckedBodyKeys[$name] );
64            // TODO: use a ParamValidator to check field value, etc!
65        }
66        if ( $uncheckedBodyKeys ) {
67            throw new LocalizedHttpException(
68                new MessageValue(
69                    'rest-extraneous-body-fields',
70                    [ new ListParam( ListType::COMMA, array_keys( $uncheckedBodyKeys ) ) ]
71                ),
72                400
73            );
74        }
75
76        return $data;
77    }
78
79    /**
80     * Returns an OpenAPI Schema Object specification structure as an associative array.
81     * @see https://swagger.io/specification/#schema-object
82     *
83     *
84     * This will contain information about the supported parameters.
85     *
86     * @return array
87     */
88    public function getOpenAPISpec(): array {
89        $body = [];
90        $required = [];
91
92        // XXX: Maybe we want to be able to define a spec file in the route definition?
93        // NOTE: the route definition may not be loaded when this is called before init()!
94
95        foreach ( $this->bodyParamSettings as $name => $paramSetting ) {
96            $param = Validator::getParameterSpec(
97                $name,
98                $paramSetting
99            );
100
101            $body['properties'][$name] = $param['schema'];
102
103            if ( isset( $param['description'] ) ) {
104                $body['properties'][$name]['description'] = $param['description'];
105            }
106
107            if ( $param['required'] ?? false ) {
108                $required[] = $param['name'];
109            }
110        }
111
112        if ( $required ) {
113            $body['required'] = $required;
114        }
115
116        return $body;
117    }
118}