PluginProbe
Product Labels, Quick View, Buy Now, Pre-Orders, Frequently Bought Together & More for WooCommerce – Merchant / 2.3.0
Product Labels, Quick View, Buy Now, Pre-Orders, Frequently Bought Together & More for WooCommerce – Merchant v2.3.0
2.3.2 2.3.1 2.3.0 2.2.8 2.2.7 trunk 1.10.0 1.10.1 1.10.2 1.10.3 1.10.4 1.10.5 1.11.0 1.11.1 1.11.2 1.6 1.7 1.8 1.8.1 1.8.2 1.8.3 1.9.0 1.9.1 1.9.10 1.9.11 All 60 releases
merchant / inc / abilities / class-merchant-abilities-registry.php

class-merchant-abilities-registry.php in Product Labels, Quick View, Buy Now, Pre-Orders, Frequently Bought Together & More for WooCommerce – Merchant 2.3.0, at inc/abilities/class-merchant-abilities-registry.php

787 lines 25.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Merchant Abilities Registry.
4 *
5 * Registers all gateway abilities with WordPress and manages
6 * module adapter resolution.
7 *
8 * @package Merchant
9 * @since 2.3.0
10 */
11
12 if ( ! defined( 'ABSPATH' ) ) {
13 exit;
14 }
15
16 /**
17 * Merchant_Abilities_Registry
18 *
19 * Central registry that registers all Merchant abilities with the
20 * WP Abilities API and resolves module adapters for campaign
21 * summary formatting.
22 *
23 * @since 2.3.0
24 */
25 class Merchant_Abilities_Registry {
26
27 /**
28 * Module IDs excluded from all ability surfaces.
29 *
30 * Single source of truth — handlers reference this
31 * instead of maintaining their own copies.
32 *
33 * @var array<int, string>
34 */
35 private static $excluded_module_ids = array( 'global-settings' );
36
37 /**
38 * Check if a module is excluded from abilities.
39 *
40 * @param string $module_id The module identifier.
41 *
42 * @return bool True if excluded.
43 */
44 public static function is_excluded( $module_id ) {
45 return in_array( $module_id, self::$excluded_module_ids, true );
46 }
47
48 /**
49 * Schema generator instance.
50 *
51 * @var Merchant_Schema_Generator
52 */
53 private $schema_generator;
54
55 /**
56 * Field validator instance.
57 *
58 * @var Merchant_Field_Validator
59 */
60 private $field_validator;
61
62 /**
63 * Campaign resolver instance.
64 *
65 * @var Merchant_Campaign_Resolver
66 */
67 private $campaign_resolver;
68
69 /**
70 * Registered module adapters.
71 *
72 * @var array<string, Merchant_Module_Abilities_Interface>
73 */
74 private $adapters = array();
75
76 /**
77 * Ability definitions contributed by other plugins (e.g. merchant-pro).
78 *
79 * @var array<lowercase-string&non-falsy-string, array<string, mixed>>
80 */
81 private $contributed = array();
82
83 /**
84 * Constructor.
85 *
86 * @param Merchant_Schema_Generator $schema_generator Schema generator instance.
87 * @param Merchant_Field_Validator $field_validator Field validator instance.
88 * @param Merchant_Campaign_Resolver $campaign_resolver Campaign resolver instance.
89 */
90 public function __construct( $schema_generator, $field_validator, $campaign_resolver ) {
91 $this->schema_generator = $schema_generator;
92 $this->field_validator = $field_validator;
93 $this->campaign_resolver = $campaign_resolver;
94 }
95
96 /**
97 * Register all 10 gateway abilities with WordPress, followed by any
98 * abilities contributed via register_ability_definition().
99 *
100 * @return void
101 */
102 public function register_all_abilities() {
103 $abilities = $this->get_ability_definitions();
104
105 foreach ( $abilities as $ability_id => $definition ) {
106 wp_register_ability( $ability_id, $definition );
107 }
108
109 foreach ( $this->contributed as $ability_id => $definition ) {
110 wp_register_ability( $ability_id, $definition );
111 }
112 }
113
114 /**
115 * Contribute an ability definition to be registered alongside the core map.
116 *
117 * Lets other plugins (e.g. merchant-pro) add abilities without
118 * modifying the core definitions. Contributions are deduped by id;
119 * registering the same id again overwrites the previous definition.
120 *
121 * @param lowercase-string&non-falsy-string $id The ability identifier.
122 * @param array<string, mixed> $def The ability definition, in the same
123 * shape as wp_register_ability() expects.
124 *
125 * @return void
126 */
127 public function register_ability_definition( $id, $def ) {
128 $this->contributed[ $id ] = $def;
129 }
130
131 /**
132 * Register a custom module adapter.
133 *
134 * @param string $module_id The module identifier.
135 * @param Merchant_Module_Abilities_Interface $adapter The adapter instance.
136 *
137 * @return void
138 */
139 public function register_adapter( $module_id, $adapter ) {
140 $this->adapters[ $module_id ] = $adapter;
141 }
142
143 /**
144 * Get the adapter for a module.
145 *
146 * Returns a custom adapter if registered, otherwise a default adapter.
147 *
148 * @param string $module_id The module identifier.
149 *
150 * @return Merchant_Module_Abilities_Interface
151 */
152 public function get_adapter( $module_id ) {
153 if ( isset( $this->adapters[ $module_id ] ) ) {
154 return $this->adapters[ $module_id ];
155 }
156
157 return new Merchant_Default_Module_Adapter( $module_id );
158 }
159
160 /**
161 * Get the schema generator instance.
162 *
163 * @return Merchant_Schema_Generator
164 */
165 public function get_schema_generator() {
166 return $this->schema_generator;
167 }
168
169 /**
170 * Get the field validator instance.
171 *
172 * @return Merchant_Field_Validator
173 */
174 public function get_field_validator() {
175 return $this->field_validator;
176 }
177
178 /**
179 * Get the campaign resolver instance.
180 *
181 * @return Merchant_Campaign_Resolver
182 */
183 public function get_campaign_resolver() {
184 return $this->campaign_resolver;
185 }
186
187 /**
188 * Build all 10 ability definition arrays.
189 *
190 * @return array<lowercase-string&non-falsy-string, array<string, mixed>>
191 */
192 private function get_ability_definitions() {
193 $registry = $this;
194
195 return array(
196 'merchant/list-modules' => array(
197 'label' => __( 'List Merchant Modules', 'merchant' ),
198 'description' => __( 'Discover all available Merchant modules with their types and status.', 'merchant' ),
199 'category' => 'merchant',
200 'meta' => array(
201 'show_in_rest' => true,
202 'annotations' => array(
203 'readonly' => true,
204 'destructive' => false,
205 'idempotent' => true,
206 ),
207 ),
208 'input_schema' => array(
209 'type' => 'object',
210 'additionalProperties' => false,
211 'properties' => array(
212 'filter' => array(
213 'type' => 'string',
214 'description' => 'Filter by status: active, inactive, or empty for all.',
215 'enum' => array( 'active', 'inactive' ),
216 'default' => '',
217 ),
218 'section' => array(
219 'type' => 'string',
220 'description' => 'Filter by section slug.',
221 'default' => '',
222 ),
223 ),
224 ),
225 'output_schema' => array(
226 'type' => 'object',
227 'description' => 'Module listing with counts.',
228 'required' => array( 'success', 'data' ),
229 'properties' => array(
230 'success' => array( 'type' => 'boolean' ),
231 'data' => array(
232 'type' => 'object',
233 'properties' => array(
234 'modules' => array(
235 'type' => 'array',
236 'items' => array(
237 'type' => 'object',
238 'properties' => array(
239 'module_id' => array( 'type' => 'string' ),
240 'is_active' => array( 'type' => 'boolean' ),
241 'is_pro' => array( 'type' => 'boolean' ),
242 'section' => array( 'type' => 'string' ),
243 ),
244 ),
245 ),
246 'total_count' => array( 'type' => 'integer' ),
247 'active_count' => array( 'type' => 'integer' ),
248 ),
249 ),
250 ),
251 ),
252 'permission_callback' => function () {
253 return Merchant_Abilities_Permissions::check( 'merchant/list-modules' );
254 },
255 'execute_callback' => function ( $params = array() ) {
256 $handler = new Merchant_List_Modules_Handler();
257 return $handler->handle( $params );
258 },
259 ),
260 'merchant/toggle-module' => array(
261 'label' => __( 'Toggle Merchant Module', 'merchant' ),
262 'description' => __( 'Activate or deactivate a Merchant module.', 'merchant' ),
263 'category' => 'merchant',
264 'meta' => array(
265 'show_in_rest' => true,
266 'annotations' => array(
267 'readonly' => false,
268 'destructive' => false,
269 'idempotent' => true,
270 ),
271 ),
272 'input_schema' => array(
273 'type' => 'object',
274 'additionalProperties' => false,
275 'properties' => array(
276 'module_id' => array(
277 'type' => 'string',
278 'minLength' => 1,
279 'description' => 'The module identifier.',
280 ),
281 'action' => array(
282 'type' => 'string',
283 'description' => 'activate or deactivate.',
284 'enum' => array( 'activate', 'deactivate' ),
285 ),
286 ),
287 'required' => array( 'module_id', 'action' ),
288 ),
289 'output_schema' => array(
290 'type' => 'object',
291 'description' => 'Toggle result with before/after status.',
292 'required' => array( 'success', 'data' ),
293 'properties' => array(
294 'success' => array( 'type' => 'boolean' ),
295 'data' => array(
296 'type' => 'object',
297 'properties' => array(
298 'module_id' => array( 'type' => 'string' ),
299 'previous_status' => array( 'type' => 'string' ),
300 'new_status' => array( 'type' => 'string' ),
301 ),
302 ),
303 ),
304 ),
305 'permission_callback' => function () {
306 return Merchant_Abilities_Permissions::check( 'merchant/toggle-module' );
307 },
308 'execute_callback' => function ( $params = array() ) {
309 $handler = new Merchant_Toggle_Module_Handler();
310 return $handler->handle( $params );
311 },
312 ),
313 'merchant/get-module-settings' => array(
314 'label' => __( 'Get Module Settings', 'merchant' ),
315 'description' => __( 'Read a module\'s settings with full field schemas.', 'merchant' ),
316 'category' => 'merchant',
317 'meta' => array(
318 'show_in_rest' => true,
319 'annotations' => array(
320 'readonly' => true,
321 'destructive' => false,
322 'idempotent' => true,
323 ),
324 ),
325 'input_schema' => array(
326 'type' => 'object',
327 'additionalProperties' => false,
328 'properties' => array(
329 'module_id' => array(
330 'type' => 'string',
331 'minLength' => 1,
332 'description' => 'The module identifier.',
333 ),
334 'include_schema' => array(
335 'type' => 'boolean',
336 'description' => 'Include field schemas in response.',
337 'default' => true,
338 ),
339 ),
340 'required' => array( 'module_id' ),
341 ),
342 'output_schema' => array(
343 'type' => 'object',
344 'properties' => array(
345 'success' => array( 'type' => 'boolean' ),
346 'data' => array(
347 'type' => 'object',
348 'properties' => array(
349 'module_id' => array( 'type' => 'string' ),
350 'is_active' => array( 'type' => 'boolean' ),
351 'settings' => array( 'type' => 'object' ),
352 ),
353 ),
354 ),
355 ),
356 'permission_callback' => function () {
357 return Merchant_Abilities_Permissions::check( 'merchant/get-module-settings' );
358 },
359 'execute_callback' => function ( $params = array() ) use ( $registry ) {
360 $handler = new Merchant_Get_Settings_Handler( $registry->get_schema_generator() );
361 return $handler->handle( $params );
362 },
363 ),
364 'merchant/update-module-settings' => array(
365 'label' => __( 'Update Module Settings', 'merchant' ),
366 'description' => __( 'Update a module\'s settings with validation and before/after reporting.', 'merchant' ),
367 'category' => 'merchant',
368 'meta' => array(
369 'show_in_rest' => true,
370 'annotations' => array(
371 'readonly' => false,
372 'destructive' => false,
373 'idempotent' => true,
374 ),
375 ),
376 'input_schema' => array(
377 'type' => 'object',
378 'additionalProperties' => false,
379 'properties' => array(
380 'module_id' => array(
381 'type' => 'string',
382 'minLength' => 1,
383 'description' => 'The module identifier.',
384 ),
385 'updates' => array(
386 'type' => 'object',
387 'description' => 'Key-value pairs of settings to update.',
388 'maxProperties' => 50,
389 ),
390 ),
391 'required' => array( 'module_id', 'updates' ),
392 ),
393 'output_schema' => array(
394 'type' => 'object',
395 'properties' => array(
396 'success' => array( 'type' => 'boolean' ),
397 'data' => array(
398 'type' => 'object',
399 'properties' => array(
400 'updated_fields' => array( 'type' => 'object' ),
401 'updated_count' => array( 'type' => 'integer' ),
402 ),
403 ),
404 'errors' => array( 'type' => 'array' ),
405 'warnings' => array( 'type' => 'array' ),
406 ),
407 ),
408 'permission_callback' => function () {
409 return Merchant_Abilities_Permissions::check( 'merchant/update-module-settings' );
410 },
411 'execute_callback' => function ( $params = array() ) use ( $registry ) {
412 $handler = new Merchant_Update_Settings_Handler(
413 $registry->get_field_validator()
414 );
415 return $handler->handle( $params );
416 },
417 ),
418 'merchant/list-campaigns' => array(
419 'label' => __( 'List Module Campaigns', 'merchant' ),
420 'description' => __( 'List campaigns with configuration summaries and analytics snapshots.', 'merchant' ),
421 'category' => 'merchant',
422 'meta' => array(
423 'show_in_rest' => true,
424 'annotations' => array(
425 'readonly' => true,
426 'destructive' => false,
427 'idempotent' => true,
428 ),
429 ),
430 'input_schema' => array(
431 'type' => 'object',
432 'additionalProperties' => false,
433 'properties' => array(
434 'module_id' => array(
435 'type' => 'string',
436 'minLength' => 1,
437 'description' => 'The module identifier.',
438 ),
439 ),
440 'required' => array( 'module_id' ),
441 ),
442 'output_schema' => array(
443 'type' => 'object',
444 'properties' => array(
445 'success' => array( 'type' => 'boolean' ),
446 'data' => array(
447 'type' => 'object',
448 'properties' => array(
449 'module_id' => array( 'type' => 'string' ),
450 'campaigns' => array(
451 'type' => 'array',
452 'items' => array(
453 'type' => 'object',
454 'properties' => array(
455 'index' => array( 'type' => 'integer' ),
456 'name' => array( 'type' => 'string' ),
457 'summary' => array( 'type' => 'object' ),
458 ),
459 ),
460 ),
461 'total_count' => array( 'type' => 'integer' ),
462 'field_schema' => array(
463 'type' => 'object',
464 'description' => 'Per-field JSON schema for this module\'s campaign layout, including x-merchant-* AI guidance.',
465 ),
466 ),
467 ),
468 ),
469 ),
470 'permission_callback' => function () {
471 return Merchant_Abilities_Permissions::check( 'merchant/list-campaigns' );
472 },
473 'execute_callback' => function ( $params = array() ) use ( $registry ) {
474 $handler = new Merchant_List_Campaigns_Handler( $registry );
475 return $handler->handle( $params );
476 },
477 ),
478 'merchant/create-campaign' => array(
479 'label' => __( 'Create Campaign', 'merchant' ),
480 'description' => __( 'Create a new campaign with defaults and validation. Call merchant/list-campaigns or merchant/get-module-settings (include_schema=true) first to read the field contract and per-field usage hints.', 'merchant' ),
481 'category' => 'merchant',
482 'meta' => array(
483 'show_in_rest' => true,
484 'annotations' => array(
485 'readonly' => false,
486 'destructive' => false,
487 'idempotent' => false,
488 ),
489 ),
490 'input_schema' => array(
491 'type' => 'object',
492 'additionalProperties' => false,
493 'properties' => array(
494 'module_id' => array(
495 'type' => 'string',
496 'minLength' => 1,
497 'description' => 'The module identifier.',
498 ),
499 'campaign' => array(
500 'type' => 'object',
501 'description' => 'Campaign data to create.',
502 'maxProperties' => 50,
503 ),
504 ),
505 'required' => array( 'module_id' ),
506 ),
507 'output_schema' => array(
508 'type' => 'object',
509 'properties' => array(
510 'success' => array( 'type' => 'boolean' ),
511 'data' => array(
512 'type' => 'object',
513 'properties' => array(
514 'module_id' => array( 'type' => 'string' ),
515 'index' => array( 'type' => 'integer' ),
516 'campaign' => array( 'type' => 'object' ),
517 ),
518 ),
519 ),
520 ),
521 'permission_callback' => function () {
522 return Merchant_Abilities_Permissions::check( 'merchant/create-campaign' );
523 },
524 'execute_callback' => function ( $params = array() ) use ( $registry ) {
525 $handler = new Merchant_Create_Campaign_Handler(
526 $registry,
527 $registry->get_field_validator()
528 );
529 return $handler->handle( $params );
530 },
531 ),
532 'merchant/update-campaign' => array(
533 'label' => __( 'Update Campaign', 'merchant' ),
534 'description' => __( 'Update specific fields of an existing campaign with validation and diff reporting. Call merchant/list-campaigns or merchant/get-module-settings (include_schema=true) first to read the field contract and per-field usage hints.', 'merchant' ),
535 'category' => 'merchant',
536 'meta' => array(
537 'show_in_rest' => true,
538 'annotations' => array(
539 'readonly' => false,
540 'destructive' => false,
541 'idempotent' => true,
542 ),
543 ),
544 'input_schema' => array(
545 'type' => 'object',
546 'additionalProperties' => false,
547 'properties' => array(
548 'module_id' => array(
549 'type' => 'string',
550 'minLength' => 1,
551 'description' => 'The module identifier.',
552 ),
553 'campaign_identifier' => array(
554 'type' => 'string',
555 'minLength' => 1,
556 'description' => 'Campaign flexible_id UUID, numeric index (0-based), or campaign name.',
557 ),
558 'updates' => array(
559 'type' => 'object',
560 'description' => 'Key-value pairs to update.',
561 ),
562 ),
563 'required' => array( 'module_id', 'campaign_identifier', 'updates' ),
564 ),
565 'output_schema' => array(
566 'type' => 'object',
567 'description' => 'Update result with before/after diff and optional validation warnings.',
568 'required' => array( 'success', 'data' ),
569 'properties' => array(
570 'success' => array( 'type' => 'boolean' ),
571 'data' => array(
572 'type' => 'object',
573 'properties' => array(
574 'module_id' => array( 'type' => 'string' ),
575 'campaign_index' => array( 'type' => 'integer' ),
576 'updated_fields' => array( 'type' => 'object' ),
577 ),
578 ),
579 'warnings' => array( 'type' => 'array' ),
580 ),
581 ),
582 'permission_callback' => function () {
583 return Merchant_Abilities_Permissions::check( 'merchant/update-campaign' );
584 },
585 'execute_callback' => function ( $params = array() ) use ( $registry ) {
586 $handler = new Merchant_Update_Campaign_Handler(
587 $registry->get_campaign_resolver(),
588 $registry,
589 $registry->get_field_validator()
590 );
591 return $handler->handle( $params );
592 },
593 ),
594 'merchant/delete-campaign' => array(
595 'label' => __( 'Delete Campaign', 'merchant' ),
596 'description' => __( 'Delete a campaign with confirmation guard.', 'merchant' ),
597 'category' => 'merchant',
598 'meta' => array(
599 'show_in_rest' => true,
600 'annotations' => array(
601 'readonly' => false,
602 'destructive' => true,
603 'idempotent' => true,
604 ),
605 ),
606 'input_schema' => array(
607 'type' => 'object',
608 'additionalProperties' => false,
609 'properties' => array(
610 'module_id' => array(
611 'type' => 'string',
612 'minLength' => 1,
613 'description' => 'The module identifier.',
614 ),
615 'campaign_identifier' => array(
616 'type' => 'string',
617 'minLength' => 1,
618 'description' => 'Campaign flexible_id UUID. Use merchant/list-campaigns to find it.',
619 ),
620 'confirm' => array(
621 'type' => 'boolean',
622 'description' => 'Confirmation guard.',
623 'default' => false,
624 ),
625 ),
626 'required' => array( 'module_id', 'campaign_identifier', 'confirm' ),
627 ),
628 'output_schema' => array(
629 'type' => 'object',
630 'properties' => array(
631 'success' => array( 'type' => 'boolean' ),
632 'data' => array(
633 'type' => 'object',
634 'properties' => array(
635 'module_id' => array( 'type' => 'string' ),
636 'deleted_index' => array( 'type' => 'integer' ),
637 'deleted_name' => array( 'type' => 'string' ),
638 'remaining_campaigns_count' => array( 'type' => 'integer' ),
639 ),
640 ),
641 ),
642 ),
643 'permission_callback' => function () {
644 return Merchant_Abilities_Permissions::check( 'merchant/delete-campaign' );
645 },
646 'execute_callback' => function ( $params = array() ) use ( $registry ) {
647 $handler = new Merchant_Delete_Campaign_Handler(
648 $registry->get_campaign_resolver(),
649 $registry
650 );
651 return $handler->handle( $params );
652 },
653 ),
654 'merchant/get-analytics' => array(
655 'label' => __( 'Get Analytics', 'merchant' ),
656 'description' => __( 'View analytics at global, module, or campaign scope.', 'merchant' ),
657 'category' => 'merchant',
658 'meta' => array(
659 'show_in_rest' => true,
660 'annotations' => array(
661 'readonly' => true,
662 'destructive' => false,
663 'idempotent' => true,
664 ),
665 ),
666 'input_schema' => array(
667 'type' => 'object',
668 'additionalProperties' => false,
669 'properties' => array(
670 'scope' => array(
671 'type' => 'string',
672 'description' => 'Analytics scope: global, module, or campaign.',
673 'enum' => array( 'global', 'module', 'campaign' ),
674 'default' => 'global',
675 ),
676 'module_id' => array(
677 'type' => 'string',
678 'description' => 'Module identifier (required for module/campaign scope).',
679 ),
680 'campaign_id' => array(
681 'type' => 'string',
682 'description' => 'Campaign identifier (required for campaign scope).',
683 ),
684 ),
685 ),
686 'output_schema' => array(
687 'type' => 'object',
688 'properties' => array(
689 'success' => array( 'type' => 'boolean' ),
690 'data' => array(
691 'type' => 'object',
692 'properties' => array(
693 'scope' => array( 'type' => 'string' ),
694 'metrics' => array( 'type' => 'object' ),
695 'module_id' => array( 'type' => 'string' ),
696 'campaign_id' => array( 'type' => 'string' ),
697 ),
698 ),
699 ),
700 ),
701 'permission_callback' => function () {
702 return Merchant_Abilities_Permissions::check( 'merchant/get-analytics' );
703 },
704 'execute_callback' => function ( $params = array() ) {
705 $handler = new Merchant_Get_Analytics_Handler();
706 return $handler->handle( $params );
707 },
708 ),
709 'merchant/get-recommendations' => array(
710 'label' => __( 'Get Recommendations', 'merchant' ),
711 'description' => __( 'Get optimization recommendations based on analytics data.', 'merchant' ),
712 'category' => 'merchant',
713 'meta' => array(
714 'show_in_rest' => true,
715 'annotations' => array(
716 'readonly' => true,
717 'destructive' => false,
718 'idempotent' => true,
719 ),
720 ),
721 'input_schema' => array(
722 'type' => 'object',
723 'additionalProperties' => false,
724 'properties' => array(
725 'module_id' => array(
726 'type' => 'string',
727 'description' => 'Optional. Limit recommendations to a specific module.',
728 ),
729 'focus' => array(
730 'type' => 'string',
731 'description' => 'Recommendation focus area.',
732 'enum' => array( 'conversion', 'revenue', 'engagement', 'general' ),
733 'default' => 'general',
734 ),
735 'date_range' => array(
736 'type' => 'object',
737 'description' => 'Optional date range for recommendations. Defaults to last 30 days.',
738 'properties' => array(
739 'start' => array(
740 'type' => 'string',
741 'description' => 'Start date (Y-m-d or m/d/y).',
742 ),
743 'end' => array(
744 'type' => 'string',
745 'description' => 'End date (Y-m-d or m/d/y).',
746 ),
747 ),
748 ),
749 ),
750 ),
751 'output_schema' => array(
752 'type' => 'object',
753 'properties' => array(
754 'success' => array( 'type' => 'boolean' ),
755 'data' => array(
756 'type' => 'object',
757 'properties' => array(
758 'recommendations' => array(
759 'type' => 'array',
760 'items' => array(
761 'type' => 'object',
762 'properties' => array(
763 'flag' => array( 'type' => 'string' ),
764 'severity' => array( 'type' => 'string' ),
765 'module_id' => array( 'type' => 'string' ),
766 'context' => array( 'type' => 'string' ),
767 'metrics' => array( 'type' => 'object' ),
768 ),
769 ),
770 ),
771 'total_recommendations' => array( 'type' => 'integer' ),
772 ),
773 ),
774 ),
775 ),
776 'permission_callback' => function () {
777 return Merchant_Abilities_Permissions::check( 'merchant/get-recommendations' );
778 },
779 'execute_callback' => function ( $params = array() ) use ( $registry ) {
780 $handler = new Merchant_Get_Recommendations_Handler( $registry );
781 return $handler->handle( $params );
782 },
783 ),
784 );
785 }
786 }
787