Skip to content

Integrations/pull contact data#4496

Merged
leogermani merged 15 commits intointegrations/get-fieldsfrom
integrations/pull-contact-data
Mar 3, 2026
Merged

Integrations/pull contact data#4496
leogermani merged 15 commits intointegrations/get-fieldsfrom
integrations/pull-contact-data

Conversation

@leogermani
Copy link
Contributor

@leogermani leogermani commented Feb 18, 2026

All Submissions:

Changes proposed in this Pull Request:

Implements the pull contact data strategy.

It triggers an async pull every five minutes when the user is active in the site.

If the user is returning after some time away:

  • It will trigger an immediate async pull if they were around in the last 24h
  • it will trigger a synchronous pull if last time they were around was over 24h ago

The idea is that we want to get fresh data on their first request to be able to properly segment them.

But, we time cap this - the sync request should not hold the page load for too long.

The goal here is to make sure the user is properly segmented on the first page load when they are back after some time away.

TODOs:

  • We might need to change how we store selected fields when we have more than just keys there (data types, etc)
  • Need to surface pull errors. Wonder if we can combine those with other errors in the dedicated view we are going to build. How to store them?

How to test the changes in this Pull Request:

  1. Open WP Shell and set a selected field for the ESP integration. Ex:
$i = new Newspack\Reader_Activation\Integrations\ESP();
$i->set_selected_fields( [ 'HAS_SOMETHING' ] );
  1. Add a value to this metadata in a contact directly in your ESP dashboard
  2. Visit the site logged in as that reader. Look for something like this in the logs:
[NEWSPACK][Newspack\Reader_Activation\Integrations\Contact_Pull::pull_single_integration]: Pulled data from esp: {"HAS_SOMETHING":"yes"}
[NEWSPACK][Newspack\Reader_Activation\Integrations\Contact_Pull::pull_sync]: Loopback pull succeeded for esp.
  1. Wait 5 minutes - or edit the code to make the interval shorter - and watch it again in the logs
  2. See the actions piling up with: wp action-scheduler action list --hook=newspack_pull_integration_contact_data
  3. Verify that the metadata value was stored as user meta:
newspack_reader_data_item_HAS_SOMETHING                         | yes
  1. Clear the np_integrations_last_pull metadata for the user and add a sleep in the integration callback (or lower the timeout limit).
  2. Confirm you see the error in the log and that an action is scheduled right away (all should be printed in the logs)

Other information:

  • Have you added an explanation of what your changes do and why you'd like us to include them?
  • Have you written new tests for your changes, as applicable?
  • Have you successfully ran tests with your changes locally?

@leogermani leogermani marked this pull request as ready for review February 19, 2026 14:55
@leogermani leogermani requested a review from a team as a code owner February 19, 2026 14:55
@leogermani leogermani requested a review from Copilot February 19, 2026 14:55
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR implements a pull contact data strategy that periodically fetches contact metadata from integrations (e.g., ESPs) and stores it in WordPress user meta. The implementation includes throttling logic to run synchronous pulls when data is stale (>24 hours) and async pulls for fresh data (<24 hours but >5 minutes old), with a 5-second time limit on synchronous operations to avoid blocking page loads.

Changes:

  • Adds Contact_Pull orchestration class to manage sync/async pulling with configurable intervals and time limits
  • Extends Integration base class with pull_contact_data() abstract method and selected fields storage/retrieval
  • Implements ESP integration's pull_contact_data() using Newspack Newsletters API

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
includes/reader-activation/integrations/class-contact-pull.php New class implementing pull orchestration with sync/async logic, time limits, and Action Scheduler integration
includes/reader-activation/integrations/class-integration.php Adds abstract pull_contact_data() method and get/set_selected_fields() for field selection
includes/reader-activation/integrations/class-esp.php Implements pull_contact_data() to fetch metadata from ESP via Newspack Newsletters
includes/reader-activation/class-integrations.php Initializes Contact_Pull hooks and includes new class file
tests/unit-tests/integrations/class-test-integrations.php Comprehensive test coverage for pull functionality including throttling, sync/async behavior, error handling, and field filtering
tests/unit-tests/integrations/class-sample-integration.php Adds pull_contact_data() stub to test integration
tests/unit-tests/integrations/class-failing-sample-integration.php Adds pull_contact_data() and get_incoming_available_contact_fields() stubs

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@leogermani leogermani self-assigned this Feb 19, 2026
@leogermani leogermani added the [Status] Needs Review The issue or pull request needs to be reviewed label Feb 19, 2026
Copy link
Member

