Skip to content

Conversation

@chrislo
Copy link
Contributor

@chrislo chrislo commented Aug 4, 2025

In order to support the ExperienceCS customer success team, this PR ensures that newly-verified School records are synchronised into Salesforce as Account records.

Setting up a Salesforce account and application in the Sandbox

The RPF sandbox salesforce environment is here: https://raspberrypi--digital.sandbox.lightning.force.com

Step 1

Liesl Quinn created me an account using my [email protected] email address. She sent me a password reset email to allow me to set a new password, and changed my profile to allow API access.

Step 2

Once in salesforce I needed to View profile > My personal information > Reset my security token to generate a new security token.

Step 3

I then visited Setup > Setup for current app > platform tools > Apps > External Client Apps > Settings and enabled "Allow creation of connected apps".

I clicked "New Connected App"

I created an editorapi app with OAuth settings:

  • Require PKCE
  • Require secret for web server flow
  • Require secret for refresh token flow
  • Callback URL: https://example.com
  • Selected OAuth Scopes: Full access (full)

I copied the Consumer key and secret from "Manage consumer details".

Connecting to salesforce using restforce

With the above in place I was able to use restforce to connect to salesforce:

client = Restforce.new(
  username:      username,
  password:      password + security_token,
  client_id:     client_id,
  client_secret: client_secret,
  host:          'raspberrypi--digital.sandbox.my.salesforce.com',
  api_version:   '57.0'
)

Where username and password are from step 1, security_token from step 2 and client_id/client_secret from step 3 above. Note that the host is not the same as the URL you use to login to salesforce, lightning.force.com is replaced with my.salesforce.com.

I could then create an Account in Salesforce with

client.create('Account', { Name: 'Test Account' })

TODO

  • Work out which user to associate salesforce editor api application with
  • Create Salesforce application for editor api in production salesforce
  • Associate sandbox editorapi application with correct user
  • Reduce OAuth permissions for editorapi to the minimum required (currently "Full access" in the salesforce sandbox)
  • Set new ENV variables in staging
  • Set new ENV variables in production
  • Work out whether we need to create Client records in Salesforce or if these are already created as part of another sync.
  • Work out whether we need to create an Application record in Salesforce. This is suggested in the google sheet Jason and Emma put together, but I can't see it defined in the sandbox environment
  • Work out how to back-fill all existing verified schools.
  • Decide whether to store the Salesforce ID on the School (or another) record. It feels like this would be useful in case we need to later update the salesforce records.
  • Do we want to try to find existing records for a school in Salesforce?
  • Do we need to filter out "non-ExCS" schools?

@cla-bot cla-bot bot added the cla-signed label Aug 4, 2025
@raspberrypiherokubot raspberrypiherokubot temporarily deployed to editor-api-p-salesforce-llttwu August 4, 2025 14:19 Inactive
@danhalson danhalson requested a review from Copilot October 29, 2025 15:48
Copy link

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 Salesforce integration to synchronize newly-verified School records into Salesforce as Account records, supporting the ExperienceCS customer success team.

  • Adds SalesforceSyncJob to create Salesforce Account records when schools are verified
  • Integrates the restforce gem for Salesforce API communication
  • Configures Salesforce credentials via environment variables

Reviewed Changes

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

Show a summary per file
File Description
app/models/school.rb Enqueues SalesforceSyncJob when a school is verified
app/jobs/salesforce_sync_job.rb New job that creates Salesforce Account records with school data
spec/models/school_spec.rb Adds test verifying job enqueue on school verification
spec/jobs/salesforce_sync_job_spec.rb Comprehensive tests for Salesforce sync job behavior
Gemfile Adds restforce gem dependency
.env.example Documents required Salesforce environment variables

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

Comment on lines +23 to +32
def client
Restforce.new(
username: ENV.fetch('SALESFORCE_USERNAME'),
password: ENV.fetch('SALESFORCE_PASSWORD'),
client_id: ENV.fetch('SALESFORCE_CLIENT_ID'),
client_secret: ENV.fetch('SALESFORCE_CLIENT_SECRET'),
host: ENV.fetch('SALESFORCE_HOST'),
api_version: '57.0'
)
end
Copy link

Copilot AI Oct 29, 2025

Choose a reason for hiding this comment

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

The Restforce client is being instantiated on every job execution. Consider memoizing the client with @client ||= to avoid recreating the connection for potential retry scenarios.

Copilot uses AI. Check for mistakes.
Comment on lines +18 to +19
client.create('Account', account_data)
end
Copy link

Copilot AI Oct 29, 2025

Choose a reason for hiding this comment

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

The Salesforce API call has no error handling. If the API call fails, the job will raise an exception with no context about which school failed to sync. Consider wrapping this in a rescue block that logs the school_id and re-raises, or let the job framework handle retries with better error context.

Suggested change
client.create('Account', account_data)
end
begin
client.create('Account', account_data)
rescue => e
Rails.logger.error("SalesforceSyncJob failed for school_id=#{school_id}: #{e.class} - #{e.message}")
raise
end

Copilot uses AI. Check for mistakes.
Comment on lines +69 to +70

true
Copy link

Copilot AI Oct 29, 2025

Choose a reason for hiding this comment

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

[nitpick] The explicit true return value is unnecessary since perform_later already returns a truthy value. If an explicit boolean return is required for the method contract, consider documenting why this is needed.

Suggested change
true

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants