Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
81.48% covered (warning)
81.48%
132 / 162
14.29% covered (danger)
14.29%
2 / 14
CRAP
0.00% covered (danger)
0.00%
0 / 1
StagingApi
81.48% covered (warning)
81.48%
132 / 162
14.29% covered (danger)
14.29%
2 / 14
17.63
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 register_routes
98.94% covered (success)
98.94%
93 / 94
0.00% covered (danger)
0.00%
0 / 1
1
 checkPermission
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 createStaging
0.00% covered (danger)
0.00%
0 / 17
0.00% covered (danger)
0.00%
0 / 1
6
 cloneProductionToStaging
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 deployToProduction
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getDeployStatus
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 advanceContentCopy
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 getContentCopyStatus
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getCopyTypeArgs
92.86% covered (success)
92.86%
13 / 14
0.00% covered (danger)
0.00%
0 / 1
1.00
 getDeployTypeArgs
88.89% covered (warning)
88.89%
8 / 9
0.00% covered (danger)
0.00%
0 / 1
1.00
 destroyStaging
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getStagingDetails
93.75% covered (success)
93.75%
15 / 16
0.00% covered (danger)
0.00%
0 / 1
2.00
 switchTo
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3namespace NewfoldLabs\WP\Module\Staging;
4
5use NewfoldLabs\WP\ModuleLoader\Container;
6use function NewfoldLabs\WP\ModuleLoader\container;
7
8/**
9 * Class StagingApi
10 */
11class StagingApi extends \WP_REST_Controller {
12
13    /**
14     * The namespace of this controller's route.
15     *
16     * @var string
17     */
18    protected $namespace = 'newfold-staging/v1';
19
20    /**
21     * An instance of the Staging class.
22     *
23     * @var Staging
24     */
25    protected $staging;
26
27    /**
28     * Staging_Controller constructor.
29     *
30     * @param Container $container An instance of the Container class.
31     */
32    public function __construct( Container $container ) {
33        $this->container = $container;
34        $this->staging   = new Staging( $this->container );
35    }
36
37    /**
38     * Registers the routes for the objects of the controller.
39     */
40    public function register_routes() {
41
42        register_rest_route(
43            $this->namespace,
44            '/staging',
45            array(
46                array(
47                    'methods'             => \WP_REST_Server::READABLE,
48                    'callback'            => array( $this, 'getStagingDetails' ),
49                    'permission_callback' => array( $this, 'checkPermission' ),
50                ),
51                array(
52                    'methods'             => \WP_REST_Server::CREATABLE,
53                    'callback'            => array( $this, 'createStaging' ),
54                    'permission_callback' => array( $this, 'checkPermission' ),
55                ),
56                array(
57                    'methods'             => \WP_REST_Server::DELETABLE,
58                    'callback'            => array( $this, 'destroyStaging' ),
59                    'permission_callback' => array( $this, 'checkPermission' ),
60                ),
61            )
62        );
63
64        register_rest_route(
65            $this->namespace,
66            '/staging/clone',
67            array(
68                array(
69                    'methods'             => \WP_REST_Server::EDITABLE,
70                    'callback'            => array( $this, 'cloneProductionToStaging' ),
71                    'permission_callback' => array( $this, 'checkPermission' ),
72                ),
73            )
74        );
75
76        register_rest_route(
77            $this->namespace,
78            '/staging/deploy',
79            array(
80                array(
81                    'methods'             => \WP_REST_Server::EDITABLE,
82                    'callback'            => array( $this, 'deployToProduction' ),
83                    'permission_callback' => array( $this, 'checkPermission' ),
84                    'args'                => $this->getDeployTypeArgs(),
85                ),
86                array(
87                    'methods'             => \WP_REST_Server::READABLE,
88                    'callback'            => array( $this, 'getDeployStatus' ),
89                    'permission_callback' => array( $this, 'checkPermission' ),
90                    'args'                => $this->getDeployTypeArgs(),
91                ),
92            )
93        );
94
95        register_rest_route(
96            $this->namespace,
97            '/staging/copy/advance',
98            array(
99                array(
100                    'methods'             => \WP_REST_Server::CREATABLE,
101                    'callback'            => array( $this, 'advanceContentCopy' ),
102                    'permission_callback' => array( $this, 'checkPermission' ),
103                    'args'                => $this->getCopyTypeArgs(),
104                ),
105            )
106        );
107
108        register_rest_route(
109            $this->namespace,
110            '/staging/copy/status',
111            array(
112                array(
113                    'methods'             => \WP_REST_Server::READABLE,
114                    'callback'            => array( $this, 'getContentCopyStatus' ),
115                    'permission_callback' => array( $this, 'checkPermission' ),
116                    'args'                => $this->getCopyTypeArgs(),
117                ),
118            )
119        );
120
121        register_rest_route(
122            $this->namespace,
123            '/staging/switch-to',
124            array(
125                array(
126                    'methods'             => \WP_REST_Server::READABLE,
127                    'callback'            => array( $this, 'switchTo' ),
128                    'permission_callback' => array( $this, 'checkPermission' ),
129                    'args'                => array(
130                        'env' => array(
131                            'required'          => true,
132                            'enum'              => array( 'staging', 'production' ),
133                            'validate_callback' => function ( $value ) {
134                                return in_array( $value, array( 'staging', 'production' ), true );
135                            },
136                        ),
137                    ),
138                ),
139            )
140        );
141    }
142
143    /**
144     * Check permissions for routes.
145     *
146     * @return bool
147     */
148    public function checkPermission() {
149        return (bool) current_user_can( 'manage_options' );
150    }
151
152    /**
153     * Create a staging environment.
154     *
155     * @return \WP_REST_Response
156     */
157    public function createStaging() {
158
159        $payload = $this->staging->createStaging();
160        if ( ! is_wp_error( $payload ) ) {
161            $this->staging->getConfig( false );
162            $payload = array(
163                'creationDate'           => $this->staging->getCreationDate(),
164                'currentEnvironment'     => $this->staging->getEnvironment(),
165                'message'                => $payload['message'],
166                'productionDir'          => $this->staging->getProductionDir(),
167                'productionThumbnailUrl' => $this->staging->getProductionScreenshotUrl(),
168                'productionUrl'          => $this->staging->getProductionUrl(),
169                'stagingDir'             => $this->staging->getStagingDir(),
170                'stagingExists'          => $this->staging->stagingExists(),
171                'stagingThumbnailUrl'    => $this->staging->getStagingScreenshotUrl(),
172                'stagingUrl'             => $this->staging->getStagingUrl(),
173                'status'                 => $payload['status'],
174            );
175        }
176
177        return rest_ensure_response( $payload );
178    }
179
180    /**
181     * Clone production environment to staging.
182     *
183     * @return \WP_REST_Response
184     */
185    public function cloneProductionToStaging() {
186        return rest_ensure_response( $this->staging->cloneProductionToStaging() );
187    }
188
189    /**
190     * Deploy files and/or database from staging to production.
191     *
192     * @param \WP_REST_Request $request WordPress HTTP request model.
193     *
194     * @return \WP_REST_Response
195     */
196    public function deployToProduction( \WP_REST_Request $request ) {
197        return rest_ensure_response( $this->staging->deployToProduction( $request->get_param( 'type' ) ) );
198    }
199
200    /**
201     * Poll deploy progress (used when deploy runs asynchronously).
202     *
203     * @param \WP_REST_Request $request WordPress HTTP request model.
204     *
205     * @return \WP_REST_Response
206     */
207    public function getDeployStatus( \WP_REST_Request $request ) {
208        return rest_ensure_response( $this->staging->getDeployStatus( $request->get_param( 'type' ) ) );
209    }
210
211    /**
212     * Advance the stepped content-copy deploy by one bounded unit.
213     *
214     * @param \WP_REST_Request $request WordPress HTTP request model.
215     *
216     * @return \WP_REST_Response
217     */
218    public function advanceContentCopy( \WP_REST_Request $request ) {
219        return rest_ensure_response(
220            $this->staging->advanceContentCopy( $request->get_param( 'type' ), (bool) $request->get_param( 'restart' ) )
221        );
222    }
223
224    /**
225     * Poll the stepped content-copy deploy progress without advancing it.
226     *
227     * @param \WP_REST_Request $request WordPress HTTP request model.
228     *
229     * @return \WP_REST_Response
230     */
231    public function getContentCopyStatus( \WP_REST_Request $request ) {
232        return rest_ensure_response( $this->staging->getContentCopyStatus( $request->get_param( 'type' ) ) );
233    }
234
235    /**
236     * Shared REST args for copy type on the content-copy advance and status routes.
237     *
238     * @return array
239     */
240    protected function getCopyTypeArgs() {
241        return array(
242            'type'    => array(
243                'default'           => 'deploy_files',
244                'enum'              => array( 'deploy_files' ),
245                'validate_callback' => function ( $value ) {
246                    return in_array( $value, array( 'deploy_files' ), true );
247                },
248            ),
249            // Advance only: true on the first call of a new deploy to discard any prior job's state so a
250            // previous run's terminal 'done'/'error' does not short-circuit it. Ignored by status.
251            'restart' => array(
252                'default'           => false,
253                'type'              => 'boolean',
254                'sanitize_callback' => 'rest_sanitize_boolean',
255            ),
256        );
257    }
258
259    /**
260     * Shared REST args for deploy type on POST and GET deploy routes.
261     *
262     * @return array
263     */
264    protected function getDeployTypeArgs() {
265        return array(
266            'type' => array(
267                'default'           => 'all',
268                'enum'              => array( 'all', 'db', 'files' ),
269                'validate_callback' => function ( $value ) {
270                    return in_array( $value, array( 'all', 'db', 'files' ), true );
271                },
272            ),
273        );
274    }
275
276    /**
277     * Destroy the staging environment.
278     *
279     * @return \WP_REST_Response
280     */
281    public function destroyStaging() {
282        return rest_ensure_response( $this->staging->destroyStaging() );
283    }
284
285    /**
286     * Get all staging details.
287     *
288     * @return \WP_REST_Response
289     */
290    public function getStagingDetails() {
291        $health = new StagingHealthCheck( $this->staging );
292        if ( $health->maybe_repair() ) {
293            $this->staging->getConfig( false );
294        }
295
296        return rest_ensure_response(
297            array(
298                'creationDate'           => $this->staging->getCreationDate(),
299                'currentEnvironment'     => $this->staging->getEnvironment(),
300                'productionDir'          => $this->staging->getProductionDir(),
301                'productionThumbnailUrl' => $this->staging->getProductionScreenshotUrl(),
302                'productionUrl'          => $this->staging->getProductionUrl(),
303                'stagingDir'             => $this->staging->getStagingDir(),
304                'stagingExists'          => $this->staging->stagingExists(),
305                'stagingThumbnailUrl'    => $this->staging->getStagingScreenshotUrl(),
306                'stagingUrl'             => $this->staging->getStagingUrl(),
307            )
308        );
309    }
310
311    /**
312     * Switch to the production environment.
313     *
314     * @param \WP_REST_Request $request WordPress HTTP request model.
315     *
316     * @return \WP_REST_Response
317     */
318    public function switchTo( \WP_REST_Request $request ) {
319        return rest_ensure_response( $this->staging->switchTo( $request->get_param( 'env' ) ) );
320    }
321}