Skip to content

Cannot update email marketing consent state if opt-in level doesn't change #1287

Closed
@ClaytonPassmore

Description

@ClaytonPassmore

Issue summary

Before opening this issue, I have:

  • Upgraded to the latest version of the package
    • shopify_api version: 2024-01
    • Ruby version: 3.2.3
    • Operating system: Ubuntu 22.04 LTS
  • Set log_level: :debug in my configuration, if applicable
  • Found a reliable way to reproduce the problem that indicates it's a problem with the package
  • Looked for similar issues in this repository
  • Checked that this isn't an issue with a Shopify API

When updating a customer's email marketing consent state, the Shopify API seems to require both the state and opt_in_level. However, if the opt_in_level does not change, the gem does not send up the opt_in_level part of the hash, which results in a 422.

Aside: The process of updating a customer's email marketing consent is not well documented. The official REST docs have an example titled "update a customer's marketing opt-in state" but it uses the old, deprecated parameters.

Expected behavior

The gem should send up all required parameters, regardless of whether or not they change.

Actual behavior

The gem only sends up fields that change, this makes it impossible to update a customer's email marketing consent state when the opt-in level doesn't change.

Steps to reproduce the problem

  1. Fetch a customer from Shopify
  2. Create a new hash for the email_marketing_consent field that contains all the same attributes, but a different state (e.g. use "subscribed" if the customer was not subscribed)
  3. Try to save the customer

Alternatively, try adding these tests to the test/rest/2024_01/customer_test.rb file.

The first test is a "control" to ensure the test works as expected when the opt-in level changes. This test should pass.

  sig do
    void
  end
  def test_x_control
    session = ShopifyAPI::Auth::Session.new(
      shop: "test-shop.myshopify.io",
      access_token: "this_is_a_test_token"
    )

    # Simulate fetching an existing customer via the API:
    customer = ShopifyAPI::Customer.create_instance(
      session: session,
      data: {
        "id" => 207119551,
        "email_marketing_consent" => {
          "state" => "not_subscribed",
          "opt_in_level" => "confirmed_opt_in",
          "consent_updated_at" => "2023-01-01T00:00:00.000Z"
        }
      }
    )

    # Stub update with full `email_marketing_consent` hash included:
    stub_request(:put, "https://test-shop.myshopify.io/admin/api/2024-01/customers/207119551.json")
      .with(
        # This stub matches because `opt_in_level` changes.
        body: { "customer" => hash_including({"email_marketing_consent" => {"state" => "subscribed", "opt_in_level" => "single_opt_in", "consent_updated_at" => "2024-01-01T00:00:00.000Z"}}) }
      )
      .to_return(status: 200, body: JSON.generate({"customer" => {"email" => "[email protected]", "first_name" => "Bob", "last_name" => "Norman", "id" => 207119551, "accepts_marketing" => false, "created_at" => "2024-01-02T09:24:29-05:00", "updated_at" => "2024-01-02T09:24:29-05:00", "orders_count" => 1, "state" => "disabled", "total_spent" => "199.65", "last_order_id" => 450789469, "note" => nil, "verified_email" => true, "multipass_identifier" => nil, "tax_exempt" => false, "tags" => "L\u00E9on, No\u00EBl", "last_order_name" => "#1001", "currency" => "USD", "phone" => "+16136120707", "addresses" => [{"id" => 207119551, "customer_id" => 207119551, "first_name" => nil, "last_name" => nil, "company" => nil, "address1" => "Chestnut Street 92", "address2" => "", "city" => "Louisville", "province" => "Kentucky", "country" => "United States", "zip" => "40202", "phone" => "555-625-1199", "name" => "", "province_code" => "KY", "country_code" => "US", "country_name" => "United States", "default" => true}], "accepts_marketing_updated_at" => "2005-06-12T11:57:11-04:00", "marketing_opt_in_level" => nil, "tax_exemptions" => [], "email_marketing_consent" => {"state" => "not_subscribed", "opt_in_level" => nil, "consent_updated_at" => "2004-06-13T11:57:11-04:00"}, "sms_marketing_consent" => {"state" => "not_subscribed", "opt_in_level" => "single_opt_in", "consent_updated_at" => "2024-01-02T09:24:29-05:00", "consent_collected_from" => "OTHER"}, "admin_graphql_api_id" => "gid://shopify/Customer/207119551", "default_address" => {"id" => 207119551, "customer_id" => 207119551, "first_name" => nil, "last_name" => nil, "company" => nil, "address1" => "Chestnut Street 92", "address2" => "", "city" => "Louisville", "province" => "Kentucky", "country" => "United States", "zip" => "40202", "phone" => "555-625-1199", "name" => "", "province_code" => "KY", "country_code" => "US", "country_name" => "United States", "default" => true}}}), headers: {})

    customer.email_marketing_consent = {
      "state" => "subscribed",
      "opt_in_level" => "single_opt_in",
      "consent_updated_at" => "2024-01-01T00:00:00.000Z"
    }

    # Trigger put request which will match the stubbed request.
    customer.save

    assert_requested(:put, "https://test-shop.myshopify.io/admin/api/2024-01/customers/207119551.json")
  end

