Completed
Push — develop ( c1be74...f8bbf2 )
by David
06:55
created

Sync_Service::request_cancel()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 5
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Wordlift\Dataset;
4
5
use Wordlift\Api\Api_Service;
6
use Wordlift\Jsonld\Jsonld_Service;
7
use Wordlift\Object_Type_Enum;
8
9
class Sync_Service {
10
11
	/**
12
	 * @var \Wordlift_Log_Service
13
	 */
14
	private $log;
15
16
	/**
17
	 * @var Api_Service
18
	 */
19
	private $api_service;
20
21
	/**
22
	 * @var Jsonld_Service
23
	 */
24
	private $jsonld_service;
25
26
	/**
27
	 * @var Sync_Background_Process
28
	 */
29
	private $sync_background_process;
30
31
	/**
32
	 * The number of posts processed in one call.
33
	 *
34
	 * @var int The batch size.
35
	 */
36
	private $batch_size;
37
38
	/**
39
	 * @var Sync_Object_Adapter_Factory
40
	 */
41
	private $sync_object_adapter_factory;
42
43
	/**
44
	 * @var Sync_Service
45
	 */
46
	private static $instance;
47
48
	/**
49
	 * Constructor.
50
	 *
51
	 * @param Api_Service $api_service The {@link Api_Service} used to communicate with the remote APIs.
52
	 * @param Sync_Object_Adapter_Factory $sync_object_adapter_factory
53
	 */
54
	public function __construct( $api_service, $sync_object_adapter_factory ) {
55
56
		$this->log = \Wordlift_Log_Service::get_logger( get_class() );
57
58
		$this->api_service                 = $api_service;
59
		$this->sync_object_adapter_factory = $sync_object_adapter_factory;
60
		$this->batch_size                  = 10;
61
62
		// You need to initialize this early, otherwise the Background Process isn't registered in AJAX calls.
63
		$this->sync_background_process = new Sync_Background_Process( $this );;
64
65
		self::$instance = $this;
66
67
	}
68
69
	public static function get_instance() {
70
		return self::$instance;
71
	}
72
73
	/**
74
	 * Starts a new synchronization.
75
	 */
76
	public function start() {
77
78
		// Create the Sync_Background_Process.
79
		$this->sync_background_process->start();
80
81
	}
82
83
	/**
84
	 * Request to cancel a background process.
85
	 */
86
	public function request_cancel() {
87
88
		$this->sync_background_process->request_cancel();
89
90
	}
91
92
	/**
93
	 * Get the next post IDs to synchronize.
94
	 *
95
	 * @return array An array of post IDs.
96
	 */
97
	public function next() {
98
		global $wpdb;
99
100
		$state = $this->info();
101
102
		// Limit the query to the allowed post types.
103
		$post_type_in = implode( "','", array_map( 'esc_sql', \Wordlift_Entity_Service::valid_entity_post_types() ) );
104
105
		// Get the next post ID.
106
		return $wpdb->get_col( "
107
			SELECT p.ID
108
			FROM $wpdb->posts p
109
			WHERE p.post_status = 'publish'
110
			  AND p.post_type IN ('$post_type_in')
111
			ORDER BY p.ID
112
			LIMIT {$state->index},{$this->batch_size}
113
			" );
114
	}
115
116
	public function count() {
117
		global $wpdb;
118
		$post_type_in = implode( "','", array_map( 'esc_sql', \Wordlift_Entity_Service::valid_entity_post_types() ) );
119
120
		return $wpdb->get_var( "
121
			SELECT COUNT(1)
122
			FROM $wpdb->posts p
123
			WHERE p.post_status = 'publish'
124
			  AND p.post_type IN ('$post_type_in')
125
			" );
126
	}
127
128
	public function info() {
129
		return Sync_Background_Process::get_state();
130
	}
131
132
	/**
133
	 * @param $type
134
	 * @param $post_id
135
	 *
136
	 * @throws \Exception
137
	 */
138
	public function sync_one( $type, $post_id ) {
139
140
		if ( Object_Type_Enum::POST !== $type ) {
141
			throw new \Exception( "Type $type is unsupported." );
142
		}
143
144
		$this->sync_items( array( $post_id ), false );
145
	}
146
147
	public function sync_items( $post_ids, $clear = true ) {
148
149
		$this->log->debug( sprintf( 'Synchronizing post(s) %s...', implode( ', ', $post_ids ) ) );
150
151
		debug_print_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS, 10 );
152
153
		// If we're starting the sync, try to clear the dataset.
154
		if ( $clear && 0 === $this->info()->index ) {
155
			$this->api_service->request( 'DELETE', '/middleware/dataset/delete' );
156
		}
157
158
		$that         = $this;
159
		$request_body = array_filter( array_map( function ( $post_id ) use ( $that ) {
160
			// Check if the post type is public.
161
			$post_type     = get_post_type( $post_id );
162
			$post_type_obj = get_post_type_object( $post_type );
163
			if ( ! $post_type_obj->public ) {
164
				return false;
165
			}
166
167
			$is_private       = ( 'publish' !== get_post_status( $post_id ) );
168
			$uri              = get_post_meta( $post_id, 'entity_url', true );
169
			$object_adapter   = $that->sync_object_adapter_factory->create( Object_Type_Enum::POST, $post_id );
170
			$jsonld           = $object_adapter->get_jsonld_and_update_hash();
171
			$jsonld_as_string = wp_json_encode( $jsonld );
172
173
			$that->log->trace( "Posting JSON-LD:\n$jsonld_as_string" );
174
175
			return array(
176
				'uri'     => $uri,
177
				'model'   => $jsonld_as_string,
178
				'private' => $is_private
179
			);
180
		}, $post_ids ) );
181
182
		// There's no point in making a request if the request is empty.
183
		if ( empty( $request_body ) ) {
184
			return true;
185
		}
186
187
		// Make a request to the remote endpoint.
188
		$state              = $this->info();
189
		$state_header_value = str_replace( "\n", '', wp_json_encode( $state ) );
190
		$response           = $this->api_service->request(
191
			'POST', '/middleware/dataset/batch',
192
			array(
193
				'Content-Type'                     => 'application/json',
194
				'X-Wordlift-Dataset-Sync-State-V1' => $state_header_value
195
			),
196
			wp_json_encode( $request_body ) );
197
198
		$this->log->debug( "Response received: " . ( $response->is_success() ? 'yes' : 'no' ) );
199
200
		// Update the sync date in case of success, otherwise log an error.
201
		if ( $response->is_success() ) {
202
203
			foreach ( $post_ids as $post_id ) {
204
				update_post_meta( $post_id, '_wl_synced_gmt', current_time( 'mysql', true ) );
205
			}
206
207
			$this->log->debug( sprintf( 'Posts %s synchronized.', implode( ', ', $post_ids ) ) );
208
209
			return true;
210
		} else {
211
			// @@todo: should we put a limit retry here?
212
			$response_dump = var_export( $response, true );
213
			$this->log->error(
0 ignored issues
show
Bug introduced by
The call to error() misses a required argument $exception.

This check looks for function calls that miss required arguments.

Loading history...
214
				sprintf( 'An error occurred while synchronizing the data for post IDs %s: %s', implode( ', ', $post_ids ), $response_dump ) );
215
216
			return false;
217
		}
218
219
	}
220
221
	/**
222
	 * @param $post_id
223
	 *
224
	 * @todo Complete the delete item.
225
	 */
226
	public function delete_item( $post_id ) {
227
		$uri = get_post_meta( $post_id, 'entity_url', true );
228
		// Make a request to the remote endpoint.
229
		$state_header_value = str_replace( wp_json_encode( $this->info() ), "\n", '' );
230
		$response           = $this->api_service->request(
0 ignored issues
show
Unused Code introduced by
$response is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
231
			'DELETE', '/middleware/dataset?uri=' . rawurlencode( $uri ),
232
			array(
233
				'Content-Type'                     => 'application/ld+json',
234
				'X-Wordlift-Dataset-Sync-State-V1' => $state_header_value
235
			) );
236
	}
237
238
	public function get_batch_size() {
239
240
		return $this->batch_size;
241
	}
242
243
	public function delete_one($type, $object_id) {
0 ignored issues
show
Unused Code introduced by
The parameter $type is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $object_id is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
244
245
		// @@todo implement.
246
247
	}
248
249
}
250