ibm-apiconnect / devportal-addons

Example addon modules and themes for the API Connect Developer Portal
GNU General Public License v2.0
1 stars 2 forks source link

Is there an example of iterating the $appnode input parameter in the appcreds_sync hooks? #12

Closed m2web closed 2 years ago

m2web commented 2 years ago

I am attempting to get values out of the $appnode input parameter within an apic_app_delete($appnode, $data) hook. I see where it is logged:

[   php-fpm stderr]   588 80eab5:08cd01:af4a9d 2022-06-30 14:52:37: WARNING: [pool www] child 35996 said into stderr: "WATCHDOG: [DEBUG] [appcreds_sync] appcreds_sync_apic_app_delete hook invoked for: O:31:"Drupal\Core\Field\FieldItemList":8:{s:7:"*list";a:1:{i:0;O:52:"Drupal\Core\Field\Plugin\Field\FieldType\IntegerItem":8:{s:13:"*....

However, when attempting to determine the datatype of the $appnode:

\Drupal::logger('appcreds_sync')->debug("What is $appnode?: " . get_debug_type($appnode));

The following is in the log:

 [     nginx stderr]   587 80eab5:08cd01:a86b32 2022-06-30 17:46:31: [error] 1013#1013: *116258 FastCGI sent in stderr: "PHP message: Error: Object of class Drupal\node\Entity\Node could not be converted to string in /web/platforms/devportal-9.x-10.0.4-20211201-1711/sites/wsfg.int-dev.apiportal-intdev10.ws.wsfgrp.net/modules/appcreds_sync/appcreds_sync.module on line 87 #0 [internal function]: appcreds_sync_apic_app_delete()

Also, when attempting:

 \Drupal::logger('appcreds_sync')->debug("What is $appnode?: " . gettype($appnode));

The following is in the log:

 [     nginx stderr]   587 80eab5:08cd01:a86b32 2022-06-30 17:51:22: [error] 1013#1013: *116316 FastCGI sent in stderr: "PHP message: Error: Object of class Drupal\node\Entity\Node could not be converted to string in /web/platforms/devportal-9.x-10.0.4-20211201-1711/sites/wsfg.int-dev.apiportal-intdev10.ws.wsfgrp.net/modules/appcreds_sync/appcreds_sync.module on line 87 #0 [internal function]: appcreds_sync_apic_app_delete()

I understand that this is more of a PHP issue, however, I am trying to use the $appnode param and am stumped. Thanks!

chrisdudley commented 2 years ago

The answer is in the error message:

Object of class Drupal\node\Entity\Node

that’s what it is, it’s a drupal Node entity, a quick Google should find you what that looks like (sorry on a phone so harder to do from here).

stackoverflow likely has lots of examples of using drupal node hooks, this isn’t anything specific to apic or the developer portal, it’s a standard drupal node entity in a standard drupal node hook.

I’m aware there is quite a learning curve with drupal, I remember facing it myself - it is climbable though :-)

please let us know if have further questions

m2web commented 2 years ago

Thanks Chris. As I understand, I will need to iterate the $appnode entity looking for a key:value pair.

While I will continue digging, if you have any examples, please send them my way. Thanks again!

chrisdudley commented 2 years ago

It’s not a map, it’s an object so it likely has getters and setters. What field are you after and I’ll see if I can find the right function?

m2web commented 2 years ago

Thanks Chris! This is very helpful when I know the weekend is gearing up 🙂!

The property I need is "something" like $appnode.app_credentials.client_id.

chrisdudley commented 2 years ago

Aha, it would be one of the more complicated fields ;-)

Here, try this, it will create a new array called $credentials that has all of the application's credentials in it - remember an application can have multiple sets of credentials.

            $appCredentials = $appnode->application_credentials_refs->referencedEntities();
            if ($appCredentials !== NULL && is_array($appCredentials)) {
              foreach ($appCredentials as $cred) {
                $credentials[] = [
                  'client_id' => $cred->client_id(),
                  'name' => $cred->name(),
                  'title' => $cred->title(),
                  'description' => $cred->summary(),
                ];
              }
            }