The second test is the same as the first, the only difference is that the opt-in level stays the same. This test currently fails (but should pass when the bug gets fixed).

  sig do
    void
  end
  def test_x_broken
    session = ShopifyAPI::Auth::Session.new(
      shop: "test-shop.myshopify.io",
      access_token: "this_is_a_test_token"
    )

    # Simulate fetching an existing customer via the API:
    customer = ShopifyAPI::Customer.create_instance(
      session: session,
      data: {
        "id" => 207119551,
        "email_marketing_consent" => {
          "state" => "not_subscribed",
          "opt_in_level" => "single_opt_in",
          "consent_updated_at" => "2023-01-01T00:00:00.000Z"
        }
      }
    )

    # Stub update with full `email_marketing_consent` hash included:
    stub_request(:put, "https://test-shop.myshopify.io/admin/api/2024-01/customers/207119551.json")
      .with(
        # This stub isn't going to match because the `opt_in_level` doesn't change and therefore isn't sent up.
        body: { "customer" => hash_including({"email_marketing_consent" => {"state" => "subscribed", "opt_in_level" => "single_opt_in", "consent_updated_at" => "2024-01-01T00:00:00.000Z"}}) }
      )
      .to_return(status: 200, body: JSON.generate({"customer" => {"email" => "[email protected]", "first_name" => "Bob", "last_name" => "Norman", "id" => 207119551, "accepts_marketing" => false, "created_at" => "2024-01-02T09:24:29-05:00", "updated_at" => "2024-01-02T09:24:29-05:00", "orders_count" => 1, "state" => "disabled", "total_spent" => "199.65", "last_order_id" => 450789469, "note" => nil, "verified_email" => true, "multipass_identifier" => nil, "tax_exempt" => false, "tags" => "L\u00E9on, No\u00EBl", "last_order_name" => "#1001", "currency" => "USD", "phone" => "+16136120707", "addresses" => [{"id" => 207119551, "customer_id" => 207119551, "first_name" => nil, "last_name" => nil, "company" => nil, "address1" => "Chestnut Street 92", "address2" => "", "city" => "Louisville", "province" => "Kentucky", "country" => "United States", "zip" => "40202", "phone" => "555-625-1199", "name" => "", "province_code" => "KY", "country_code" => "US", "country_name" => "United States", "default" => true}], "accepts_marketing_updated_at" => "2005-06-12T11:57:11-04:00", "marketing_opt_in_level" => nil, "tax_exemptions" => [], "email_marketing_consent" => {"state" => "not_subscribed", "opt_in_level" => nil, "consent_updated_at" => "2004-06-13T11:57:11-04:00"}, "sms_marketing_consent" => {"state" => "not_subscribed", "opt_in_level" => "single_opt_in", "consent_updated_at" => "2024-01-02T09:24:29-05:00", "consent_collected_from" => "OTHER"}, "admin_graphql_api_id" => "gid://shopify/Customer/207119551", "default_address" => {"id" => 207119551, "customer_id" => 207119551, "first_name" => nil, "last_name" => nil, "company" => nil, "address1" => "Chestnut Street 92", "address2" => "", "city" => "Louisville", "province" => "Kentucky", "country" => "United States", "zip" => "40202", "phone" => "555-625-1199", "name" => "", "province_code" => "KY", "country_code" => "US", "country_name" => "United States", "default" => true}}}), headers: {})

    customer.email_marketing_consent = {
      "state" => "subscribed",
      "opt_in_level" => "single_opt_in",
      "consent_updated_at" => "2024-01-01T00:00:00.000Z"
    }

    # Trigger put request which won't match the stubbed request.
    customer.save

    assert_requested(:put, "https://test-shop.myshopify.io/admin/api/2024-01/customers/207119551.json")
  end

Debug logs

// Paste any relevant logs here

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions