Passed
Branch master (950424)
by Christopher
11:06
created

SegmentParser::createSegmentDescriptors()   B

Complexity

Conditions 4
Paths 5

Size

Total Lines 35
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 23
nc 5
nop 2
dl 0
loc 35
rs 8.5806
c 0
b 0
f 0
1
<?php
2
3
namespace POData\UriProcessor\ResourcePathProcessor\SegmentParser;
4
5
use Illuminate\Support\Str;
6
use POData\Common\Messages;
7
use POData\Common\ODataConstants;
8
use POData\Common\ODataException;
9
use POData\Providers\Metadata\ResourceEntityType;
10
use POData\Providers\Metadata\ResourcePropertyKind;
11
use POData\Providers\Metadata\ResourceSetWrapper;
12
use POData\Providers\Metadata\ResourceType;
13
use POData\Providers\Metadata\ResourceTypeKind;
14
use POData\Providers\ProvidersWrapper;
15
16
/**
17
 * Class SegmentParser.
18
 *
19
 * A parser to parse the segments in OData URI, Uri is made up of bunch of segments,
20
 * each segment is separated by '/' character
21
 * e.g. Customers('ALFKI')/Orders(2134)/Order_Details/Product
22
 *
23
 * Syntax of an OData segment is:
24
 * Segment       : identifier[(keyPredicate)]?            : e.g. Customers, Customers('ALFKI'),
25
 *                                                          Order_Details(OrderID=123, ProductID=11)
26
 * keyPredicate  : keyValue | NamedKeyValue
27
 * NamedKeyValue : keyName=keyValue [, keyName=keyValue]* : e.g. OrderID=123, ProductID=11
28
 * keyValue      : quotedValue | unquotedValue            : e.g. 'ALFKI'
29
 * quotedValue   : "'" nqChar "'"
30
 * unquotedValue : [.*]                                   : Any character
31
 * nqChar        : [^\']                                  : Character other than quotes
32
 */