If you're interested in why this code is so complicated, its due to how the data is stored in drupal. Credentials are a separate 'entity' that are cross-referenced to the application for performance reasons, it means we can really exploit the relational database. The downside is when you want to access the data its a little more complicated - that referencedEntities() function above tells Drupal to actually expand the xref ID and replace it with the target object rather than having to go do another sql query ourselves. The credentials themselves are then also objects and have getters and setters for each field. Its never simple :-)

m2web commented 2 years ago

Awesome! I am on holiday today, with the 4th of July on Monday here in the USA, making a long weekend 🙂 so I will get back to you Tuesday at the latest with the outcome.

Is there is documentation somewhere showing the data models along with class diagrams of what you explained above well?

m2web commented 2 years ago

Chris, I have the following in the appcreds_sync_apic_app_delete hook:

$appCredentials = $appnode->application_credentials_refs->referencedEntities();
if ($appCredentials !== NULL && is_array($appCredentials)) {
\Drupal::logger('appcreds_sync')->debug("appCredentials Array size: " . count($appCredentials));
      foreach ($appCredentials as $cred) {
              $credentials[] = [
                  'client_id' => $cred->client_id(),
                  'name' => $cred->name(),
                  'title' => $cred->title(),
                  'description' => $cred->summary(),
              ];
      }
}

Looking at the log, the appCredentials array is empty:

[   php-fpm stderr]   588 80eab5:08cd01:af4a9d 2022-07-02 15:24:48: WARNING: [pool www] child 35996 said into stderr: "WATCHDOG: [DEBUG] [appcreds_sync] appCredentials Array size: 0 | user: anonymous ...."

Any other ideas? Thanks again for your help!

chrisdudley commented 2 years ago

It might not be a fully populated node, try this:

$fullNode = Node::load($appnode->id());
$appCredentials = $fullNode->application_credentials_refs->referencedEntities();
if ($appCredentials !== NULL && is_array($appCredentials)) {
\Drupal::logger('appcreds_sync')->debug("appCredentials Array size: " . count($appCredentials));
      foreach ($appCredentials as $cred) {
              $credentials[] = [
                  'client_id' => $cred->client_id(),
                  'name' => $cred->name(),
                  'title' => $cred->title(),
                  'description' => $cred->summary(),
              ];
      }
}

You'll need to add use Drupal\node\Entity\Node; to the top of your file too in order to import the Node class.

m2web commented 2 years ago

This line:

$appCredentials = $fullNode->application_credentials_refs->referencedEntities();

return the following in the log:

[   php-fpm stderr]   588 80eab5:08cd01:af4a9d 2022-07-05 14:09:41: WARNING: [pool www] child 80194 said into stderr: "WATCHDOG: [ERROR] [php] Error: Call to a member function referencedEntities() on null in appcreds_sync_apic_app_delete() (line 121 of /web/platforms/devportal-9.x-10.0.4-20211201-1711/sites/wsfg.int-dev.apiportal-intdev10.ws.wsfgrp.net/modules/appcreds_sync/appcreds_sync.module) #0 [internal function]: appcreds_sync_apic_app_delete()"

Again, thank you for your help!

chrisdudley commented 2 years ago

that means that fullNode is null - so likely need to work backwards to see why. Fairly hard for me to debug line by line remotely, i dont think thats very efficient!

You'll need to dump stuff to the log (e.g. send it through serialise first so that its a string and then you can see what you're dealing with.) dumping appnode is likely to be too large, but try appnode->id() to check that it has a valid node id, and then go from there.

m2web commented 2 years ago

The $appnode->id() getter call was returning a string of an integer such as 1779.

I used the following:

if (is_string($appnode->nid)) {
    $debug = $appnode->nid;
}
    else {
    $debug = serialize($appnode->nid);
}
\Drupal::logger('appcreds_sync')->debug('%function hook with $appnode->nid: ' . $debug);

To log:

[   php-fpm stderr]   588 80eab5:08cd01:af4a9d 2022-07-05 17:17:12: WARNING: [pool www] child 1330 said into stderr: "WATCHDOG: [DEBUG] [appcreds_sync] %function hook with $appnode->nid: O:31:"Drupal\Core\Field\FieldItemList":8:{s:7:"*list";a:1:{i:0;O:52:"Drupal\Core\Field\Plugin\Field\FieldType\IntegerItem":8:{s:13:"*definition";O:51:"Drupal\Core\Field\TypedData\FieldItemDataDefinition":2:{s:18:"*fieldDefinition";O:37:"Drupal\Core\Field\BaseFieldDefinition":5:{s:7:"*type";s:7:"integer";s:9:"*schema";N;s:10:"*indexes";a:0:@s:17:"*itemDefinition";r:4;s:13:"*definition";a:6:{s:5:"label";O:48:"Drupal\Core\StringTranslation\TranslatableMarkup":3:{s:9:"*string";s:2:"ID";s:12:"*arguments";a:0:@s:10:"*options";a:0:@}s:9:"read-only";b:1;s:8:"provider";s:4:"node";s:10:"field_name";s:3:"nid";s:11:"entity_type";s:"
[   php-fpm stderr]   588 80eab5:08cd01:af4a9d 2022-07-05 17:17:12: WARNING: [pool www] child 1330 said into stderr: "4:"node";s:6:"bundle";N;}}s:13:"*definition";a:2:{s:4:"type";s:18:"field_item:integer";s:8:"settings";a:6:@s:8:"unsigned";b:1;s:4:"size";s:6:"normal";s:3:"min";s:0:"";s:3:"max";s:0:"";s:6:"prefix";s:0:"";s:6:"suffix";s:0:"";}}s:9:"*values";a:1:@s:5:"value";s:4:"1781";s:13:"*properties";a:0:@s:7:"*name";i:0;s:9:"*parent";r:1;s:14:"*_serviceIds";a:1:@s:16:"typedDataManager";s:18:"typed_data_manager";s:18:"*_entityStorages";a:0:@s:20:"*stringTranslation";N;}}s:11:"*langcode";s:2:"en";s:13:"*definition";r:5;s:7:"*name";s:3:"nid";s:9:"*parent";O:48:"Drupal\Core\Entity\Plugin\DataType\EntityAdapter&quot"
[   php-fpm stderr]   588 80eab5:08cd01:af4a9d 2022-07-05 17:17:12: WARNING: [pool www] child 1330 said into stderr: ";:8:{s:9:"*entity";O:23:"Drupal\node\Entity\Node":29:{s:10:"in_preview";N;s:9:"*values";a:44:{s:3:"nid";a:1:{s:9:"x-default";a:1:{i:0;a:1:@s:5:"value";s:4:"1781";}}s:3:"vid";a:1:{s:9:"x-default";a:1:{i:0;a:1:@s:5:"value";s:4:"1781";}}s:4:"type";a:1:{s:9:"x-default";a:1:{i:0;a:1:@s:9:"target_id";s:11:"application";}}s:4:"uuid";a:1:{s:9:"x-default";a:1:{i:0;a:1:@s:5:"value";s:36:"68ef1fe3-1597-4e2e-9c92-aa020a72d665";}}s:8:"langcode";a:1:{s:9:"x-default";a:1:{i:0;a:1:@s:5:"value";s:2:"en";}}s:12:"revision_uid";a:1:{s:9:"x-default";a:1:{i:0;a:1:@s:9:"target_id";s:1:"9";}}s:18:"revision_timestamp";a:1:{s:9:"x-default";a:1:{i:0;a:1:@s:5:"val"
[   php-fpm stderr]   588 80eab5:08cd01:af4a9d 2022-07-05 17:17:12: WARNING: [pool www] child 1330 said into stderr: "ue";s:10:"1657041427";}}s:12:"revision_log";a:1:{s:9:"x-default";a:0:@}s:16:"revision_default";a:1:{s:9:"x-default";a:1:{i:0;a:1:@s:5:"value";s:1:"1";}}s:17:"isDefaultRevision";a:1:@s:9:"x-default";s:1:"1";s:6:"status";a:1:{s:9:"x-default";a:1:{i:0;a:1:@s:5:"value";s:1:"1";}}s:3:"uid";a:1:{s:9:"x-default";a:1:{i:0;a:1:@s:9:"target_id";s:1:"9";}}s:5:"title";a:1:{s:9:"x-default";a:1:{i:0;a:1:@s:5:"value";s:12:"Test v10 App";}}s:7:"created";a:1:{s:9:"x-default";a:1:{i:0;a:1:@s:5:"value";s:10:"1657041427";}}s:7:"changed";a:1:{s:9:"x-default";a:1:{i:0;a:1:@s:5:"value";s:10:"1657041427";}}s:7:"promote";a:1:{s:9:"x-default""
[   php-fpm stderr]   588 80eab5:08cd01:af4a9d 2022-07-05 17:17:12: WARNING: [pool www] child 1330 said into stderr: ";a:1:{i:0;a:1:@s:5:"value";s:1:"0";}}s:6:"sticky";a:1:{s:9:"x-default";a:1:{i:0;a:1:@s:5:"value";s:1:"0";}}s:16:"default_langcode";a:1:{s:9:"x-default";a:1:{i:0;a:1:@s:5:"value";s:1:"1";}}s:29:"revision_translation_affected";a:1:{s:9:"x-default";a:1:{i:0;a:1:@s:5:"value";s:1:"1";}}s:26:"content_translation_source";a:1:{s:9:"x-default";a:1:{i:0;a:1:@s:5:"value";s:3:"und";}}s:28:"content_translation_outdated";a:1:{s:9:"x-default";a:1:{i:0;a:1:@s:5:"value";s:1:"0";}}s:15:"apic_catalog_id";a:1:{s:9:"x-default";a:1:{i:0;a:1:@s:5:"value";s:36:"3afac7d8-2274-427b-9dac-a2902d5ec087";}}s:15:"apic_created_at";a:1:{s:9:"x-default";a:1:{i:0;a:1:@s:5:"value";s:10:"
[   php-fpm stderr]   588 80eab5:08cd01:af4a9d 2022-07-05 17:17:12: WARNING: [pool www] child 1330 said into stderr: ""1657041426";}}s:13:"apic_hostname";a:1:{s:9:"x-default";a:1:{i:0;a:1:@s:5:"value";s:51:"https://apimgmt-intdev10.ws.wsfgrp.net/consumer-api";}}s:16:"apic_provider_id";a:1:{s:9:"x-default";a:1:{i:0;a:1:@s:5:"value";s:36:"d7a952bf-4740-4eaf-8473-dbaf8293ff19";}}s:10:"apic_state";a:1:{s:9:"x-default";a:1:{i:0;a:1:@s:5:"value";s:7:"enabled";}}s:12:"apic_summary";a:1:{s:9:"x-default";a:1:{i:0;a:2:@s:5:"value";s:23:"Testing v10 Module here";s:6:"format";N;}}s:15:"apic_updated_at";a:1:{s:9:"x-default";a:1:{i:0;a:1:@s:5:"value";s:10:"1657041426";}}s:8:"apic_url";a:1:{s:9:"x-default";a:1:{i:0;a:1:@s:5:"value";s:92:"/consumer-api/apps/50f1d5cf-4098-40ad-b082-a664bd5f1dbd/68357944-107a-4a66-b2c4"
[   php-fpm stderr]   588 80eab5:08cd01:af4a9d 2022-07-05 17:17:12: WARNING: [pool www] child 1330 said into stderr: "-a10af692e0e3";}}s:23:"application_client_type";a:1:{s:9:"x-default";a:1:{i:0;a:1:@s:5:"value";s:12:"confidential";}}s:28:"application_consumer_org_url";a:1:{s:9:"x-default";a:1:{i:0;a:1:@s:5:"value";s:55:"/consumer-api/orgs/50f1d5cf-4098-40ad-b082-a664bd5f1dbd";}}s:28:"application_credentials_refs";a:1:{s:9:"x-default";a:1:{i:0;a:1:@s:9:"target_id";s:3:"265";}}s:16:"application_data";a:1:{s:9:"x-default";a:1:{i:0;a:1:{s:5:"value";s:949:"a:15:{s:4:"type";s:3:"app";s:11:"api_version";s:5:"2.0.0";s:2:"id";s:36:"68357944-107a-4a66-b2c4-a10af692e0e3";s:4:"name";s:12:"test-v10-app";s:5:"title";s:12:"Test v10 App";s:7:"summary";s:23:"Testing v10 Module here";s:5:&quo"
[   php-fpm stderr]   588 80eab5:08cd01:af4a9d 2022-07-05 17:17:12: WARNING: [pool www] child 1330 said into stderr: "t;state";s:7:"enabled";s:14:"image_endpoint";s:101:"https://wsfg.int-dev.apiportal-intdev10.ws.wsfgrp.net/wsfg/int-dev/modules/apic_app/images/app_15.png";s:15:"lifecycle_state";s:10:"production";s:10:"created_at";s:24:"2022-07-05T17:17:06.739Z";s:10:"updated_at";s:24:"2022-07-05T17:17:06.739Z";s:7:"org_url";s:55:"/consumer-api/orgs/50f1d5cf-4098-40ad-b082-a664bd5f1dbd";s:3:"url";s:92:"/consumer-api/apps/50f1d5cf-4098-40ad-b082-a664bd5f1dbd/68357944-107a-4a66-b2c4-a10af692e0e3";s:19:"app_credential_urls";a:1:@i:0;s:141:"/consumer-api/apps/50f1d5cf-4098-40ad-b082-a664bd5f1dbd/68357944-107a-4a66-b2c4-a10af692e0e3/credentials/2b4affa1-3a64-47bb-b09b-116aee56bd4f";s:9:"client_id";s:32:"e0554a207faf63c1abd8dc4fe6d0ea66";}";}}}s:19:"application_enabled";a"
[   php-fpm stderr]   588 80eab5:08cd01:af4a9d 2022-07-05 17:17:12: WARNING: [pool www] child 1330 said into stderr: ":1:{s:9:"x-default";a:1:{i:0;a:1:@s:5:"value";s:4:"true";}}s:14:"application_id";a:1:{s:9:"x-default";a:1:{i:0;a:1:@s:5:"value";s:36:"68357944-107a-4a66-b2c4-a10af692e0e3";}}s:27:"application_lifecycle_state";a:1:{s:9:"x-default";a:1:{i:0;a:1:@s:5:"value";s:10:"PRODUCTION";}}s:16:"application_name";a:1:{s:9:"x-default";a:1:{i:0;a:1:@s:5:"value";s:12:"test-v10-app";}}s:7:"metatag";a:1:{s:9:"x-default";a:0:@}s:4:"path";a:1:{s:9:"x-default";a:1:{i:0;a:1:@s:8:"langcode";s:2:"en";}}s:9:"menu_link";a:1:{s:9:"x-default";a:0:@}s:17:"application_image";a:1:{s:9:"x-default";a:0:@}s:29:"application_lifecycle_pending";a:1:{s:9:"x-default";a:0:@}s:30:"application_redirect_endpoi"
[   php-fpm stderr]   588 80eab5:08cd01:af4a9d 2022-07-05 17:17:12: WARNING: [pool www] child 1330 said into stderr: "nts";a:1:{s:9:"x-default";a:0:@}s:29:"application_subscription_refs";a:1:{s:9:"x-default";a:0:@}}s:9:"*fields";a:0:@s:19:"*fieldDefinitions";N;s:12:"*languages";N;s:14:"*langcodeKey";s:8:"langcode";s:21:"*defaultLangcodeKey";s:16:"default_langcode";s:17:"*activeLangcode";s:9:"x-default";s:18:"*defaultLangcode";s:2:"en";s:15:"*translations";a:1:{s:9:"x-default";a:1:@s:6:"status";i:1;}s:24:"*translationInitialize";b:0;s:14:"*newRevision";b:0;s:20:"*isDefaultRevision";s:1:"1";s:13:"*entityKeys";a:4:@s:6:"bundle";s:11:"application";s:2:"id";s:4:"1781";s:8:"revision";s:4:"1781";s:4:"uuid";s:36:"68ef1fe3-1597-4e2e-9c92-aa020a72d665";s:25:&quo"
[   php-fpm stderr]   588 80eab5:08cd01:af4a9d 2022-07-05 17:17:12: WARNING: [pool www] child 1330 said into stderr: "t;*translatableEntityKeys";a:8:{s:5:"label";a:1:@s:9:"x-default";s:12:"Test v10 App";s:8:"langcode";a:1:@s:9:"x-default";s:2:"en";s:6:"status";a:1:@s:9:"x-default";s:1:"1";s:9:"published";a:1:@s:9:"x-default";s:1:"1";s:3:"uid";a:1:@s:9:"x-default";s:1:"9";s:5:"owner";a:1:@s:9:"x-default";s:1:"9";s:16:"default_langcode";a:1:@s:9:"x-default";s:1:"1";s:29:"revision_translation_affected";a:1:@s:9:"x-default";s:1:"1";}s:12:"*validated";b:0;s:21:"*validationRequired";b:0;s:19:"*loadedRevisionId";s:4:"1781";s:33:"*revisionTranslationAffectedKey";s:29:"revision_translation_affected";s:37:"*enforceRevisionTranslationAffected";a:0:@s:15"
[   php-fpm stderr]   588 80eab5:08cd01:af4a9d 2022-07-05 17:17:12: WARNING: [pool www] child 1330 said into stderr: ":"*entityTypeId";s:4:"node";s:15:"*enforceIsNew";N;s:12:"*typedData";N;s:16:"*cacheContexts";a:0:@s:12:"*cacheTags";a:0:@s:14:"*cacheMaxAge";i:-1;s:14:"*_serviceIds";a:0:@s:18:"*_entityStorages";a:0:@s:12:"*isSyncing";b:0;}s:13:"*definition";O:49:"Drupal\Core\Entity\TypedData\EntityDataDefinition":1:{s:13:"*definition";a:1:{s:11:"constraints";a:2:{s:10:"EntityType";s:4:"node";s:6:"Bundle";a:1:@i:0;s:11:"application";}}}s:7:"*name";N;s:9:"*parent";N;s:14:"*_serviceIds";a:0:@s:18:"*_entityStorages";a:0:@s:20:"*stringTranslation";N;s:19:"*typedDataManager";N;}s:14:"*_serviceIds";a:1:@s:16:"typedDataManager";s:18:"typed_data_manager";s:18:"*_entityStorages";a:0:@s:"
[     nginx stdout]   587 80eab5:08cd01:a86b32 2022-07-05 17:17:12: "10.131.77.205, 10.131.171.35" apiportal-intdev10.ws.wsfgrp.net "POST /wsfg/int-dev/application/68357944-107a-4a66-b2c4-a10af692e0e3/delete HTTP/1.1" 303 518 1322 1324 "https://apiportal-intdev10.ws.wsfgrp.net/wsfg/int-dev/application/68357944-107a-4a66-b2c4-a10af692e0e3/delete" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:102.0) Gecko/20100101 Firefox/102.0" 0.802 "-"
[   php-fpm stderr]   588 80eab5:08cd01:af4a9d 2022-07-05 17:17:12: WARNING: [pool www] child 1330 said into stderr: "20:"*stringTranslation";N;} | user: anonymous | uri: https://wsfg.int-dev.apiportal-intdev10.ws.wsfgrp.net/wsfg/int-dev/application/68357944-107a-4a66-b2c4-a10af692e0e3/delete | referer: https://apiportal-intdev10.ws.wsfgrp.net/wsfg/int-dev/application/68357944-107a-4a66-b2c4-a10af692e0e3/delete
"

I see the client_id key and the value of e0554a207faf63c1abd8dc4fe6d0ea66.

Other than manually rebuilding the $appnode->nid log output into an actual hierarchy, what is the notation for the client_id value in the $appnode->nid object?

m2web commented 2 years ago

Just wanting to follow up on this topic. Any ideas? Thanks in advance as I know you are all busy.

m2web commented 2 years ago

With Anu's help from IBM Support, here is what I did to get the client ID when a Portal Application is deleted.

First, instead of using the _apic_app_delete($appnode, $data) hook, I used the __apic_app_pre_delete(NodeInterface $node, $data) hook. Within this method, I do the following:

if (isset($data['application_credentials_refs'])) { 
    foreach ($data['application_credentials_refs'] as $ref) {
    $cred = ApplicationCredentials::load($ref);
        if ($cred !== NULL) {
             $credentials[] = [ 
            'client_id' => $cred->client_id(),
        ];}
    }
}

I then do the following to get the client Id out of the array:

$clientId = $credentials[0]['client_id'];

Of course, this is an issue when the application has multiple creds, but this worked.

Thanks again for the help!