From e4a83ccfe904fa0f7ac2087ce9a923cbcc88fc22 Mon Sep 17 00:00:00 2001 From: Aaron Bauman <260-aaronbauman@users.noreply.drupalcode.org> Date: Mon, 27 Mar 2023 17:42:11 +0000 Subject: [PATCH] Issue #3320557 by AaronBauman, Dmitriy.trt: D10 Readiness --- composer.json | 8 +- .../salesforce_address.info.yml | 2 +- .../salesforce_example.info.yml | 2 +- modules/salesforce_jwt/composer.json | 2 +- .../salesforce_jwt/salesforce_jwt.info.yml | 4 +- .../salesforce_logger.info.yml | 2 +- modules/salesforce_mapping/composer.json | 7 +- .../salesforce_mapping.drush.inc | 323 ------------ .../salesforce_mapping.info.yml | 4 +- .../PropertiesExtended.php | 6 + .../RelatedProperties.php | 6 + .../salesforce_mapping_ui.info.yml | 2 +- .../salesforce_oauth.info.yml | 2 +- modules/salesforce_pull/composer.json | 5 +- .../salesforce_pull/salesforce_pull.drush.inc | 315 ----------- .../salesforce_pull/salesforce_pull.info.yml | 2 +- .../tests/src/Functional/PullQueueTest.php | 14 +- .../tests/src/Unit/DeleteHandlerTest.php | 2 +- .../tests/src/Unit/QueueHandlerTest.php | 2 +- modules/salesforce_push/composer.json | 5 +- .../salesforce_push/salesforce_push.drush.inc | 108 ---- .../salesforce_push/salesforce_push.info.yml | 2 +- .../tests/src/Unit/PushQueueTest.php | 2 +- .../SalesforcePushQueueProcessorRestTest.php | 8 +- modules/salesforce_soap/composer.json | 2 +- .../salesforce_soap/salesforce_soap.info.yml | 2 +- modules/salesforce_webform/composer.json | 5 +- .../salesforce_webform.info.yml | 4 +- salesforce.drush.inc | 492 ------------------ salesforce.info.yml | 2 +- src/Tests/TestHttpClient.php | 3 +- src/Tests/TestHttpClientWrapper.php | 14 +- .../salesforce_test_rest_client.info.yml | 2 +- ...alesforceTestRestClientServiceProvider.php | 4 +- 34 files changed, 79 insertions(+), 1286 deletions(-) delete mode 100644 modules/salesforce_mapping/salesforce_mapping.drush.inc delete mode 100644 modules/salesforce_pull/salesforce_pull.drush.inc delete mode 100644 modules/salesforce_push/salesforce_push.drush.inc delete mode 100644 salesforce.drush.inc diff --git a/composer.json b/composer.json index 3c349755..7acaed47 100644 --- a/composer.json +++ b/composer.json @@ -27,7 +27,7 @@ "extra": { "drush": { "services": { - "drush.services.yml": "^9" + "drush.services.yml": "^9 || ^10 || ^11" } } }, @@ -41,9 +41,9 @@ "drupal/key": "^1.14", "firebase/php-jwt": "^5.0 || ^6.0", "lusitanian/oauth": "^0.8.11", - "drupal/dynamic_entity_reference": "^1.9 || ^2.0", - "drupal/typed_data": "^1.0-alpha5", - "messageagency/force.com-toolkit-for-php": "^1.0.1", + "drupal/dynamic_entity_reference": "^1.9 || ^2.0 || ^3 || ^4", + "drupal/typed_data": "^1.0-beta2", + "messageagency/force.com-toolkit-for-php": "^1.0.2", "ext-soap": "*" }, "suggest": { diff --git a/modules/salesforce_address/salesforce_address.info.yml b/modules/salesforce_address/salesforce_address.info.yml index 89314300..2567ac9a 100644 --- a/modules/salesforce_address/salesforce_address.info.yml +++ b/modules/salesforce_address/salesforce_address.info.yml @@ -1,7 +1,7 @@ name: Salesforce Address type: module description: A custom Address Field Widget for Salesforce compatibility. -core_version_requirement: ^9.1 +core_version_requirement: ^9 || ^10 package: Salesforce dependencies: - address:address diff --git a/modules/salesforce_example/salesforce_example.info.yml b/modules/salesforce_example/salesforce_example.info.yml index d021e892..3a0e260e 100644 --- a/modules/salesforce_example/salesforce_example.info.yml +++ b/modules/salesforce_example/salesforce_example.info.yml @@ -1,7 +1,7 @@ name: Salesforce Example type: module description: Salesforce Examples -core_version_requirement: ^9.1 +core_version_requirement: ^9 || ^10 package: Salesforce dependencies: - salesforce:salesforce_push diff --git a/modules/salesforce_jwt/composer.json b/modules/salesforce_jwt/composer.json index 98144e31..456f7617 100644 --- a/modules/salesforce_jwt/composer.json +++ b/modules/salesforce_jwt/composer.json @@ -25,7 +25,7 @@ "source": "http://cgit.drupalcode.org/salesforce" }, "require": { - "drupal/key": "^1.14", + "drupal/key": "^1.14 || 1.x-dev", "firebase/php-jwt": "^5.0 || ^6.0", "lusitanian/oauth": "^0.8.11" } diff --git a/modules/salesforce_jwt/salesforce_jwt.info.yml b/modules/salesforce_jwt/salesforce_jwt.info.yml index c9b7dc15..56bf9287 100644 --- a/modules/salesforce_jwt/salesforce_jwt.info.yml +++ b/modules/salesforce_jwt/salesforce_jwt.info.yml @@ -1,9 +1,9 @@ name: Salesforce JWT Auth Provider type: module description: Provides key-based Salesforce authentication. -core_version_requirement: ^9.1 +core_version_requirement: ^9 || ^10 package: Salesforce configure: salesforce.auth_config dependencies: - salesforce:salesforce - - key:key (>= 1.14) + - key:key diff --git a/modules/salesforce_logger/salesforce_logger.info.yml b/modules/salesforce_logger/salesforce_logger.info.yml index b86658df..ef85f72d 100644 --- a/modules/salesforce_logger/salesforce_logger.info.yml +++ b/modules/salesforce_logger/salesforce_logger.info.yml @@ -1,7 +1,7 @@ name: Salesforce Logger type: module description: Consolidated logging for Salesforce Log events. -core_version_requirement: ^9.1 +core_version_requirement: ^9 || ^10 package: Salesforce configure: salesforce_logger.settings dependencies: diff --git a/modules/salesforce_mapping/composer.json b/modules/salesforce_mapping/composer.json index 8cdaac2c..8a47be29 100644 --- a/modules/salesforce_mapping/composer.json +++ b/modules/salesforce_mapping/composer.json @@ -9,14 +9,13 @@ } ], "require": { - "php": ">=5.6.0", - "drupal/dynamic_entity_reference": "^1.9 || ^2.0", - "drupal/typed_data": "^1.0-alpha5" + "drupal/dynamic_entity_reference": "^1.9 || ^2.0 || ^3 || ^4", + "drupal/typed_data": "^1.0-beta2" }, "extra": { "drush": { "services": { - "drush.services.yml": "^9" + "drush.services.yml": "^9 || ^10 || ^11" } } } diff --git a/modules/salesforce_mapping/salesforce_mapping.drush.inc b/modules/salesforce_mapping/salesforce_mapping.drush.inc deleted file mode 100644 index 46069360..00000000 --- a/modules/salesforce_mapping/salesforce_mapping.drush.inc +++ /dev/null @@ -1,323 +0,0 @@ -<?php - -/** - * @file - * Drush integration for Salesforce. - */ - -use Drupal\salesforce\SelectQuery; -use Drupal\salesforce\Rest\RestClientInterface; -use Drupal\Core\Entity\ContentEntityStorageInterface; - -/** - * Implements hook_drush_command(). - */ -function salesforce_mapping_drush_command() { - $items['sf-prune-revisions'] = [ - 'description' => 'Delete old revisions of Mapped Objects, based on revision limit settings. Useful if you have recently changed settings, or if you have just updated to a version with prune support.', - 'aliases' => ['sfprune'], - ]; - $items['sf-purge-drupal'] = [ - 'description' => 'Clean up Mapped Objects table by deleting any records which reference missing Drupal entities.', - 'aliases' => ['sfpd'], - 'options' => [ - 'mapping' => 'Given a mapping id, limit purge to only those referenced.', - ], - ]; - $items['sf-purge-salesforce'] = [ - 'description' => 'Clean up Mapped Objects table by deleting any records which reference missing Salesforce records.', - 'aliases' => ['sfpsf'], - 'options' => [ - 'mapping' => 'Given a mapping id, limit purge to only those referenced.', - ], - ]; - $items['sf-purge-mapping'] = [ - 'description' => 'Clean up Mapped Objects table by deleting any records which reference missing Mappings.', - 'aliases' => ['sfpmap'], - 'options' => [ - 'mapping' => 'Given a mapping id, limit purge to only those referenced.', - ], - ]; - $items['sf-purge-all'] = [ - 'description' => 'Clean up Mapped Objects table by deleting any records which reference missing Mappings, Entities, or Salesforce records.', - 'aliases' => ['sfpall'], - 'options' => [ - 'mapping' => 'Given a mapping id, limit purge to only those referenced.', - ], - ]; - return $items; -} - -/** - * Support for drush 8 is deprecated and will be removed in a future release. - */ -function drush_salesforce_mapping_sf_prune_revisions() { - _drush_salesforce_deprecated(); - - $limit = - \Drupal::service('config.factory') - ->get('salesforce.settings') - ->get('limit_mapped_object_revisions'); - if ($limit <= 0) { - drush_log('Mapped Object revisions limit is 0. No action taken.', 'warning'); - return; - } - $etm = \Drupal::service('entity_type.manager'); - /** @var \Drupal\salesforce_mapping\MappedObjectStorage $storage */ - $storage = $etm - ->getStorage('salesforce_mapped_object'); - $revision_table = $etm - ->getDefinition('salesforce_mapped_object') - ->getRevisionTable(); - $ids = \Drupal::service('database') - ->select($revision_table, 'r') - ->fields('r', ['id']) - ->having('COUNT(r.id) > ' . $limit) - ->groupBy('r.id') - ->execute() - ->fetchCol(); - if (empty($ids)) { - drush_log("No Mapped Objects with more than $limit revision(s). No action taken.", 'warning'); - return; - } - drush_log('Found ' . count($ids) . ' mapped objects with excessive revisions. Will prune to ' . $limit . ' revision(s) each. This may take a while.', 'ok'); - $total = count($ids); - $i = 0; - $buckets = ceil($total / 20); - if ($buckets == 0) { - $buckets = 1; - } - foreach ($ids as $id) { - if ($i++ % $buckets == 0) { - drush_log("Pruned $i of $total records.", 'ok'); - } - /** @var \Drupal\salesforce_mapping\Entity\MappedObject $mapped_object */ - if ($mapped_object = $storage->load($id)) { - $mapped_object->pruneRevisions($storage); - } - } - -} - -/** - * Support for drush 8 is deprecated and will be removed in a future release. - */ -function drush_salesforce_mapping_sf_purge_drupal() { - _drush_salesforce_deprecated(); - $etm = \Drupal::service('entity_type.manager'); - $mapped_obj_storage = $etm->getStorage('salesforce_mapped_object'); - $mapped_obj_table = $etm - ->getDefinition('salesforce_mapped_object') - ->getBaseTable(); - - $query = \Drupal::service('database') - ->select($mapped_obj_table, 'm') - ->fields('m', ['drupal_entity__target_type']) - ->distinct(); - if ($mapping_id = drush_get_option('mapping')) { - $query->condition('salesforce_mapping', $mapping_id); - } - $entity_type_ids = $query - ->execute() - ->fetchCol(); - if (empty($entity_type_ids)) { - drush_log('No orphaned mapped objects found by Drupal entities.', 'ok'); - return; - } - - foreach ($entity_type_ids as $et_id) { - $query = \Drupal::service('database') - ->select($mapped_obj_table, 'm') - ->fields('m', ['id']) - ->condition('drupal_entity__target_type', $et_id); - - $entity_type = $etm->getDefinition($et_id); - if ($entity_type) { - $id_key = $entity_type->getKey('id'); - $query->addJoin("LEFT", $entity_type->getBaseTable(), 'et', "et.$id_key = m.drupal_entity__target_id_int"); - $query->isNull("et.$id_key"); - } - $mapped_obj_ids = $query->execute()->fetchCol(); - if (empty($mapped_obj_ids)) { - drush_log('No orphaned mapped objects found for ' . $et_id . '.', 'ok'); - continue; - } - _drush_salesforce_mapping_confirm_and_delete($mapped_obj_ids, $mapped_obj_storage, 'entity type: ' . $et_id); - } -} - -/** - * Support for drush 8 is deprecated and will be removed in a future release. - */ -function drush_salesforce_mapping_sf_purge_salesforce() { - _drush_salesforce_deprecated(); - $etm = \Drupal::service('entity_type.manager'); - $client = \Drupal::service('salesforce.client'); - $object_types = _drush_salesforce_mapping_object_types_by_prefix($client); - - $mapped_obj_storage = $etm->getStorage('salesforce_mapped_object'); - $mapped_obj_table = $etm - ->getDefinition('salesforce_mapped_object') - ->getBaseTable(); - - $query = \Drupal::service('database') - ->select($mapped_obj_table, 'm'); - $query->addExpression('distinct substr(salesforce_id, 1, 3)'); - $mapping_id = FALSE; - if ($mapping_id = drush_get_option('mapping')) { - $query->condition('salesforce_mapping', $mapping_id); - } - $sfid_prefixes = $query - ->execute() - ->fetchCol(); - - foreach ($sfid_prefixes as $prefix) { - if (empty($object_types[$prefix]['name'])) { - $mapped_obj_ids = \Drupal::service('database') - ->select($mapped_obj_table, 'm') - ->fields('m', ['salesforce_id', 'id']) - ->condition('salesforce_id', $prefix . '%', 'LIKE') - ->execute() - ->fetchAllKeyed(); - if (empty($mapped_obj_ids)) { - continue; - } - drush_log('Unknown object type for Salesforce ID prefix ' . $prefix); - _drush_salesforce_mapping_confirm_and_delete($mapped_obj_ids, $mapped_obj_storage, 'prefix ' . $prefix); - continue; - } - $query = \Drupal::service('database') - ->select($mapped_obj_table, 'm') - ->fields('m', ['salesforce_id', 'id']); - if ($mapping_id) { - $query->condition('salesforce_mapping', $mapping_id); - } - else { - $query->condition('salesforce_id', $prefix . '%', 'LIKE'); - } - $sfids = $query - ->execute() - ->fetchAllKeyed(); - $to_delete = $sfids; - // SOQL queries are limited to 4000-characters in where statements. - // Chunkify in case we have more than ~200 sfids. - foreach (array_chunk($sfids, 200, TRUE) as $chunk) { - $soql_query = new SelectQuery($object_types[$prefix]['name']); - $soql_query->fields[] = 'Id'; - $soql_query->addCondition('Id', array_keys($chunk)); - $results = $client->query($soql_query); - foreach ($results->records() as $record) { - unset($to_delete[(string) $record->id()]); - } - } - if (empty($to_delete)) { - drush_log('No orphaned mapped objects found for SObject type ' . $object_types[$prefix]['name'], 'ok'); - continue; - } - _drush_salesforce_mapping_confirm_and_delete(array_values($to_delete), $mapped_obj_storage, 'SObject type *' . $object_types[$prefix]['name'] . '*'); - } -} - -/** - * Support for drush 8 is deprecated and will be removed in a future release. - */ -function drush_salesforce_mapping_sf_purge_mapping() { - _drush_salesforce_deprecated(); - $etm = \Drupal::service('entity_type.manager'); - $mapped_obj_storage = $etm->getStorage('salesforce_mapped_object'); - $mapped_obj_table = $etm - ->getDefinition('salesforce_mapped_object') - ->getBaseTable(); - - $query = \Drupal::service('database') - ->select($mapped_obj_table, 'm') - ->fields('m', ['salesforce_mapping']) - ->distinct(); - if ($mapping_id = drush_get_option('mapping')) { - $query->condition('salesforce_mapping', $mapping_id); - } - $mapping_ids = $query - ->execute() - ->fetchCol(); - if (empty($entity_type_ids)) { - drush_log('No orphaned mapped objects found by mapping.', 'ok'); - return; - } - - foreach ($mapping_ids as $mapping_id) { - $mapping = $etm - ->getStorage('salesforce_mapping') - ->load($mapping_id); - // If mapping loads successsfully, we assume the mapped object is OK. - if ($mapping) { - continue; - } - $mapped_obj_ids = \Drupal::service('database') - ->select($mapped_obj_table, 'm') - ->fields('m', ['id']) - ->condition('salesforce_mapping', $mapping_id) - ->distinct() - ->execute() - ->fetchCol(); - - _drush_salesforce_mapping_confirm_and_delete($mapped_obj_ids, $mapped_obj_storage, 'missing mapping: ' . $mapping_id); - } -} - -/** - * Support for drush 8 is deprecated and will be removed in a future release. - */ -function drush_salesforce_mapping_sf_purge_all() { - _drush_salesforce_deprecated(); - drush_salesforce_mapping_sf_purge_mapping(); - drush_salesforce_mapping_sf_purge_drupal(); - drush_salesforce_mapping_sf_purge_salesforce(); -} - -/** - * Helper to fetch object types by prefix. - * - * @param \Drupal\salesforce\Rest\RestClientInterface $client - * Client interface. - * - * @return array - * Array of objects, indexed by key prefix. - */ -function _drush_salesforce_mapping_object_types_by_prefix(RestClientInterface $client) { - $ret = []; - $describe = $client->objects(); - foreach ($describe as $object) { - $ret[$object['keyPrefix']] = $object; - } - return $ret; -} - -/** - * Helper to interactively confirm delete. - * - * @param array $object_ids - * Records to be deleted. - * @param \Drupal\Core\Entity\ContentEntityStorageInterface $storage - * Storage. - * @param string $extra - * Extra message parts. - * - * @throws \Drupal\Core\Entity\EntityStorageException - */ -function _drush_salesforce_mapping_confirm_and_delete(array $object_ids, ContentEntityStorageInterface $storage, $extra = '') { - if (empty($object_ids)) { - return; - } - $message = 'Delete ' . count($object_ids) . ' orphaned mapped objects'; - if ($extra) { - $message .= ' for ' . $extra; - } - $message .= '?'; - if (!drush_confirm($message)) { - return; - } - - // Still have to *load* entities in order to delete them. **UGH**. - $mapped_objs = $storage->loadMultiple($object_ids); - $storage->delete($mapped_objs); -} diff --git a/modules/salesforce_mapping/salesforce_mapping.info.yml b/modules/salesforce_mapping/salesforce_mapping.info.yml index ffe53f7a..13fc08b5 100644 --- a/modules/salesforce_mapping/salesforce_mapping.info.yml +++ b/modules/salesforce_mapping/salesforce_mapping.info.yml @@ -2,8 +2,8 @@ name: Salesforce Mapping type: module description: Map Drupal entities to Salesforce objects. package: Salesforce -core_version_requirement: ^9.1 +core_version_requirement: ^9 || ^10 dependencies: - salesforce:salesforce - dynamic_entity_reference:dynamic_entity_reference - - typed_data:typed_data (>= 1.0-alpha5) + - typed_data:typed_data diff --git a/modules/salesforce_mapping/src/Plugin/SalesforceMappingField/PropertiesExtended.php b/modules/salesforce_mapping/src/Plugin/SalesforceMappingField/PropertiesExtended.php index e5ddb815..ef8fe535 100644 --- a/modules/salesforce_mapping/src/Plugin/SalesforceMappingField/PropertiesExtended.php +++ b/modules/salesforce_mapping/src/Plugin/SalesforceMappingField/PropertiesExtended.php @@ -25,7 +25,13 @@ class PropertiesExtended extends PropertiesBase { */ public function getPluginDefinition() { $definition = parent::getPluginDefinition(); + $field_name = $this->config('drupal_field_value'); + if ($field_name === NULL) { + // No need to load the field if the plugin isn't configured. + return $definition; + } + if (strpos($field_name, '.')) { list($field_name, $dummy) = explode('.', $field_name, 2); } diff --git a/modules/salesforce_mapping/src/Plugin/SalesforceMappingField/RelatedProperties.php b/modules/salesforce_mapping/src/Plugin/SalesforceMappingField/RelatedProperties.php index 7538745d..82fed025 100644 --- a/modules/salesforce_mapping/src/Plugin/SalesforceMappingField/RelatedProperties.php +++ b/modules/salesforce_mapping/src/Plugin/SalesforceMappingField/RelatedProperties.php @@ -101,7 +101,13 @@ class RelatedProperties extends SalesforceMappingFieldPluginBase { public function getPluginDefinition() { $definition = parent::getPluginDefinition(); $definition['config_dependencies']['config'] = []; + $field_name = $this->config('drupal_field_value'); + if ($field_name === NULL) { + // No need to load the field if the plugin isn't configured. + return $definition; + } + if (strpos($field_name, ':')) { [$field_name, $dummy] = explode(':', $field_name, 2); } diff --git a/modules/salesforce_mapping_ui/salesforce_mapping_ui.info.yml b/modules/salesforce_mapping_ui/salesforce_mapping_ui.info.yml index 2f9dfe4a..57e038f7 100644 --- a/modules/salesforce_mapping_ui/salesforce_mapping_ui.info.yml +++ b/modules/salesforce_mapping_ui/salesforce_mapping_ui.info.yml @@ -2,7 +2,7 @@ name: Salesforce Mapping UI type: module description: User interface for managing Salesforce mappings. package: Salesforce -core_version_requirement: ^9.1 +core_version_requirement: ^9 || ^10 configure: entity.salesforce_mapping.list dependencies: - salesforce:salesforce_mapping diff --git a/modules/salesforce_oauth/salesforce_oauth.info.yml b/modules/salesforce_oauth/salesforce_oauth.info.yml index a8c15b9a..aa589a0f 100644 --- a/modules/salesforce_oauth/salesforce_oauth.info.yml +++ b/modules/salesforce_oauth/salesforce_oauth.info.yml @@ -1,7 +1,7 @@ name: Salesforce OAuth user-agent Provider type: module description: Provides user-agent-based Salesforce OAuth authentication. -core_version_requirement: ^9.1 +core_version_requirement: ^9 || ^10 package: Salesforce configure: salesforce.auth_config dependencies: diff --git a/modules/salesforce_pull/composer.json b/modules/salesforce_pull/composer.json index 573c6a5b..e5f00555 100644 --- a/modules/salesforce_pull/composer.json +++ b/modules/salesforce_pull/composer.json @@ -8,13 +8,10 @@ "email": "aaron@messageagency.com" } ], - "require": { - "php": ">=5.6.0" - }, "extra": { "drush": { "services": { - "drush.services.yml": "^9" + "drush.services.yml": "^9 || ^10 || ^11" } } } diff --git a/modules/salesforce_pull/salesforce_pull.drush.inc b/modules/salesforce_pull/salesforce_pull.drush.inc deleted file mode 100644 index 067e49b0..00000000 --- a/modules/salesforce_pull/salesforce_pull.drush.inc +++ /dev/null @@ -1,315 +0,0 @@ -<?php - -/** - * @file - * Salesforce Pull drush 8 commands. - */ - -use Drupal\salesforce\SFID; -use Drupal\salesforce\Event\SalesforceEvents; -use Drupal\salesforce_mapping\Event\SalesforceQueryEvent; - -/** - * Implements hook_drush_command(). - */ -function salesforce_pull_drush_command() { - - $items['sf-pull-query'] = [ - 'category' => 'salesforce', - 'description' => 'Given a mapping, enqueue records for pull from Salesforce, ignoring modification timestamp. This command is useful, for example, when seeding content for a Drupal site prior to deployment.', - 'aliases' => ['sfpq', 'sfiq'], - 'arguments' => [ - 'name' => 'Machine name of the Salesforce Mapping for which to queue pull records.', - ], - 'options' => [ - 'where' => [ - 'description' => 'A WHERE clause to add to the SOQL pull query. Default behavior is to query and pull all records.', - ], - 'start' => 'strtotime()able string for the start timeframe over which to pull, e.g. "-5 hours". If omitted, use the value given by the mapping\'s pull timestamp. Must be in the past.', - 'stop' => 'strtotime()able string for the end timeframe over which to pull, e.g. "-5 hours". If omitted, defaults to "now". Must be "now" or earlier', - 'force-pull' => 'if given, force all queried records to be pulled regardless of updated timestamps. If omitted, only Salesforce records which are newer than linked Drupal records will be pulled.', - ], - 'examples' => [ - 'drush sfpq user' => 'Query and queue all records for "user" Salesforce mapping.', - 'drush sfpq user --where="Email like \'%foo%\' AND (LastName = \'bar\' OR FirstName = \'bar\')"' => 'Query and queue all records for "user" Salesforce mapping with Email field containing the string "foo" and First or Last name equal to "bar"', - 'drush sfpq' => 'Fetch and process all pull queue items', - 'drush sfpq --start="-25 minutes" --stop="-5 minutes"' => 'Fetch updated records for all mappings between 25 minutes and 5 minutes old, and process them.', - 'drush sfpq foo --start="-25 minutes" --stop="-5 minutes"' => 'Fetch updated records for mapping "foo" between 25 minutes and 5 minutes old, and process them.', - ], - ]; - - $items['sf-pull-file'] = [ - 'category' => 'salesforce', - 'description' => 'Given a mapping, enqueue a list of object IDs to be pulled from a CSV file, e.g. a Salesforce report. The first column of the CSV file must be SFIDs. Additional columns will be ignored.', - 'aliases' => ['sfpf', 'sfif'], - 'arguments' => [ - 'file' => 'CSV file name of 15- or 18-character Salesforce ids to be pulled. ', - 'name' => 'Machine name of the Salesforce Mapping for which to queue pull records.', - ], - ]; - - $items['sf-pull-reset'] = [ - 'category' => 'salesforce', - 'description' => 'Reset pull timestamps for one or all Salesforce Mappings, and set all mapped objects to be force-pulled.', - 'arguments' => [ - 'name' => [ - 'description' => 'Machine name of the Salesforce Mapping for which to reset pull timestamps.', - ], - ], - 'options' => [ - 'delete' => 'Reset delete date timestamp (instead of pull date timestamp)', - ], - 'examples' => [ - 'drush sf-pull-reset' => 'Reset pull timestamps for all mappings.', - 'drush sf-pull-reset foo' => 'Reset pull timestamps for mapping "foo"', - 'drush sf-pull-reset --delete' => 'Reset "delete" timestamps for all mappings', - 'drush sf-pull-reset foo --delete' => 'Reset "delete" timestamp for mapping "foo"', - ], - ]; - - $items['sf-pull-set'] = [ - 'category' => 'salesforce', - 'description' => 'Set pull timestamp on a single Salesforce Mappings to a specific point in history (or now).', - 'arguments' => [ - 'name' => [ - 'description' => 'Machine name of the Salesforce Mapping for which to reset pull timestamps.', - ], - 'time' => [ - 'description' => 'Timestamp to set the value to. Defaults to the runtime.', - ], - ], - 'examples' => [ - 'drush sf-pull-set foo' => 'Set pull timestamps for mapping "foo" to "now"', - 'drush sf-pull-set foo 1517416761' => 'Set pull timestamps for mapping "foo" to Jan 31, 2018, around 8:40am time in Portland, OR', - ], - ]; - - return $items; -} - -/** - * Queues records for pull from salesforce for the given mapping. - * - * @param string $name - * Mapping name. - */ -function drush_salesforce_pull_sf_pull_query($name) { - _drush_salesforce_deprecated(); - if (!($mapping = _salesforce_drush_get_mapping($name))) { - return; - } - - if ($start = drush_get_option('start')) { - $start = strtotime($start); - } - else { - $start = 0; - } - - if ($stop = drush_get_option('stop')) { - $stop = strtotime($stop); - } - else { - $stop = 0; - } - - $where = drush_get_option('where'); - - if (!($soql = $mapping->getPullQuery([], $start, $stop))) { - drush_log(dt('!mapping: Unable to generate pull query. Does this mapping have any Salesforce Action Triggers enabled?', ['!mapping' => $mapping->id()]), 'error'); - return; - } - - if ($where) { - $soql->conditions[] = [$where]; - } - - \Drupal::service('event_dispatcher')->dispatch( - new SalesforceQueryEvent($mapping, $soql), - SalesforceEvents::PULL_QUERY - ); - - drush_log(dt('!mapping: Issuing pull query: !query', ['!query' => (string) $soql, '!mapping' => $mapping->id()]), 'notice'); - $results = \Drupal::service('salesforce.client')->query($soql); - - if (empty($results)) { - drush_log(dt('!mapping: No records found to pull.', ['!mapping' => $mapping->id()]), 'warning'); - return; - } - - $force_pull = drush_get_option('force-pull') ? TRUE : FALSE; - - \Drupal::service('salesforce_pull.queue_handler') - ->enqueueAllResults($mapping, $results, $force_pull); - drush_log(dt('!mapping: Queued !count items for pull.', ['!count' => $results->size(), '!mapping' => $mapping->id()]), 'success'); - -} - -/** - * Queues records for pull from Salesforce from the given file and mapping. - * - * @param string $file - * File name with IDs. - * @param string $name - * Mapping name. - */ -function drush_salesforce_pull_sf_pull_file($file, $name = NULL) { - _drush_salesforce_deprecated(); - if (empty($file)) { - drush_log("File argument is required.", 'error'); - drush_log("usage:\n drush sf-pull-file file_name [mapping_id]", 'error'); - return; - } - if (!file_exists($file)) { - drush_log('File not found.', 'error'); - return; - } - - if (!($mapping = _salesforce_drush_get_mapping($name))) { - return; - } - - // Fetch the base query to make sure we can pull using this mapping. - $soql = $mapping->getPullQuery([], 1, 0); - if (empty($soql)) { - drush_log(dt('Unable to generate pull query for !name. Does this mapping have any Salesforce Action Triggers enabled?'), 'error'); - return; - } - - $sf = \Drupal::service('salesforce.client'); - - $rows = array_map('str_getcsv', file($file)); - - // Track IDs to avoid duplicates. - $seen = []; - - // Max length for SOQL query is 20,000 characters. Chunk the IDs into smaller - // units to avoid this limit. 1000 IDs per query * 18 chars per ID = up to - // 18000 characters per query, plus up to 2000 for fields, where condition, - // etc. - $queries = []; - foreach (array_chunk($rows, 1000) as $i => $chunk) { - // Reset our base query: - $soql = $mapping->getPullQuery([], 1, 0); - - // Now add all the IDs to it. - $sfids = []; - foreach ($chunk as $j => $row) { - if (empty($row) || empty($row[0])) { - drush_log(dt('Skipping row !n, no SFID found.', ['!n' => $j]), 'warning'); - continue; - } - try { - $sfid = new SFID($row[0]); - // Sanity check to make sure the key-prefix is correct. - // If so, this is probably a good SFID. - // If not, it is definitely not a good SFID. - if ($mapping->getSalesforceObjectType() != $sf->getObjectTypeName($sfid)) { - throw new \Exception(); - } - } - catch (\Exception $e) { - drush_log(dt('Skipping row !n, no SFID found.', ['!n' => $j]), 'warning'); - continue; - } - $sfid = (string) $sfid; - if (empty($sfids[$sfid])) { - $sfids[] = $sfid; - $seen[$sfid] = $sfid; - } - } - $soql->addCondition('Id', $sfids, 'IN'); - $queries[] = $soql; - } - if (empty($seen)) { - drush_log(dt('No SFIDs found in the given file.'), 'error'); - return; - } - if (!drush_confirm(dt('Ready to enqueue !count records for pull?', ['!count' => count($seen)]))) { - return; - } - - foreach ($queries as $soql) { - \Drupal::service('event_dispatcher')->dispatch( - new SalesforceQueryEvent($mapping, $soql), - SalesforceEvents::PULL_QUERY - ); - - drush_log(dt('Issuing pull query: !query', ['!query' => (string) $soql])); - - $results = \Drupal::service('salesforce.client')->query($soql); - - if (empty($results)) { - drush_lo('No records found to pull.'); - continue; - } - - \Drupal::service('salesforce_pull.queue_handler') - ->enqueueAllResults($mapping, $results); - drush_print(dt('Queued !count items for pull.', ['!count' => $results->size()])); - } - -} - -/** - * Get an array of all pull mappings, or the given mapping by name. - * - * @param string $name - * Mapping name. - * - * @return \Drupal\salesforce_mapping\Entity\SalesforceMappingInterface[] - * Mappings. - * - * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException - * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException - */ -function _salesforce_pull_load_single_mapping_array_or_all_pull_mappings($name = NULL) { - if ($name != NULL) { - $mapping = _salesforce_drush_get_mapping($name); - if ($mapping && !$mapping->doesPull()) { - return []; - } - if ($mapping) { - return [$mapping]; - } - return []; - } - else { - return \Drupal::entityTypeManager() - ->getStorage('salesforce_mapping') - ->loadPullMappings(); - } -} - -/** - * Reset pull time on all mappings, or the given mapping by name. - */ -function drush_salesforce_pull_sf_pull_reset($name = NULL) { - $mappings = _salesforce_pull_load_single_mapping_array_or_all_pull_mappings($name); - if (empty($mappings)) { - return; - } - foreach ($mappings as $mapping) { - $mapping->setLastPullTime(NULL); - \Drupal::entityTypeManager() - ->getStorage('salesforce_mapped_object') - ->setForcePull($mapping); - } -} - -/** - * Set pull time on all mappings, or the given mapping by name. - */ -function drush_salesforce_pull_sf_pull_set($name, $time = NULL) { - _drush_salesforce_deprecated(); - if (is_null($time)) { - $time = time(); - } - $mapping = _salesforce_drush_get_mapping($name); - if ($mapping) { - $mapping->setLastPullTime($time); - \Drupal::entityTypeManager() - ->getStorage('salesforce_mapped_object') - ->setForcePull($mapping); - } -} diff --git a/modules/salesforce_pull/salesforce_pull.info.yml b/modules/salesforce_pull/salesforce_pull.info.yml index 5b497123..c93ae4ea 100644 --- a/modules/salesforce_pull/salesforce_pull.info.yml +++ b/modules/salesforce_pull/salesforce_pull.info.yml @@ -2,6 +2,6 @@ name: Salesforce Pull type: module description: Imports objects from Salesforce based on mappings defined in Salesforce Mapping. package: Salesforce -core_version_requirement: ^9.1 +core_version_requirement: ^9 || ^10 dependencies: - salesforce:salesforce_mapping diff --git a/modules/salesforce_pull/tests/src/Functional/PullQueueTest.php b/modules/salesforce_pull/tests/src/Functional/PullQueueTest.php index a388627a..64901206 100644 --- a/modules/salesforce_pull/tests/src/Functional/PullQueueTest.php +++ b/modules/salesforce_pull/tests/src/Functional/PullQueueTest.php @@ -76,8 +76,18 @@ class PullQueueTest extends BrowserTestBase { $this->assertEquals('SALESFORCE TEST', $createdEntity->getTitle()); $this->assertEquals($data->getSobject() ->field('Email'), $createdEntity->field_salesforce_test_email->value); - $this->assertEquals(date('Y-m-d', strtotime($data->getSobject() - ->field('Birthdate'))), date('Y-m-d', strtotime($createdEntity->field_salesforce_test_date->value))); + + $birthdate = $data->getSobject()->field('Birthdate'); + if ($birthdate === NULL) { + $this->assertNull($createdEntity->field_salesforce_test_date->value); + } + else { + $this->assertEquals( + date('Y-m-d', strtotime($birthdate)), + date('Y-m-d', strtotime($createdEntity->field_salesforce_test_date->value)) + ); + } + $this->assertEquals((boolean) $data->getSobject() ->field('d5__Do_Not_Mail__c'), (boolean) $createdEntity->field_salesforce_test_bool->value); $this->assertEquals($data->getSobject() diff --git a/modules/salesforce_pull/tests/src/Unit/DeleteHandlerTest.php b/modules/salesforce_pull/tests/src/Unit/DeleteHandlerTest.php index cf531814..c30e19e6 100644 --- a/modules/salesforce_pull/tests/src/Unit/DeleteHandlerTest.php +++ b/modules/salesforce_pull/tests/src/Unit/DeleteHandlerTest.php @@ -144,7 +144,7 @@ class DeleteHandlerTest extends UnitTestCase { // Mock event dispatcher. $prophecy = $this->prophesize(ContainerAwareEventDispatcher::CLASS); - $prophecy->dispatch(Argument::any(), Argument::any())->willReturn(); + $prophecy->dispatch(Argument::any(), Argument::any())->willReturnArgument(0); $this->ed = $prophecy->reveal(); $this->dh = new DeleteHandler( diff --git a/modules/salesforce_pull/tests/src/Unit/QueueHandlerTest.php b/modules/salesforce_pull/tests/src/Unit/QueueHandlerTest.php index 08667a88..8456a16c 100644 --- a/modules/salesforce_pull/tests/src/Unit/QueueHandlerTest.php +++ b/modules/salesforce_pull/tests/src/Unit/QueueHandlerTest.php @@ -118,7 +118,7 @@ class QueueHandlerTest extends UnitTestCase { // Mock event dispatcher. $prophecy = $this->prophesize(EventDispatcherInterface::CLASS); - $prophecy->dispatch(Argument::any(), Argument::any())->willReturn(); + $prophecy->dispatch(Argument::any(), Argument::any())->willReturnArgument(0); $this->ed = $prophecy->reveal(); $this->time = $this->getMockBuilder(TimeInterface::CLASS)->getMock(); diff --git a/modules/salesforce_push/composer.json b/modules/salesforce_push/composer.json index a2a6c7ee..ee4c4b95 100644 --- a/modules/salesforce_push/composer.json +++ b/modules/salesforce_push/composer.json @@ -8,13 +8,10 @@ "email": "aaron@messageagency.com" } ], - "require": { - "php": ">=5.6.0" - }, "extra": { "drush": { "services": { - "drush.services.yml": "^9" + "drush.services.yml": "^9 || ^10 || ^11" } } } diff --git a/modules/salesforce_push/salesforce_push.drush.inc b/modules/salesforce_push/salesforce_push.drush.inc deleted file mode 100644 index 42e483a6..00000000 --- a/modules/salesforce_push/salesforce_push.drush.inc +++ /dev/null @@ -1,108 +0,0 @@ -<?php - -/** - * @file - * Drush tools for the Salesforce Push module. - */ - -/** - * Implements hook_drush_command(). - */ -function salesforce_push_drush_command() { - $items = []; - - $items['sf-push-queue'] = [ - 'category' => 'salesforce', - 'description' => 'Process push queues (as though during cron) for one or all Salesforce Mappings.', - 'aliases' => ['sfpushq', 'sfpm'], - 'arguments' => [ - 'name' => [ - 'description' => 'Machine name of the Salesforce Mapping for which to process push queue. If omitted, process all queues.', - ], - ], - 'examples' => [ - 'drush sfpushq' => 'Process all push queue items', - 'drush sfpushq foo' => 'Process push queue items for mapping "foo"', - ], - ]; - - // Push entities in a mapping that have not yet been pushed. - $items['salesforce-push-unmapped'] = [ - 'description' => "Attempt to push entities of a mapped type that are not linked to Salesforce Objects.", - 'aliases' => ['sfpu'], - 'bootstrap' => DRUSH_BOOTSTRAP_DRUPAL_FULL, - 'core' => ['8+'], - 'arguments' => [ - 'mapping_id' => 'The Drupal machine name of the mapping for the entities.', - ], - 'options' => [ - 'count' => [ - 'description' => 'The number of entities to try to sync. (Default is 50).', - 'example-value' => 42, - ], - ], - 'examples' => [ - 'drush sfpu foo' => 'Push 50 drupal entities without mapped objects for mapping "foo"', - 'drush sfpu foo --count=42' => 'Push 42 unmapped drupal entities without mapped objects for mapping "foo"', - ], - ]; - - return $items; -} - -/** - * Implements drush_hook_COMMAND(). - */ -function drush_salesforce_push_sf_push_queue($name = NULL) { - _drush_salesforce_deprecated(); - $queue = \Drupal::service('queue.salesforce_push'); - if ($name !== NULL) { - if (!($mapping = _salesforce_drush_get_mapping($name))) { - return; - } - // Process one mapping queue. - $queue->processQueue($mapping); - } - else { - // Process all queues. - $queue->processQueues(); - } -} - -/** - * Implements drush_hook_COMMAND(). - * - * Search for entities without Salesforce Object mappings and attempt to push - * them to Salesforce. - */ -function drush_salesforce_push_unmapped($name = NULL) { - _drush_salesforce_deprecated(); - if (!($mapping = _salesforce_drush_get_mapping($name))) { - return; - } - - $entity_type = $mapping->get('drupal_entity_type'); - $etm = \Drupal::entityTypeManager(); - $entity_storage = $etm->getStorage($entity_type); - $entity_keys = $etm->getDefinition($entity_type)->getKeys(); - $id_key = $entity_keys['id']; - $bundle_key = empty($entity_keys['bundle']) ? FALSE : $entity_keys['bundle']; - $query = \Drupal::database()->select($entity_storage->getBaseTable(), 'b'); - $query->leftJoin('salesforce_mapped_object', 'm', "b.$id_key = m.drupal_entity__target_id AND m.drupal_entity__target_type = '$entity_type'"); - if ($bundle_key) { - $query->condition("b.$bundle_key", $mapping->get('drupal_bundle')); - } - $results = $query - ->fields('b', [$id_key]) - ->isNull('m.drupal_entity__target_id') - ->range(0, drush_get_option('count', 50)) - ->execute() - ->fetchAllAssoc($id_key); - $entities = $entity_storage->loadMultiple(array_keys($results)); - $log = []; - foreach ($entities as $entity) { - salesforce_push_entity_crud($entity, 'push_create'); - $log[] = $entity->id(); - } - drush_print(count($log) . " unmapped entities found and push to Salesforce attempted. See logs for more details."); -} diff --git a/modules/salesforce_push/salesforce_push.info.yml b/modules/salesforce_push/salesforce_push.info.yml index 1f476102..c453d48d 100644 --- a/modules/salesforce_push/salesforce_push.info.yml +++ b/modules/salesforce_push/salesforce_push.info.yml @@ -2,6 +2,6 @@ name: Salesforce Push type: module description: Push data to Salesforce when updates are made to Drupal entities. package: Salesforce -core_version_requirement: ^9.1 +core_version_requirement: ^9 || ^10 dependencies: - salesforce:salesforce_mapping diff --git a/modules/salesforce_push/tests/src/Unit/PushQueueTest.php b/modules/salesforce_push/tests/src/Unit/PushQueueTest.php index 04a56638..d9aeb638 100644 --- a/modules/salesforce_push/tests/src/Unit/PushQueueTest.php +++ b/modules/salesforce_push/tests/src/Unit/PushQueueTest.php @@ -59,7 +59,7 @@ class PushQueueTest extends UnitTestCase { $this->eventDispatcher = $this->getMockBuilder(EventDispatcherInterface::CLASS)->getMock(); $this->eventDispatcher->expects($this->any()) ->method('dispatch') - ->willReturn(NULL); + ->willReturnArgument(0); $this->string_translation = $this->getMockBuilder(TranslationInterface::class)->getMock(); $this->time = $this->getMockBuilder(TimeInterface::class)->getMock(); diff --git a/modules/salesforce_push/tests/src/Unit/SalesforcePushQueueProcessorRestTest.php b/modules/salesforce_push/tests/src/Unit/SalesforcePushQueueProcessorRestTest.php index ba3ae473..e73623b8 100644 --- a/modules/salesforce_push/tests/src/Unit/SalesforcePushQueueProcessorRestTest.php +++ b/modules/salesforce_push/tests/src/Unit/SalesforcePushQueueProcessorRestTest.php @@ -7,6 +7,7 @@ use Drupal\Core\DependencyInjection\ContainerBuilder; use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Entity\Sql\SqlEntityStorageInterface; use Drupal\Core\Queue\SuspendQueueException; +use Drupal\Core\StringTranslation\TranslatableMarkup; use Drupal\salesforce\EntityNotFoundException; use Drupal\salesforce\SalesforceAuthProviderPluginManager; use Drupal\Tests\UnitTestCase; @@ -48,10 +49,15 @@ class SalesforcePushQueueProcessorRestTest extends UnitTestCase { $this->eventDispatcher = $this->getMockBuilder(EventDispatcherInterface::CLASS)->getMock(); $this->eventDispatcher->expects($this->any()) ->method('dispatch') - ->willReturn(NULL); + ->willReturnArgument(0); $this->entity_manager = $this->getMockBuilder(EntityTypeManagerInterface::class)->getMock(); $this->string_translation = $this->getMockBuilder(TranslationInterface::class)->getMock(); + $this->string_translation->expects($this->any()) + ->method('translateString') + ->willReturnCallback(function (TranslatableMarkup $markup) { + return $markup->getUntranslatedString(); + }); $this->mapping = $this->getMockBuilder(SalesforceMappingInterface::CLASS)->getMock(); diff --git a/modules/salesforce_soap/composer.json b/modules/salesforce_soap/composer.json index 859c77f4..b31a88ee 100644 --- a/modules/salesforce_soap/composer.json +++ b/modules/salesforce_soap/composer.json @@ -25,7 +25,7 @@ "source": "http://cgit.drupalcode.org/salesforce" }, "require": { - "messageagency/force.com-toolkit-for-php": "^1.0.1", + "messageagency/force.com-toolkit-for-php": "^1.0.2", "ext-soap": "*" } } diff --git a/modules/salesforce_soap/salesforce_soap.info.yml b/modules/salesforce_soap/salesforce_soap.info.yml index bdd49fac..3c6e5263 100644 --- a/modules/salesforce_soap/salesforce_soap.info.yml +++ b/modules/salesforce_soap/salesforce_soap.info.yml @@ -2,6 +2,6 @@ name: Salesforce Soap type: module description: Exposes a SoapClient service for communicating with the Salesforce SOAP API. package: Salesforce -core_version_requirement: ^9.1 +core_version_requirement: ^9 || ^10 dependencies: - salesforce:salesforce diff --git a/modules/salesforce_webform/composer.json b/modules/salesforce_webform/composer.json index 77fd08ad..ca37d48d 100644 --- a/modules/salesforce_webform/composer.json +++ b/modules/salesforce_webform/composer.json @@ -8,13 +8,10 @@ "email": "author@example.com" } ], - "require": { - "php": ">=5.6.0" - }, "extra": { "drush": { "services": { - "drush.services.yml": "^9" + "drush.services.yml": "^9 || ^10 || ^11" } } } diff --git a/modules/salesforce_webform/salesforce_webform.info.yml b/modules/salesforce_webform/salesforce_webform.info.yml index 0c4c230e..c7e28be8 100644 --- a/modules/salesforce_webform/salesforce_webform.info.yml +++ b/modules/salesforce_webform/salesforce_webform.info.yml @@ -2,8 +2,8 @@ name: Salesforce Webform type: module description: Adds support for webforms fields in Salesforce Mapping. package: Salesforce -core_version_requirement: ^9.1 +core_version_requirement: ^9 || ^10 dependencies: - salesforce:salesforce_mapping - - webform:webform (>= 5.9-beta1) + - webform:webform diff --git a/salesforce.drush.inc b/salesforce.drush.inc deleted file mode 100644 index 43b84333..00000000 --- a/salesforce.drush.inc +++ /dev/null @@ -1,492 +0,0 @@ -<?php - -/** - * @file - * Drush integration for Salesforce. - */ - -use Drupal\salesforce\SFID; -use Drupal\salesforce\SelectQuery; -use Drupal\salesforce\Exception as SalesforceException; - -/** - * Implements hook_drush_command(). - */ -function salesforce_drush_command() { - $items['sf-rest-version'] = [ - 'description' => 'Displays information about the current REST API version', - 'aliases' => ['sfrv'], - ]; - - $items['sf-list-objects'] = [ - 'description' => 'List the objects that are available in your organization and available to the logged-in user.', - 'aliases' => ['sflo'], - ]; - - $items['sf-describe-object'] = [ - 'description' => 'Retrieve all the metadata for an object, including information about each field, URLs, and child relationships.', - 'aliases' => ['sfdo'], - 'arguments' => [ - 'object' => 'The object name in Salesforce.', - ], - 'options' => [ - 'output' => "Specify an output type. -Options are: -info: (default) Display metadata about an object -fields: Display information about fields that are part of the object -field-data FIELDNAME: Display information about a specific field that is part of an object -raw: Display the complete, raw describe response.", - ], - 'examples' => [ - 'drush sfdo Contact' => 'Show metadata about Contact SObject type.', - 'drush sfdo Contact --output=fields' => 'Show addtional metadata about Contact fields.', - 'drush sfdo Contact --output=field --field=Email' => 'Show full metadata about Contact.Email field.', - 'drush sfdo Contact --output=raw' => 'Display the full metadata for Contact SObject type.', - ], - ]; - - $items['sf-list-resources'] = [ - 'description' => 'Lists the resources available for the specified API version. It provides the name and URI of each resource.', - 'aliases' => ['sflr'], - ]; - - $items['sf-read-object'] = [ - 'description' => 'Retrieve all the data for an object with a specific ID.', - 'aliases' => ['sfro'], - 'arguments' => [ - 'id' => 'The object ID in Salesforce.', - ], - 'options' => [ - 'format' => [ - 'description' => 'Format to output the object. Use "print_r" for print_r (default), "export" for var_export, and "json" for JSON.', - 'example-value' => 'export', - ], - ], - ]; - - $items['sf-create-object'] = [ - 'description' => 'Create an object with specified data.', - 'aliases' => ['sfco'], - 'arguments' => [ - 'object' => 'The object type name in Salesforce (e.g. Account).', - 'data' => 'The data to use when creating the object (default is JSON format). Use \'-\' to read the data from STDIN.', - ], - 'options' => [ - 'format' => [ - 'description' => 'Format to parse the object. Use "json" for JSON (default) or "query" for data formatted like a query string, e.g. \'Company=Foo&LastName=Bar\'.', - 'example-value' => 'json', - ], - ], - ]; - - $items['sf-query-object'] = [ - 'description' => 'Query an object using SOQL with specified conditions.', - 'aliases' => ['sfqo'], - 'arguments' => [ - 'object' => 'The object type name in Salesforce (e.g. Account).', - ], - 'options' => [ - 'format' => [ - 'description' => 'Format to output the objects. Use "print_r" for print_r (default), "export" for var_export, and "json" for JSON.', - 'example-value' => 'export', - ], - 'where' => [ - 'description' => 'A WHERE clause to add to the SOQL query', - ], - 'fields' => [ - 'description' => 'A comma-separated list fields to select in the SOQL query. If absent, an API call is used to find all fields', - ], - 'limit' => [ - 'description' => 'Integer limit on the number of results to return for the query.', - ], - 'order' => [ - 'description' => 'Comma-separated fields by which to sort results. Make sure to enclose in quotes for any whitespace.', - ], - ], - ]; - - $items['sf-execute-query'] = [ - 'description' => 'Execute a SOQL query.', - 'aliases' => ['sfeq', 'soql'], - 'arguments' => [ - 'query' => 'The query to execute.', - ], - ]; - - $items['sf-pull-query'] = [ - 'description' => 'Given a mapping, enqueue records for pull from Salesforce, ignoring modification timestamp. This command is useful, for example, when seeding content for a Drupal site prior to deployment.', - 'aliases' => ['sfpq', 'sfiq'], - 'arguments' => [ - 'name' => 'Machine name of the Salesforce Mapping for which to queue pull records.', - ], - 'options' => [ - 'where' => [ - 'description' => 'A WHERE clause to add to the SOQL pull query. Default behavior is to query and pull all records.', - ], - ], - 'examples' => [ - 'drush sfpq' => 'Interactively select a mapping for which to queue records.', - 'drush sfpq user' => 'Query and queue all records for "user" Salesforce mapping.', - 'drush sfpq user --where="Email like \'%foo%\' AND (LastName = \'bar\' OR FirstName = \'bar\')"' => 'Query and queue all records for "user" Salesforce mapping with Email field containing the string "foo" and First or Last name equal to "bar"', - ], - ]; - - $items['sf-pull-file'] = [ - 'description' => 'Given a mapping, enqueue a list of object IDs to be pulled from a CSV file, e.g. a Salesforce report. The first column of the CSV file must be SFIDs. Additional columns will be ignored.', - 'aliases' => ['sfpf', 'sfif'], - 'arguments' => [ - 'file' => 'CSV file name of 15- or 18-character Salesforce ids to be pulled. ', - 'name' => 'Machine name of the Salesforce Mapping for which to queue pull records.', - ], - ]; - - return $items; -} - -/** - * List the resources available for the specified API version. - * - * This command provides the name and URI of each resource. - */ -function drush_salesforce_sf_list_resources() { - _drush_salesforce_deprecated(); - $salesforce = \Drupal::service('salesforce.client'); - $resources = $salesforce->listResources(); - if ($resources) { - $items[] = ['Resource', 'URL']; - foreach ($resources->resources as $resource => $url) { - $items[] = [$resource, $url]; - } - drush_print("The following resources are available:\n"); - drush_print_table($items); - } - else { - drush_log('Could not obtain a list of resources!', 'error'); - } -} - -/** - * Describes a Salesforce object. - * - * Use the --fields option to display information about the fields of an object, - * or the --field-data option to display information about a single field in an - * object. - * - * @param string $object_name - * The name of a Salesforce object to query. - */ -function drush_salesforce_sf_describe_object($object_name = NULL) { - _drush_salesforce_deprecated(); - - if (!$object_name) { - return drush_log('Please specify an object as an argument.', 'error'); - } - $salesforce = \Drupal::service('salesforce.client'); - - $object = $salesforce->objectDescribe($object_name); - - // Return if we cannot load any data. - if (!is_object($object)) { - return drush_log(dt('Could not load data for object !object', ['!object' => $object_name]), 'error'); - } - - $output = drush_get_option('output'); - switch ($output) { - case 'raw': - drush_print_r($object->data); - return; - - case 'fields': - $rows = [['Name', 'Type', 'Label']]; - foreach ($object->fields as $field) { - $rows[] = [$field['name'], $field['type'], $field['label']]; - } - drush_print_table($rows, TRUE); - return; - - case 'field': - $fieldname = drush_get_option('field'); - if (empty($fieldname)) { - drush_log(dt('Please specify a field name'), 'error'); - return; - } - try { - $field_data = $object->getField($fieldname); - } - catch (\Exception $e) { - watchdog_exception('salesforce.drush', $e); - drush_log(dt('Could not load data for field !field on !object object', [ - '!field' => $fieldname, - '!object' => $object_name, - ]), 'error'); - return; - } - drush_print_r($field_data); - return; - - default: - if ($output != 'info') { - drush_log(dt('Unkonwn output option !output', ['!output' => $output]), 'error'); - return; - } - - // Display information about the object. - $rows = []; - $rows[] = ['Name', $object->name]; - $rows[] = ['Label', $object->label]; - $rows[] = ['Field count', count($object->getFields())]; - $rows[] = ['SFID prefix', $object->keyPrefix]; - $rows[] = [ - 'Child Relationships', - isset($object->childRelationships) ? count($object->childRelationships) : 0, - ]; - - $rows[] = ['Searchable', ($object->searchable == 1) ? 'TRUE' : 'FALSE']; - $rows[] = ['Creatable', ($object->createable == 1) ? 'TRUE' : 'FALSE']; - $rows[] = ['Deletable', ($object->deletable == 1) ? 'TRUE' : 'FALSE']; - $rows[] = ['Mergeable', ($object->mergeable == 1) ? 'TRUE' : 'FALSE']; - $rows[] = ['Queryable', ($object->queryable == 1) ? 'TRUE' : 'FALSE']; - drush_print_table($rows); - return; - } -} - -/** - * Displays information about the REST API version the site is using. - */ -function drush_salesforce_sf_rest_version() { - _drush_salesforce_deprecated(); - - $salesforce = \Drupal::service('salesforce.client'); - $version_id = $salesforce->getApiVersion(); - $versions = $salesforce->getVersions(); - $version = $versions[$version_id]; - $latest = array_pop($versions); - - foreach ($version as $key => $value) { - $rows[] = [$key, $value]; - } - $rows[] = ['login url', $salesforce->getLoginUrl()]; - $rows[] = [ - 'latest version', - strcmp($version_id, $latest['version']) ? $latest['version'] : 'Yes', - ]; - drush_print_table($rows, TRUE); -} - -/** - * List Salesforce objects. - * - * This command lists Salesforce objects that are available in your organization - * and available to the logged-in user. - */ -function drush_salesforce_sf_list_objects() { - _drush_salesforce_deprecated(); - $salesforce = \Drupal::service('salesforce.client'); - if ($objects = $salesforce->objects()) { - print_r($objects); - drush_print('The following objects are available in your organization and available to the logged-in user.'); - $rows[] = ['Name', 'Label', 'Label Plural']; - foreach ($objects as $object) { - $rows[] = [ - $object['name'], - $object['label'], - $object['labelPlural'], - ]; - } - drush_print_table($rows, TRUE); - } - else { - drush_log('Could not load any information about available objects.', 'error'); - } - -} - -/** - * Read a Salesforce object available to the logged-in user. - * - * @param string $id - * The Salesforce ID. - * - * @throws \Exception - */ -function drush_salesforce_sf_read_object($id) { - _drush_salesforce_deprecated(); - $salesforce = \Drupal::service('salesforce.client'); - try { - $name = $salesforce->getObjectTypeName(new SFID($id)); - if ($object = $salesforce->objectRead($name, $id)) { - drush_print(dt('!type with id !id:', [ - '!type' => $object->type(), - '!id' => $object->id(), - ])); - drush_print(drush_format($object->fields())); - } - } - catch (SalesforceException $e) { - drush_log($e->getMessage(), 'error'); - } -} - -/** - * Create a Salesforce object available to the logged-in user. - * - * @param string $name - * The object type name, e.g. Account. - * @param string $data - * The object data, or '-' to read from stdin. - */ -function drush_salesforce_sf_create_object($name, $data) { - _drush_salesforce_deprecated(); - - if ($data == '-') { - $data = stream_get_contents(STDIN); - } - $format = drush_get_option('format', 'json'); - $params = []; - switch ($format) { - case 'query': - parse_str($data, $params); - break; - - case 'json': - $params = json_decode($data, TRUE); - break; - - default: - drush_log(dt('Invalid format'), 'error'); - return; - } - $salesforce = \Drupal::service('salesforce.client'); - try { - if ($result = $salesforce->objectCreate($name, $params)) { - drush_print_r($result); - } - } - catch (SalesforceException $e) { - drush_log($e->getMessage(), 'error'); - } -} - -/** - * Query Salesforce objects available to the logged-in user. - * - * @param string $name - * The object type name, e.g. Account. - */ -function drush_salesforce_sf_query_object($name) { - _drush_salesforce_deprecated(); - $salesforce = \Drupal::service('salesforce.client'); - - $query = new SelectQuery($name); - - $fields = drush_get_option('fields', ''); - if (!$fields) { - $object = $salesforce->objectDescribe($name); - $query->fields = array_keys($object->getFields()); - } - else { - $query->fields = explode(',', $fields); - } - - $query->limit = drush_get_option('limit', ''); - - if ($where = drush_get_option('where', '')) { - $query->conditions = [[$where]]; - } - - if ($order = drush_get_option('order', '')) { - $query->order = []; - $orders = explode(',', $order); - foreach ($orders as $order) { - list($field, $dir) = preg_split('/\s+/', $order, 2); - $query->order[$field] = $dir; - } - } - - try { - $result = $salesforce->query($query); - } - catch (SalesforceException $e) { - drush_log($e->getMessage(), 'error'); - return; - } - - foreach ($result->records() as $sfid => $record) { - drush_print(drush_format([$sfid => $record->fields()])); - } - $pretty_query = str_replace('+', ' ', (string) $query); - if (!$fields) { - $fields = implode(',', $query->fields); - $pretty_query = str_replace($fields, ' * ', $pretty_query); - } - drush_print(dt("Showing !size of !total records for query:\n!query", [ - '!size' => count($result->records()), - '!total' => $result->size(), - '!query' => $pretty_query, - ])); - -} - -/** - * Execute a SOQL query. - * - * @param string $query - * The query to execute. - */ -function drush_salesforce_sf_execute_query($query = NULL) { - _drush_salesforce_deprecated(); - if (!$query) { - drush_log('Please specify a query as an argument.', 'error'); - return; - } - $salesforce = \Drupal::service('salesforce.client'); - try { - $result = $salesforce->apiCall('query?q=' . urlencode($query)); - drush_print(drush_format($result)); - } - catch (SalesforceException $e) { - drush_log($e->getMessage(), 'error'); - } -} - -/** - * Get a mapping from the given name, or from user input if name is empty. - * - * @param string $name - * The mapping name. - * - * @return \Drupal\salesforce_mapping\Entity\SalesforceMappingInterface|null - * The mapping. - */ -function _salesforce_drush_get_mapping($name = NULL) { - _drush_salesforce_deprecated(); - $mapping_storage = \Drupal::service('entity_type.manager') - ->getStorage('salesforce_mapping'); - - if (empty($name)) { - $choices = array_keys($mapping_storage->loadMultiple()); - if (empty($choices)) { - drush_log(dt('No mappings found.'), 'error'); - return; - } - ksort($choices); - $choice = drush_choice($choices, dt('Enter a number to choose which mapping to use.')); - if ($choice === FALSE) { - return; - } - $name = $choices[$choice]; - } - $mapping = $mapping_storage->load($name); - if (empty($mapping)) { - drush_log(dt('Mapping !name not found.', ['!name' => $name]), 'error'); - } - return $mapping; -} - -/** - * Trigger a deprecation error. - */ -function _drush_salesforce_deprecated() { - trigger_error('Salesforce module support for Drush 8 is deprecated and will be removed in a future release', E_USER_DEPRECATED); -} diff --git a/salesforce.info.yml b/salesforce.info.yml index 9fb7ef22..d48a7cd1 100644 --- a/salesforce.info.yml +++ b/salesforce.info.yml @@ -2,5 +2,5 @@ name: Salesforce Integration type: module description: Modules to integrate Drupal and Salesforce package: Salesforce -core_version_requirement: ^9.1 +core_version_requirement: ^9 || ^10 configure: salesforce.admin_config_salesforce diff --git a/src/Tests/TestHttpClient.php b/src/Tests/TestHttpClient.php index fe2de237..79bb4482 100644 --- a/src/Tests/TestHttpClient.php +++ b/src/Tests/TestHttpClient.php @@ -4,6 +4,7 @@ namespace Drupal\salesforce\Tests; use GuzzleHttp\Client; use GuzzleHttp\Psr7\Response; +use Psr\Http\Message\ResponseInterface; /** * Empty http client. @@ -15,7 +16,7 @@ class TestHttpClient extends Client { /** * We need to override the post() method in order to fake our OAuth process. */ - public function post($url, $headers) { + public function post($uri, array $options = []): ResponseInterface { return new Response(); } diff --git a/src/Tests/TestHttpClientWrapper.php b/src/Tests/TestHttpClientWrapper.php index 0d07746f..619bfb4a 100644 --- a/src/Tests/TestHttpClientWrapper.php +++ b/src/Tests/TestHttpClientWrapper.php @@ -2,6 +2,7 @@ namespace Drupal\salesforce\Tests; +use Drupal\Core\Extension\ExtensionPathResolver; use GuzzleHttp\ClientInterface as GuzzleClientInterface; use OAuth\Common\Http\Client\ClientInterface; use OAuth\Common\Http\Uri\UriInterface; @@ -18,14 +19,23 @@ class TestHttpClientWrapper implements ClientInterface { */ protected $httpClient; + /** + * The extension path resolver. + * + * @var \Drupal\Core\Extension\ExtensionPathResolver + */ + protected $extensionPathResolver; + /** * HttpClientWrapper constructor. * * @param \GuzzleHttp\ClientInterface $httpClient * Guzzle HTTP client service, from core http_client. + * @param */ - public function __construct(GuzzleClientInterface $httpClient) { + public function __construct(GuzzleClientInterface $httpClient, ExtensionPathResolver $extensionPathResolver) { $this->httpClient = $httpClient; + $this->extensionPathResolver = $extensionPathResolver; } /** @@ -39,7 +49,7 @@ class TestHttpClientWrapper implements ClientInterface { ) { // This method is only used to Salesforce OAuth. Based on the given args, // return a hard-coded version of the expected response. - $dir = drupal_get_path('module', 'salesforce') . '/src/Tests/'; + $dir = $this->extensionPathResolver->getPath('module', 'salesforce') . '/src/Tests/'; if ($endpoint->getPath() == '/services/oauth2/token') { switch ($requestBody['grant_type']) { case 'authorization_code': diff --git a/tests/modules/salesforce_test_rest_client/salesforce_test_rest_client.info.yml b/tests/modules/salesforce_test_rest_client/salesforce_test_rest_client.info.yml index aa748a14..5427d74c 100644 --- a/tests/modules/salesforce_test_rest_client/salesforce_test_rest_client.info.yml +++ b/tests/modules/salesforce_test_rest_client/salesforce_test_rest_client.info.yml @@ -2,6 +2,6 @@ name: 'Salesforce Test Rest Client' type: module description: 'Provides a dummy Rest Client for functional tests.' package: Testing -core_version_requirement: ^9.1 +core_version_requirement: ^9 || ^10 dependencies: - salesforce:salesforce diff --git a/tests/modules/salesforce_test_rest_client/src/SalesforceTestRestClientServiceProvider.php b/tests/modules/salesforce_test_rest_client/src/SalesforceTestRestClientServiceProvider.php index 27c26fb8..c989c002 100644 --- a/tests/modules/salesforce_test_rest_client/src/SalesforceTestRestClientServiceProvider.php +++ b/tests/modules/salesforce_test_rest_client/src/SalesforceTestRestClientServiceProvider.php @@ -8,6 +8,7 @@ use Drupal\salesforce\Tests\TestHttpClientWrapper; use Drupal\salesforce\Tests\TestRestClient; use Drupal\salesforce\Tests\TestHttpClientFactory; use Drupal\salesforce\Tests\TestSalesforceAuthProviderPluginManager; +use Symfony\Component\DependencyInjection\Reference; /** * Modifies the salesforce client service. @@ -28,7 +29,8 @@ class SalesforceTestRestClientServiceProvider extends ServiceProviderBase { $container->getDefinition('plugin.manager.salesforce.auth_providers') ->setClass(TestSalesforceAuthProviderPluginManager::class); $container->getDefinition('salesforce.http_client_wrapper') - ->setClass(TestHttpClientWrapper::class); + ->setClass(TestHttpClientWrapper::class) + ->addArgument(new Reference('extension.path.resolver')); } } -- GitLab