33
class SegmentParser
34
{
35
    /**
36
     * The wrapper of IMetadataProvider and IQueryProvider.
37
     *
38
     * @var ProvidersWrapper
39
     */
40
    private $providerWrapper;
41
42
    /**
43
     * Array of SegmentDescriptor describing each segment in the request Uri.
44
     *
45
     * @var SegmentDescriptor[]
46
     */
47
    private $segmentDescriptors = [];
48
49
    /**
50
     * Constructs a new instance of SegmentParser.
51
     *
52
     * @param ProvidersWrapper $providerWrapper Reference to metadata and query provider wrapper
53
     */
54
    private function __construct(ProvidersWrapper $providerWrapper)
55
    {
56
        $this->providerWrapper = $providerWrapper;
57
    }
58
59
    /**
60
     * Parse the given Uri segments.
61
     *
62
     * @param string[]         $segments        Array of segments in the request Uri
63
     * @param ProvidersWrapper $providerWrapper Reference to metadata and query provider wrapper
64
     * @param bool             $checkForRights  Whether to check for rights on the resource sets in the segments
65
     *
66
     * @throws ODataException If any error occurs while processing segment
67
     *
68
     * @return SegmentDescriptor[]
69
     */
70
    public static function parseRequestUriSegments($segments, ProvidersWrapper $providerWrapper, $checkForRights = true)
71
    {
72
        $segmentParser = new self($providerWrapper);
73
        $segmentParser->createSegmentDescriptors($segments, $checkForRights);
74
75
        return $segmentParser->segmentDescriptors;
76
    }
77
78
    /**
79
     * Extract identifier and key predicate from a segment.
80
     *
81
     * @param string $segment       The segment from which identifier and key
82
     * @param string &$identifier   On return, this parameter will contain identifier part of the segment
83
     * @param string &$keyPredicate On return, this parameter will contain key predicate part of the segment,
84
     *                              null if predicate is absent
85
     *
86
     * @throws ODataException If any error occurs while processing segment
87
     */
88
    private function extractSegmentIdentifierAndKeyPredicate($segment, &$identifier, &$keyPredicate)
89
    {
90
        $predicateStart = strpos($segment, '(');
91
        if ($predicateStart === false) {
92
            $identifier = $segment;
93
            $keyPredicate = null;
94
95
            return;
96
        }
97
98
        $segmentLength = strlen($segment);
99
        if (strrpos($segment, ')') !== $segmentLength - 1) {
100
            throw ODataException::createSyntaxError(Messages::syntaxError());
101
        }
102
103
        $identifier = substr($segment, 0, $predicateStart);
104
        ++$predicateStart;
105
        $keyPredicate = substr($segment, $predicateStart, $segmentLength - $predicateStart - 1);
106
        $keyPredicate = str_replace('%C3%82%C2%BB', '/', $keyPredicate);
107
    }
108
109
    /**
110
     * Process a collection of OData URI segment strings and turn them into segment descriptors.
111
     *
112
     * @param string[] $segments    array of segments strings to parse
113
     * @param bool     $checkRights Whether to check for rights or not
114
     *
115
     * @throws ODataException Exception in case of any error found while precessing segments
116
     * @return mixed
117
     */
118
    private function createSegmentDescriptors($segments, $checkRights)
119
    {
120
        if (empty($segments)) {
121
            //If there's no segments, then it's the service root
122
            $descriptor = new SegmentDescriptor();
123
            $descriptor->setTargetKind(TargetKind::SERVICE_DIRECTORY());
124
            $this->segmentDescriptors[] = $descriptor;
125
126
            return;
127
        }
128
129
        $segmentCount = count($segments);
130
        $identifier = $keyPredicate = null;
131
        $this->extractSegmentIdentifierAndKeyPredicate($segments[0], $identifier, $keyPredicate);
132
        $previous = $this->createFirstSegmentDescriptor(
133
            $identifier,
134
            $keyPredicate,
135
            $checkRights
136
        );
137
        assert($previous instanceof SegmentDescriptor, get_class($previous));
138
        $this->segmentDescriptors[0] = $previous;
139
140
        for ($i = 1; $i < $segmentCount; ++$i) {
141
            $thisSegment = $segments[$i];
142
            $current = $this->createNextSegment($previous, $thisSegment, $checkRights);
143
144
            $current->setPrevious($previous);
145
            $previous->setNext($current);
146
            $this->segmentDescriptors[] = $current;
147
            $previous = $current;
148
        }
149
150
        //At this point $previous is the final segment..which cannot be a $link
151
        if ($previous->getTargetKind() == TargetKind::LINK()) {
152
            throw ODataException::createBadRequestError(Messages::segmentParserMissingSegmentAfterLink());
153
        }
154
    }
155
156
    /**
157
     * @param SegmentDescriptor $previous
158
     * @param string            $segment
159
     * @param bool              $checkRights
160
     *
161
     * @throws ODataException
162
     * @return SegmentDescriptor
163
     */
164
    private function createNextSegment(SegmentDescriptor $previous, $segment, $checkRights)
165
    {
166
        $previousKind = $previous->getTargetKind();
167
        if ($previousKind == TargetKind::METADATA()
168
            || $previousKind == TargetKind::BATCH()
169
            || $previousKind == TargetKind::PRIMITIVE_VALUE()
170
            || $previousKind == TargetKind::BAG()
171
            || $previousKind == TargetKind::MEDIA_RESOURCE()
172
        ) {
173
            //All these targets are terminal segments, there cannot be anything after them.
174
            throw ODataException::resourceNotFoundError(
175
                Messages::segmentParserMustBeLeafSegment($previous->getIdentifier())
176
            );
177
        }
178
179
        $identifier = $keyPredicate = null;
180
        $this->extractSegmentIdentifierAndKeyPredicate($segment, $identifier, $keyPredicate);
181
        $hasPredicate = null !== $keyPredicate;
182
183
        $singleton = $this->providerWrapper->resolveSingleton($identifier);
184
        if (null !== $singleton) {
185
            throw ODataException::createSyntaxError('Singleton must be first element');
186
        }
187
188
        if ($previousKind == TargetKind::PRIMITIVE()) {
189
            if ($identifier !== ODataConstants::URI_VALUE_SEGMENT) {
190
                throw ODataException::resourceNotFoundError(
191
                    Messages::segmentParserOnlyValueSegmentAllowedAfterPrimitivePropertySegment(
192
                        $identifier,
193
                        $previous->getIdentifier()
194
                    )
195
                );
196
            }
197
198
            $this->assertion(!$hasPredicate);
199
            $current = SegmentDescriptor::createFrom($previous);
200
            $current->setIdentifier(ODataConstants::URI_VALUE_SEGMENT);
201
            $current->setTargetKind(TargetKind::PRIMITIVE_VALUE());
202
            $current->setSingleResult(true);
203
        } elseif (null !== $previous->getPrevious()
204
                  && $previous->getPrevious()->getIdentifier() === ODataConstants::URI_LINK_SEGMENT
205
                  && $identifier !== ODataConstants::URI_COUNT_SEGMENT) {
206
            throw ODataException::createBadRequestError(
207
                Messages::segmentParserNoSegmentAllowedAfterPostLinkSegment($identifier)
208
            );
209
        } elseif ($previousKind == TargetKind::RESOURCE()
210
            && $previous->isSingleResult()
211
            && $identifier === ODataConstants::URI_LINK_SEGMENT
212
        ) {
213
            $this->assertion(!$hasPredicate);
214
            $current = SegmentDescriptor::createFrom($previous);
215
            $current->setIdentifier(ODataConstants::URI_LINK_SEGMENT);
216
            $current->setTargetKind(TargetKind::LINK());
217
        } else {
218
            //Do a sanity check here
219
            if ($previousKind != TargetKind::COMPLEX_OBJECT()
220
                && $previousKind != TargetKind::RESOURCE()
221
                && $previousKind != TargetKind::LINK()
222
            ) {
223
                throw ODataException::createInternalServerError(
224
                    Messages::segmentParserInconsistentTargetKindState()
225
                );
226
            }
227
228
            if (!$previous->isSingleResult() && $identifier !== ODataConstants::URI_COUNT_SEGMENT) {
229
                throw ODataException::createBadRequestError(
230
                    Messages::segmentParserCannotQueryCollection($previous->getIdentifier())
231
                );
232
            }
233
234
            $current = new SegmentDescriptor();
235
            $current->setIdentifier($identifier);
236
            $current->setTargetSource(TargetSource::PROPERTY);
0 ignored issues
show
Bug introduced by
POData\UriProcessor\Reso...\TargetSource::PROPERTY of type integer is incompatible with the type POData\UriProcessor\Reso...mentParser\TargetSource expected by parameter $targetSource of POData\UriProcessor\Reso...ptor::setTargetSource(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

236
            $current->setTargetSource(/** @scrutinizer ignore-type */ TargetSource::PROPERTY);
Loading history...
237
            $previousType = $previous->getTargetResourceType();
238
            $projectedProperty = $previousType->resolveProperty($identifier);
239
            $current->setProjectedProperty($projectedProperty);
240
241
            if ($identifier === ODataConstants::URI_COUNT_SEGMENT) {
242
                if ($previousKind != TargetKind::RESOURCE()) {
243
                    throw ODataException::createBadRequestError(
244
                        Messages::segmentParserCountCannotBeApplied($previous->getIdentifier())
245
                    );
246
                }
247
248
                if ($previous->isSingleResult()) {
249
                    throw ODataException::createBadRequestError(
250
                        Messages::segmentParserCountCannotFollowSingleton($previous->getIdentifier())
251
                    );
252
                }
253
254
                $current->setTargetKind(TargetKind::PRIMITIVE_VALUE());
255
                $current->setSingleResult(true);
256
                $current->setTargetResourceSetWrapper(
257
                    $previous->getTargetResourceSetWrapper()
258
                );
259
                $current->setTargetResourceType(
260
                    $previous->getTargetResourceType()
261
                );
262
            } elseif ($identifier === ODataConstants::URI_VALUE_SEGMENT
263
                && $previousKind == TargetKind::RESOURCE()
264
            ) {
265
                $current->setSingleResult(true);
266
                $current->setTargetResourceType(
267
                    $previous->getTargetResourceType()
268
                );
269
                $current->setTargetKind(TargetKind::MEDIA_RESOURCE());
270
            } elseif (null === $projectedProperty) {
271
                if (null !== $previous->getTargetResourceType()
272
                    && null !== $previous->getTargetResourceType()->tryResolveNamedStreamByName($identifier)
273
                ) {
274
                    $current->setTargetKind(TargetKind::MEDIA_RESOURCE());
275
                    $current->setSingleResult(true);
276
                    $current->setTargetResourceType(
277
                        $previous->getTargetResourceType()
278
                    );
279
                } else {
280
                    throw ODataException::createResourceNotFoundError($identifier);
281
                }
282
            } else {
283
                $current->setTargetResourceType($projectedProperty->getResourceType());
284
                $current->setSingleResult($projectedProperty->getKind() != ResourcePropertyKind::RESOURCESET_REFERENCE);
285
                if ($previousKind == TargetKind::LINK()
286
                    && $projectedProperty->getTypeKind() != ResourceTypeKind::ENTITY()
287
                ) {
288
                    throw ODataException::createBadRequestError(
289
                        Messages::segmentParserLinkSegmentMustBeFollowedByEntitySegment(
290
                            $identifier
291
                        )
292
                    );
293
                }
294
295
                switch ($projectedProperty->getKind()) {
296
                    case ResourcePropertyKind::COMPLEX_TYPE:
297
                        $current->setTargetKind(TargetKind::COMPLEX_OBJECT());
298
                        break;
299
                    case ResourcePropertyKind::BAG | ResourcePropertyKind::PRIMITIVE:
300
                    case ResourcePropertyKind::BAG | ResourcePropertyKind::COMPLEX_TYPE:
301
                        $current->setTargetKind(TargetKind::BAG());
302
                        break;
303
                    case ResourcePropertyKind::RESOURCE_REFERENCE:
304
                    case ResourcePropertyKind::RESOURCESET_REFERENCE:
305
                        $current->setTargetKind(TargetKind::RESOURCE());
306
                        $prevResource = $previous->getTargetResourceType();
307
                        assert($prevResource instanceof ResourceEntityType);
0 ignored issues
show
Bug introduced by
The call to assert() has too few arguments starting with description. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

307
                        /** @scrutinizer ignore-call */ 
308
                        assert($prevResource instanceof ResourceEntityType);

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
308
                        $resourceSetWrapper = $this->providerWrapper->getResourceSetWrapperForNavigationProperty(
309
                            $previous->getTargetResourceSetWrapper(),
310
                            $prevResource,
311
                            $projectedProperty
312
                        );
313
                        if (null === $resourceSetWrapper) {
314
                            throw ODataException::createResourceNotFoundError($projectedProperty->getName());
315
                        }
316
317
                        $current->setTargetResourceSetWrapper($resourceSetWrapper);
318
                        break;
319
                    default:
320
                        if (!$projectedProperty->isKindOf(ResourcePropertyKind::PRIMITIVE)) {
0 ignored issues
show
Bug introduced by
POData\Providers\Metadat...PropertyKind::PRIMITIVE of type integer is incompatible with the type POData\Providers\Metadata\ResourcePropertyKind expected by parameter $kind of POData\Providers\Metadat...rceProperty::isKindOf(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

320
                        if (!$projectedProperty->isKindOf(/** @scrutinizer ignore-type */ ResourcePropertyKind::PRIMITIVE)) {
Loading history...
321
                            throw ODataException::createInternalServerError(
322
                                Messages::segmentParserUnExpectedPropertyKind('Primitive')
323
                            );
324
                        }
325
326
                        $current->setTargetKind(TargetKind::PRIMITIVE());
327
                        break;
328
                }
329
330
                if ($hasPredicate) {
331
                    $this->assertion(!$current->isSingleResult());
332
                    $keyDescriptor = $this->createKeyDescriptor(
333
                        $identifier . '(' . $keyPredicate . ')',
334
                        $projectedProperty->getResourceType(),
335
                        $keyPredicate
336
                    );
337
                    $current->setKeyDescriptor($keyDescriptor);
338
                    if (!$keyDescriptor->isEmpty()) {
339
                        $current->setSingleResult(true);
340
                    }
341
                }
342
343
                if ($checkRights && null !== $current->getTargetResourceSetWrapper()) {
344
                    $current->getTargetResourceSetWrapper()
345
                        ->checkResourceSetRightsForRead(
346
                            $current->isSingleResult()
347
                        );
348
                }
349
            }
350
        }
351
352
        return $current;
353
    }
354
355
    /**
356
     * Create SegmentDescriptor for the first segment.
357
     *
358
     * @param string $segmentIdentifier The identifier part of the first segment
359
     * @param string $keyPredicate      The predicate part of the first segment if any else NULL
360
     * @param bool   $checkRights       Whether to check the rights on this segment
361
     *
362
     * @throws ODataException Exception if any validation fails
363
     *
364
     * @return SegmentDescriptor Descriptor for the first segment
365
     */
366
    private function createFirstSegmentDescriptor($segmentIdentifier, $keyPredicate, $checkRights)
367
    {
368
        $descriptor = new SegmentDescriptor();
369
        $descriptor->setIdentifier($segmentIdentifier);
370
371 View Code Duplication
        if ($segmentIdentifier === ODataConstants::URI_METADATA_SEGMENT) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
372
            $this->assertion(null === $keyPredicate);
373
            $descriptor->setTargetKind(TargetKind::METADATA());
374
375
            return $descriptor;
376
        }
377
378 View Code Duplication
        if ($segmentIdentifier === ODataConstants::URI_BATCH_SEGMENT) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
379
            $this->assertion(null === $keyPredicate);
380
            $descriptor->setTargetKind(TargetKind::BATCH());
381
382
            return $descriptor;
383
        }
384
385
        if ($segmentIdentifier === ODataConstants::URI_COUNT_SEGMENT) {
386
            throw ODataException::createBadRequestError(
387
                Messages::segmentParserSegmentNotAllowedOnRoot(
388
                    ODataConstants::URI_COUNT_SEGMENT
389
                )
390
            );
391
        }
392
393
        if ($segmentIdentifier === ODataConstants::URI_LINK_SEGMENT) {
394
            throw ODataException::createBadRequestError(
395
                Messages::segmentParserSegmentNotAllowedOnRoot(
396
                    ODataConstants::URI_LINK_SEGMENT
397
                )
398
            );
399
        }
400
401
        $singleton = $this->providerWrapper->resolveSingleton($segmentIdentifier);
402
        if (null !== $singleton) {
403
            $this->assertion(null === $keyPredicate);
404
            $resourceType = $singleton->getResourceType();
405
            $typeName = Str::plural($resourceType->getName());
406
            $resourceSet = $this->providerWrapper->resolveResourceSet($typeName);
407
            assert($resourceSet instanceof ResourceSetWrapper);
0 ignored issues
show
Bug introduced by
The call to assert() has too few arguments starting with description. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

407
            /** @scrutinizer ignore-call */ 
408
            assert($resourceSet instanceof ResourceSetWrapper);

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
408
            $descriptor->setTargetKind(TargetKind::SINGLETON());
409
            $descriptor->setTargetSource(TargetSource::ENTITY_SET);
0 ignored issues
show
Bug introduced by
POData\UriProcessor\Reso...argetSource::ENTITY_SET of type integer is incompatible with the type POData\UriProcessor\Reso...mentParser\TargetSource expected by parameter $targetSource of POData\UriProcessor\Reso...ptor::setTargetSource(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

409
            $descriptor->setTargetSource(/** @scrutinizer ignore-type */ TargetSource::ENTITY_SET);
Loading history...
410
            $descriptor->setTargetResourceType($resourceType);
411
            $descriptor->setTargetResourceSetWrapper($resourceSet);
412
            $descriptor->setSingleResult(true);
413
414
            return $descriptor;
415
        }
416
417
        $resourceSetWrapper = $this->providerWrapper->resolveResourceSet($segmentIdentifier);
418
        if (null === $resourceSetWrapper) {
419
            throw ODataException::createResourceNotFoundError($segmentIdentifier);
420
        }
421
422
        $descriptor->setTargetResourceSetWrapper($resourceSetWrapper);
423
        $descriptor->setTargetResourceType($resourceSetWrapper->getResourceType());
424
        $descriptor->setTargetSource(TargetSource::ENTITY_SET);
425
        $descriptor->setTargetKind(TargetKind::RESOURCE());
426
        if (null !== $keyPredicate) {
427
            $keyDescriptor = $this->createKeyDescriptor(
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $keyDescriptor is correct as $this->createKeyDescript...eType(), $keyPredicate) targeting POData\UriProcessor\Reso...::createKeyDescriptor() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
428
                $segmentIdentifier . '(' . $keyPredicate . ')',
429
                $resourceSetWrapper->getResourceType(),
430
                $keyPredicate
431
            );
432
            $descriptor->setKeyDescriptor($keyDescriptor);
433
            if (!$keyDescriptor->isEmpty()) {
434
                $descriptor->setSingleResult(true);
435
            }
436
        }
437
438
        if ($checkRights) {
439
            $resourceSetWrapper->checkResourceSetRightsForRead(
440
                $descriptor->isSingleResult()
441
            );
442
        }
443
        return $descriptor;
444
    }
445
446
    /**
447
     * Creates an instance of KeyDescriptor by parsing a key predicate, also
448
     * validates the KeyDescriptor.
449
     *
450
     * @param string       $segment      The uri segment in the form identifier
451
     *                                   (keyPredicate)
452
     * @param ResourceType $resourceType The Resource type whose keys need to
453
     *                                   be parsed
454
     * @param string       $keyPredicate The key predicate to parse and generate
455
     *                                   KeyDescriptor for
456
     *
457
     * @throws ODataException Exception if any error occurs while parsing and
458
     *                        validating the key predicate
459
     *
460
     * @return KeyDescriptor|null Describes the key values in the $keyPredicate
461
     */
462
    private function createKeyDescriptor($segment, ResourceType $resourceType, $keyPredicate)
463
    {
464
        /**
465
         * @var KeyDescriptor
466
         */
467
        $keyDescriptor = null;
468
        if (!KeyDescriptor::tryParseKeysFromKeyPredicate($keyPredicate, $keyDescriptor)) {
469
            throw ODataException::createSyntaxError(Messages::syntaxError());
470
        }
471
472
        // Note: Currently WCF Data Service does not support multiple
473
        // 'Positional values' so Order_Details(10248, 11) is not valid
474
        if (!$keyDescriptor->isEmpty()
475
            && !$keyDescriptor->areNamedValues()
476
            && $keyDescriptor->valueCount() > 1
477
        ) {
478
            throw ODataException::createSyntaxError(
479
                Messages::segmentParserKeysMustBeNamed($segment)
480
            );
481
        }
482
        $keyDescriptor->validate($segment, $resourceType);
483
484
        return $keyDescriptor;
485
    }
486
487
    /**
488
     * Assert that the given condition is true, if false throw
489
     * ODataException for syntax error.
490
     *
491
     * @param bool $condition The condition to assert
492
     *
493
     * @throws ODataException
494
     */
495
    private function assertion($condition)
496
    {
497
        if (!$condition) {
498
            throw ODataException::createSyntaxError(Messages::syntaxError());
499
        }
500
    }
501
}
502