repo = new ExportRepository(); } /** * Registers routes. * * @return void */ public function register_routes() { $this->route( '/exports', array( array( 'methods' => \WP_REST_Server::CREATABLE, 'callback' => array( $this, 'create' ), 'permission_callback' => array( Auth::class, 'can_read' ), 'args' => array( 'spec' => array( 'required' => true, 'type' => 'object', ), 'background' => array( 'type' => 'boolean', 'default' => false, ), ), ), array( 'methods' => \WP_REST_Server::READABLE, 'callback' => array( $this, 'list_exports' ), 'permission_callback' => array( Auth::class, 'can_read' ), 'args' => array( 'page' => $this->int_param( false, 1 ), 'per_page' => array( 'type' => 'integer', 'default' => 20, 'minimum' => 1, 'maximum' => 100, ), ), ), ) ); $this->route( '/exports/(?P\d+)', array( array( 'methods' => \WP_REST_Server::READABLE, 'callback' => array( $this, 'get_export' ), 'permission_callback' => array( Auth::class, 'can_read' ), 'args' => array( 'id' => $this->int_param( true ) ), ), array( 'methods' => \WP_REST_Server::DELETABLE, 'callback' => array( $this, 'delete' ), 'permission_callback' => array( Auth::class, 'can_read' ), 'args' => array( 'id' => $this->int_param( true ) ), ), ) ); } /** * Presents an export. * * @param array $export Export. * @return array */ private function present( array $export ) { $summary = ''; try { $summary = SpecSummary::describe( \Recast\WCS\Cohort\CohortSpec::from_array( $export['spec'] ) ); } catch ( \Exception $e ) { $summary = ''; } return array( 'id' => $export['id'], 'mode' => $export['mode'], 'status' => $export['status'], 'rows_written' => $export['rows_written'], 'candidate_total' => $export['candidate_total'], 'file_size' => $export['file_size'], 'spec' => $export['spec'], 'spec_summary' => $summary, 'download_url' => in_array( $export['status'], array( ExportRepository::STATUS_READY, ExportRepository::STATUS_PENDING ), true ) || 'stream' === $export['mode'] ? DownloadHandler::url( $export['id'] ) : null, 'created_at' => $export['created_at'], 'finished_at' => $export['finished_at'], 'expires_at' => $export['expires_at'], 'error' => ExportRepository::STATUS_FAILED === $export['status'] ? $export['error'] : null, ); } /** * POST /exports. * * @param \WP_REST_Request $request Request. * @return \WP_REST_Response|\WP_Error */ public function create( \WP_REST_Request $request ) { $spec = CohortController::spec_from_request( $request ); if ( is_wp_error( $spec ) ) { return $spec; } $count = ( new Counter() )->count( $spec ); $threshold = (int) Settings::get( 'recast_export_sync_threshold' ); $background = (bool) $request['background'] || ! $count['exact'] || (int) $count['count'] > $threshold; if ( $background && ! Scheduler::available() ) { $background = false; } $ttl = max( 1, (int) Settings::get( 'recast_export_retention_hours' ) ); $id = $this->repo->insert( array( 'spec_json' => $spec->to_json(), 'spec_hash' => $spec->hash(), 'mode' => $background ? 'background' : 'stream', 'status' => ExportRepository::STATUS_PENDING, 'candidate_total' => $count['exact'] ? (int) $count['count'] : null, 'created_by' => get_current_user_id(), 'expires_at' => gmdate( 'Y-m-d H:i:s', time() + $ttl * HOUR_IN_SECONDS ), ) ); if ( $background ) { BackgroundExporter::start( $id ); } return $this->respond( $this->present( $this->repo->get( $id ) ), 201 ); } /** * GET /exports. * * @param \WP_REST_Request $request Request. * @return \WP_REST_Response */ public function list_exports( \WP_REST_Request $request ) { $result = $this->repo->list( (int) $request['page'], (int) $request['per_page'], ExportRepository::viewer_scope() ); return $this->respond( array( 'exports' => array_map( array( $this, 'present' ), $result['exports'] ), 'total' => $result['total'], ) ); } /** * GET /exports/{id}. * * @param \WP_REST_Request $request Request. * @return \WP_REST_Response|\WP_Error */ public function get_export( \WP_REST_Request $request ) { $export = $this->repo->get( (int) $request['id'] ); if ( ! $export || ! ExportRepository::viewable( $export ) ) { return $this->error( 'recast_export_not_found', __( 'Export not found.', 'recast-subscriptions' ), 404 ); } return $this->respond( $this->present( $export ) ); } /** * DELETE /exports/{id}. * * @param \WP_REST_Request $request Request. * @return \WP_REST_Response|\WP_Error */ public function delete( \WP_REST_Request $request ) { $export = $this->repo->get( (int) $request['id'] ); if ( ! $export || ! ExportRepository::viewable( $export ) ) { return $this->error( 'recast_export_not_found', __( 'Export not found.', 'recast-subscriptions' ), 404 ); } Storage::delete( $export['file_name'] ); $this->repo->delete( $export['id'] ); return $this->respond( array( 'deleted' => true ) ); } }