Passed
Pull Request — master (#93)
by Maximilian
02:52
created

Request   A

Complexity

Total Complexity 14

Size/Duplication

Total Lines 122
Duplicated Lines 0 %

Test Coverage

Coverage 95.24%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 14
eloc 50
c 1
b 0
f 0
dl 0
loc 122
ccs 20
cts 21
cp 0.9524
rs 10

2 Methods

Rating   Name   Duplication   Size   Complexity  
A getApplicationId() 0 10 6
B fromAmazonRequest() 0 23 8
1
<?php
2
3
namespace MaxBeckers\AmazonAlexa\Request;
4
5
use MaxBeckers\AmazonAlexa\Exception\MissingRequestDataException;
6
use MaxBeckers\AmazonAlexa\Exception\MissingRequiredHeaderException;
7
use MaxBeckers\AmazonAlexa\Helper\PropertyHelper;
8
use MaxBeckers\AmazonAlexa\Request\Request\AbstractRequest;
9
use MaxBeckers\AmazonAlexa\Request\Request\AlexaSkillEvent\SkillAccountLinkedRequest;
10
use MaxBeckers\AmazonAlexa\Request\Request\AlexaSkillEvent\SkillDisabledRequest;
11
use MaxBeckers\AmazonAlexa\Request\Request\AlexaSkillEvent\SkillEnabledRequest;
12
use MaxBeckers\AmazonAlexa\Request\Request\AlexaSkillEvent\SkillPermissionAcceptedRequest;
13
use MaxBeckers\AmazonAlexa\Request\Request\AlexaSkillEvent\SkillPermissionChangedRequest;
14
use MaxBeckers\AmazonAlexa\Request\Request\AudioPlayer\PlaybackFailedRequest;
15
use MaxBeckers\AmazonAlexa\Request\Request\AudioPlayer\PlaybackFinishedRequest;
16
use MaxBeckers\AmazonAlexa\Request\Request\AudioPlayer\PlaybackNearlyFinishedRequest;
17
use MaxBeckers\AmazonAlexa\Request\Request\AudioPlayer\PlaybackStartedRequest;
18
use MaxBeckers\AmazonAlexa\Request\Request\AudioPlayer\PlaybackStoppedRequest;
19
use MaxBeckers\AmazonAlexa\Request\Request\CanFulfill\CanFulfillIntentRequest;
20
use MaxBeckers\AmazonAlexa\Request\Request\Display\ElementSelectedRequest;
21
use MaxBeckers\AmazonAlexa\Request\Request\GameEngine\InputHandlerEvent;
22
use MaxBeckers\AmazonAlexa\Request\Request\PlaybackController\NextCommandIssued;
23
use MaxBeckers\AmazonAlexa\Request\Request\PlaybackController\PauseCommandIssued;
24
use MaxBeckers\AmazonAlexa\Request\Request\PlaybackController\PlayCommandIssued;
25
use MaxBeckers\AmazonAlexa\Request\Request\PlaybackController\PreviousCommandIssued;
26
use MaxBeckers\AmazonAlexa\Request\Request\Standard\IntentRequest;
27
use MaxBeckers\AmazonAlexa\Request\Request\Standard\LaunchRequest;
28
use MaxBeckers\AmazonAlexa\Request\Request\Standard\SessionEndedRequest;
29
use MaxBeckers\AmazonAlexa\Request\Request\System\ConnectionsResponseRequest;
30
use MaxBeckers\AmazonAlexa\Request\Request\System\ExceptionEncounteredRequest;
31
32
/**
33
 * @author Maximilian Beckers <[email protected]>
34
 */
35
class Request
36
{
37
    /**
38
     * List of all supported amazon request types.
39
     */
40
    const REQUEST_TYPES = [
41
        // Standard types
42
        IntentRequest::TYPE       => IntentRequest::class,
43
        LaunchRequest::TYPE       => LaunchRequest::class,
44
        SessionEndedRequest::TYPE => SessionEndedRequest::class,
45
        // AudioPlayer types
46
        PlaybackStartedRequest::TYPE        => PlaybackStartedRequest::class,
47
        PlaybackNearlyFinishedRequest::TYPE => PlaybackNearlyFinishedRequest::class,
48
        PlaybackFinishedRequest::TYPE       => PlaybackFinishedRequest::class,
49
        PlaybackStoppedRequest::TYPE        => PlaybackStoppedRequest::class,
50
        PlaybackFailedRequest::TYPE         => PlaybackFailedRequest::class,
51
        // PlaybackController types
52
        NextCommandIssued::TYPE     => NextCommandIssued::class,
53
        PauseCommandIssued::TYPE    => PauseCommandIssued::class,
54
        PlayCommandIssued::TYPE     => PlayCommandIssued::class,
55
        PreviousCommandIssued::TYPE => PreviousCommandIssued::class,
56
        // System types
57
        ExceptionEncounteredRequest::TYPE => ExceptionEncounteredRequest::class,
58
        // Display types
59
        ElementSelectedRequest::TYPE => ElementSelectedRequest::class,
60
        // Game engine types
61
        InputHandlerEvent::TYPE => InputHandlerEvent::class,
62
        // can fulfill intent
63
        CanFulfillIntentRequest::TYPE => CanFulfillIntentRequest::class,
64
        // Connections Response Request
65
        ConnectionsResponseRequest::TYPE => ConnectionsResponseRequest::class,
66
        // Skill event types
67
        SkillAccountLinkedRequest::TYPE      => SkillAccountLinkedRequest::class,
68
        SkillEnabledRequest::TYPE            => SkillEnabledRequest::class,
69
        SkillDisabledRequest::TYPE           => SkillDisabledRequest::class,
70
        SkillPermissionAcceptedRequest::TYPE => SkillPermissionAcceptedRequest::class,
71
        SkillPermissionChangedRequest::TYPE  => SkillPermissionChangedRequest::class,
72
    ];
73
74
    /**
75
     * @var string|null
76
     */
77
    public $version;
78
79
    /**
80
     * @var Session|null
81
     */
82
    public $session;
83
84
    /**
85
     * @var Context|null
86
     */
87
    public $context;
88
89
    /**
90
     * @var AbstractRequest|null
91
     */
92
    public $request;
93
94
    /**
95
     * @var string
96
     */
97
    public $amazonRequestBody;
98
99
    /**
100
     * @var string
101
     */
102
    public $signatureCertChainUrl;
103
104
    /**
105
     * @var string
106
     */
107
    public $signature;
108
109
    /**
110
     * @param string $amazonRequestBody
111
     * @param string $signatureCertChainUrl
112
     * @param string $signature
113
     *
114
     * @throws MissingRequestDataException
115
     * @throws MissingRequiredHeaderException
116
     *
117
     * @return Request
118
     */
119 42
    public static function fromAmazonRequest(string $amazonRequestBody, string $signatureCertChainUrl, string $signature): self
120
    {
121 42
        $request = new self();
122
123 42
        $request->signatureCertChainUrl = $signatureCertChainUrl;
124 42
        $request->signature             = $signature;
125 42
        $request->amazonRequestBody     = $amazonRequestBody;
126 42
        $amazonRequest                  = (array)json_decode($amazonRequestBody, true);
127
128 42
        $request->version = PropertyHelper::checkNullValue($amazonRequest, 'version');
129 42
        $request->session = isset($amazonRequest['session']) ? Session::fromAmazonRequest($amazonRequest['session']) : null;
130 42
        $request->context = isset($amazonRequest['context']) ? Context::fromAmazonRequest($amazonRequest['context']) : null;
131
132 42
        if (!isset($amazonRequest['request']['type']) || !isset(self::REQUEST_TYPES[$amazonRequest['request']['type']])) {
133 1
            throw new MissingRequestDataException();
134
        }
135 41
        $request->request = (self::REQUEST_TYPES[$amazonRequest['request']['type']])::fromAmazonRequest($amazonRequest['request']);
136
137 41
        if ($request->request->validateSignature() && (!$request->signatureCertChainUrl || !$request->signature)) {
138 1
            throw new MissingRequiredHeaderException();
139
        }
140
141 40
        return $request;
142
    }
143
144
    /**
145
     * @return string|null
146
     */
147 3
    public function getApplicationId()
148
    {
149
        // workaround for developer console
150 3
        if ($this->session && $this->session->application) {
151 1
            return $this->session->application->applicationId;
152 2
        } elseif ($this->context && ($system = $this->context->system) && $system->application) {
153 2
            return $system->application->applicationId;
154
        }
155
156
        return null;
157
    }
158
}
159