Shopify / shopify-api-ruby

ShopifyAPI is a lightweight gem for accessing the Shopify admin REST and GraphQL web services.
MIT License
1.06k stars 470 forks source link

Documentation / Requirements for Private App Usage (v10) #911

Closed MikeParkin closed 3 months ago

MikeParkin commented 2 years ago

Issue summary

Actually writing this because of an issue I raised for the PHP Shopify API library.

https://github.com/Shopify/shopify-php-api/issues/151

In summary, when using a private app only the Shop URL and store API token are required, yet Context::initialize is required to be called with lots of non-required values (the majority of which aren't required if you use the Rest client directly) which is a bit confusing when you're trying to use the library for the first time.

In the pre-10 version of this Ruby library there was a usage description for private apps that was very clear to understand:

https://github.com/Shopify/shopify_api/tree/v9#2a-private-apps https://github.com/Shopify/shopify_api/tree/v9#6a-making-requests-to-the-graphql-api

In the main branch this has been changed to similar documentation to the PHP library:

https://github.com/Shopify/shopify_api#setup-shopify-context

My understanding of Ruby is limited, however from what I can see, although named parameters are supported in Ruby, it still appears that most of the settings are required:

https://github.com/Shopify/shopify_api/blob/main/lib/shopify_api/context.rb#L42

Does this Ruby API actually require session storage, scope etc when using a private app? (The PHP version doesn't need them, but they are still required - you have to pass in dud values). Just seems a bit odd/confusing that I have to "initialize a session" just so that a Rest/GraphQL library can then use them - when the only required values are the host name and store API token.

ShopifyAPI::Context.setup(
  api_key: "<api-key>",
  api_secret_key: "<api-secret-key>",
  host_name: "<application-host-name>",
  scope: "read_orders,read_products,etc",
  session_storage: ShopifyAPI::Auth::FileSessionStorage.new, # See more details below
  is_embedded: true, # Set to true if you are building an embedded app
  is_private: false, # Set to true if you are building a private app
  api_version: "2022-01" # The version of the API you would like to use
)

I'm guessing documentation isn't finished yet for this, but it's causing confusion on the PHP side so thought I'd ask here too!

Specifications

mllemango commented 2 years ago

Hey @MikeParkin we're looking into this!

MikeParkin commented 2 years ago

Hey @milakoeva thanks!

One solution might be a method per app type, so a private app could do:

ShopifyAPI::Context.setupPrivateApp(
  api_key: "<api-key>",
  host_name: "<application-host-name>",
  api_version: "2022-01" # The version of the API you would like to use
)

Just a thought, I'm sure there are lots of solutions!

key88sf commented 2 years ago

@mllemango This is very important. PLEASE update the docs (and necessary code) with clear instructions on how to use this gem with just a private app to make REST API calls. Many, many developers use it this way in a backend system with no browser/oauth system available.

JBEpstein1 commented 2 years ago

@mllemango Thanks for looking into this! I am running into the same issue, poorly documented on how to use the setup context and what is required... Just went from a project on version 9.5 something to 10+ and for the life of me I cannot get my private app to initialize properly

bkroeker commented 2 years ago

Hey, after trial and error and scrounging through shopify's code, I found something that works.

In some kind of initializer:

ShopifyAPI::Context.setup(
  # These values are required but not actually used for private apps
  api_key: "DUMMY_VALUE",
  host_name: "DUMMY_VALUE",
  scope: "DUMMY_VALUE",

  private_shop: ENV.fetch("SHOPIFY_STORE_DOMAIN"),
  api_secret_key: ENV.fetch("SHOPIFY_ADMIN_API_ACCESS_TOKEN"), # Note that this is actually the admin token, not the api secret key

  session_storage: ShopifyAPI::Auth::FileSessionStorage.new, # This is only to be used for testing, more information in session docs
  is_embedded: false, # Set to true if you are building an embedded app
  is_private: true, # Set to true if you are building a private app
  api_version: "2022-01" # The vesion of the API you would like to use
)

Then, later...

session = ShopifyAPI::Utils::SessionUtils.load_current_session
client = ShopifyAPI::Clients::Graphql::Admin.new(session: session)
client.query(query: MY_QUERY)

Tada! Hope this helps!

luigi-zarrelli-latori commented 2 years ago

Hey while looking into this could you also take a look at how to make the context setup work with multiple private apps in different stores e.g. one rails application that supports working with multiple private apps. Beforehand you were able to create a session on request basis but this doesn't work properly currently because you cannot specify the api version (except if you specify it in the path for REST API, unsure about GraphQL API).

github-actions[bot] commented 2 years ago

This issue is stale because it has been open for 90 days with no activity. It will be closed if no further action occurs in 14 days.

MikeParkin commented 2 years ago

This shouldn’t be closed

key88sf commented 2 years ago

@mllemango Can you please update us here?

BenTalagan commented 2 years ago

Got here after receiving an email alert for deprecation of the 2022-01 API, and my first reflex was to update the gem. I discovered afterwards that the simple initialisation method for private apps used in v9 would no longer work, as the gem was completely reworked and the update process was not at all as straightforward as I tought, and finally discovered @bkroeker's hack.

But I'm not a gambler and will stay as much as possible on the V9.5.1 gem as long as the doc is not clear regarding private apps handling.

So unfortunately my solution for now, is just updating the env var SHOPIFY_API_VERSION to 2022-10 and in the gemfile :

gem 'shopify_api', '~> 9.5.1'

I don't know however for how long this will work.

GesJeremie commented 1 year ago

To add up on the solution provided by @bkroeker, I've been able to perform queries with this minimal setup:

class Gateway
  attr_reader :admin_api_access_token
  attr_reader :store_domain

  def initialize(admin_api_access_token:, store_domain:)
    @admin_api_access_token = admin_api_access_token
    @store_domain = store_domain
  end

  def query(query, variables)
    graphql_client.query(
      query: query,
      variables: variables
    )
  end

  private

  def graphql_client
    @graphql_client ||= ShopifyAPI::Clients::Graphql::Admin.new(session: session)
  end

  def session
    @session ||= ShopifyAPI::Auth::Session.new(
      shop: store_domain,
      access_token: admin_api_access_token
    )
  end
end

gateway = Gateway.new(
  store_domain: 'unicorn.myshopify.com',
  admin_api_access_token: 'shpat_2a6....'
)

GET_PRODUCTS = <<~GQL
  query getProducts($first: Int!) {
    products(first: $first) {
      edges {
        node {
          id
          title
        }
      }
    }
  }
GQL

gateway.query(GET_PRODUCTS, first: 10)

It's worth noting that I didn't find a way to set the api_version without using Context, therefore the Gateway is always set to the latest stable version.

iyerushalmi commented 1 year ago

I am experiencing the same challenge. @GesJeremie , Is there any way to use this method and use the built in resources i.e. Product.all, Order.all, etc?

maxbause commented 1 year ago

@iyerushalmi I've managed to get the built-in rest resources working:

def get_shop
  session = ShopifyAPI::Auth::Session.new(
    shop: "shop.myshopify.com",
    access_token: "XXX"
  )

  ShopifyAPI::Context.activate_session(session)
  ShopifyAPI::Context.load_rest_resources(api_version: "2023-01")

  ShopifyAPI::Shop.all.first
end
github-actions[bot] commented 1 year ago

This issue is stale because it has been open for 60 days with no activity. It will be closed if no further action occurs in 14 days.

MikeParkin commented 1 year ago

This shouldn’t be closed

github-actions[bot] commented 1 year ago

This issue is stale because it has been open for 60 days with no activity. It will be closed if no further action occurs in 14 days.

MikeParkin commented 1 year ago

Still shouldn’t be closed

key88sf commented 1 year ago

We were able to get Ruby API calls working with our private app using this config:

shopify.rb (config/initializers)

ShopifyAPI::Context.setup(
  api_key: "apk_abcdefg123456789", # API Key from private app (*not* API Access Token)
  api_secret_key: "abcdefg123456789", # API Secret Key
  host: "https://store.myshopify.com", # your shopify store URL
  scope: "", # this is controlled by the scopes set on the private app in shopify
  is_embedded: false, # Set to true if you are building an embedded app
  api_version: "2023-04", # The version of the API you would like to use
  is_private: false, # Not sure why this needs to be false, but setting to true did not work
)

Now to make an API call in Ruby:

token = "12345ffffdddeee" # Set this to the Admin API Access Token from your private app
session = ShopifyAPI::Auth::Session.new(shop: "store.myshopify.com", access_token: token)
customer = ShopifyAPI::Customer.find(session: session, id: 123456)
porterbayne commented 1 year ago

@key88sf what versions of ruby, rails, and the gems are you using? what guide did you start with?

when I use your code in my initializer, I get this error on starting the rails server:

.rbenv/versions/3.0.3/lib/ruby/gems/3.0.0/gems/shopify_api-12.2.1/lib/
shopify_api/context.rb:44:in `setup': missing keyword: :session_storage (ArgumentError)
key88sf commented 1 year ago

@porterbayne Currently using: shopify_api (12.5.0) ruby 2.6.8p205 Rails 6.1.3.1

porterbayne commented 1 year ago

Thanks, I'll try those. I'm on Ruby 3.0.x and Rails 7.

Did you use any particular setup guide? Is there a repo you're OK sharing? I spent a half day getting nowhere on this. :(

key88sf commented 1 year ago

I think I started with the code generated from this: https://github.com/Shopify/shopify_app

Make sure you're using the latest shopify_api gem version.

github-actions[bot] commented 1 year ago

This issue is stale because it has been open for 60 days with no activity. It will be closed if no further action occurs in 14 days.

MikeParkin commented 1 year ago

Still shouldn’t be closed

porterbayne commented 1 year ago

Still a considerable blocker for me.

CodeBrotha commented 1 year ago

This issue is still a blocker for me.

porterbayne commented 1 year ago

Same and so we're unable to build a necessary internal system and using spreadsheets instead.

@ShayneP thank you for assigning it to yourself. Are there any updates?

porterbayne commented 6 months ago

Any updates?

JBEpstein1 commented 6 months ago

@porterbayne for us this format works...

ShopifyAPI::Context.setup( api_key: "DUMMY_VALUE", # It works just fine without these host_name: "DUMMY_VALUE", scope: ENV["SHOPIFY_CONTEXT_SCOPE"],

private_shop: ENV["SHOPIFY_DOMAIN"], api_secret_key: ENV["SHOPIFY_API_PASSWORD"], # Note that this is actually the admin token, not the api secret key

session_storage: ShopifyAPI::Auth::FileSessionStorage.new, # This is only to be used for testing, more information in session docs

is_embedded: false, # Set to true if you are building an embedded app is_private: true, # Set to true if you are building a private app api_version: ENV["SHOPIFY_API_VERSION"] # The vesion of the API you would like to use )

porterbayne commented 5 months ago

Thank you, I will try it soon!

lizkenyon commented 3 months ago

Hi folks 👋

I wanted to flag this documentation on how to make requests for private/merchant custom apps that was previously added. I tested out following this documentation today and it was working for me.

It seems like the one limitation here is that you cannot set the API Version without setting other required context information, which may not be relevant for merchant custom apps, and requires you to set it up with dummy information. I am going to create a issue to look into resolving this.

I am going to close this issue. If there are other limitations you are facing please create a new issues for these so we are best able to track them, and they don't get lost in long comment threads.

For reference here is the simple script using the above documentation to make API calls.

require 'shopify_api'

shop = "liz.myshopify.com"
token = "shpat_12345";

# Make the GraphQL query string
query =<<~QUERY
  {
    products(first: 10) {
      edges {
        cursor
        node {
          id
          title
          onlineStoreUrl
        }
      }
    }
  }
QUERY

session = ShopifyAPI::Auth::Session.new(
  shop: shop,
  access_token: token
);

graphql_client = ShopifyAPI::Clients::Graphql::Admin.new(session: session)
response = graphql_client.query(query: query)

puts response.body['data']
CodeBrotha commented 3 months ago

@lizkenyon Thanks!