@miguelpeixe miguelpeixe left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still haven't gone through testing everything, but I wanted to respond to the sync pull strategy, since it's an important aspect of this.

Comment on lines +42 to +47
/**
* Max seconds for the entire synchronous pull routine.
*
* @var int
*/
const PULL_TIME_LIMIT = 5;
Copy link
Member

@miguelpeixe miguelpeixe Feb 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a lot of seconds to let a reader hanging. I suggest something around 700ms or less

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried 1 but we have to consider that there's the overhead of loading WP... At least in my local environment, not even 2 seconds was enough. I'll leave it at 2 for now.

See my top level comment about yet having to validate if this is gonna be useful or not. Then we can revisit

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we place this behind a filter? Our local envs are notoriously not the fastest.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, good call

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@github-actions github-actions bot added the [Status] Needs Changes or Feedback The issue or pull request needs action from the original creator label Feb 24, 2026
@leogermani
Copy link
Contributor Author

leogermani commented Feb 25, 2026

@miguelpeixe thanks for pointing me to the obvious!

Looks good now, but there's still an open question we might be able to answer later in the project:

If we update the Reader data at that point, will the data be available in time to be used by the content gate checks, and the popups segmentation?

In a quick test I did, the data added is not available in the the_post hook yet, we probably need to reload all usermeta for that user, because the update happened in another request. Need to look a bit deeper into this.

Also, I don't see the data being hydrated into the browser local storage... which I think is expected (and I suspect I saw happening the other day...)

@miguelpeixe
Copy link
Member

In a quick test I did, the data added is not available in the the_post hook yet, we probably need to reload all usermeta for that user, because the update happened in another request. Need to look a bit deeper into this.

Because it executed in a separate PHP process, it's likely that the data was already fetched from the object cache, so it's already in memory and need to be flushed/refetched. Maybe wp_cache_flush_runtime()?

@leogermani
Copy link
Contributor Author

In a quick test I did, the data added is not available in the the_post hook yet, we probably need to reload all usermeta for that user, because the update happened in another request. Need to look a bit deeper into this.

Because it executed in a separate PHP process, it's likely that the data was already fetched from the object cache, so it's already in memory and need to be flushed/refetched. Maybe wp_cache_flush_runtime()?

We can look into it later when we start using this data for segmentation and access control

return new \WP_Error( 'user_not_found', __( 'User not found.', 'newspack-plugin' ) );
}

$contact_data = Newspack_Newsletters_Subscription::get_contact_data( $user->user_email, true );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could use a class_exists here to avoid a fatal error. This could live under can_sync() for broader coverage.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is behind can_sync already

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ugh, morning brain. Sorry about that

*
* @var string
*/
const OPTION_PREFIX = 'np_integration_selected_fields_';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We usually go for newspack_ as the prefix. The database limit is 191 characters, it should hold fine.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

*
* @var string
*/
const LAST_PULL_META = 'np_integrations_last_pull';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, newspack_ as the prefix

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment on lines +116 to +118
if ( ! $this->can_sync() ) {
return new \WP_Error( 'missing_dependency', __( 'ESP Integration is not fully configured.', 'newspack-plugin' ) );
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can benefit from better error handling by setting $return_errors = true in can_sync() here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good call

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@leogermani leogermani requested a review from miguelpeixe March 3, 2026 13:51
Copy link
Member

@miguelpeixe miguelpeixe left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to update the tests

@github-actions github-actions bot added [Status] Approved The pull request has been reviewed and is ready to merge and removed [Status] Needs Review The issue or pull request needs to be reviewed [Status] Needs Changes or Feedback The issue or pull request needs action from the original creator labels Mar 3, 2026
@leogermani leogermani merged commit 3723cef into integrations/get-fields Mar 3, 2026
9 checks passed
@leogermani leogermani deleted the integrations/pull-contact-data branch March 3, 2026 16:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

[Status] Approved The pull request has been reviewed and is ready to merge

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants