Skip to content
This repository was archived by the owner on Jun 1, 2023. It is now read-only.

Commit

Permalink
Send Shopify header for employees (#906)
Browse files Browse the repository at this point in the history
* Add support for Employee Authentication

* Remove unnecessary line

* Stub so we don't need an existing config

* Simplify test

* Add test for handling of general errors

* Move partners URL building to PartnerAPI
  • Loading branch information
andyw8 authored Dec 1, 2020
1 parent fbb4cc4 commit e559d97
Show file tree
Hide file tree
Showing 24 changed files with 178 additions and 28 deletions.
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ group :test do
gem 'mocha', require: false
gem 'minitest', '>= 5.0.0', require: false
gem 'minitest-reporters', require: false
gem 'minitest-fail-fast', require: false
gem 'fakefs', '>= 1.0', require: false
gem 'webmock', require: false
gem 'timecop', require: false
Expand Down
3 changes: 3 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ GEM
hashdiff (1.0.1)
method_source (1.0.0)
minitest (5.14.2)
minitest-fail-fast (0.1.0)
minitest (~> 5)
minitest-reporters (1.4.2)
ansi
builder
Expand Down Expand Up @@ -63,6 +65,7 @@ DEPENDENCIES
byebug
fakefs (>= 1.0)
minitest (>= 5.0.0)
minitest-fail-fast
minitest-reporters
mocha
pry-byebug
Expand Down
13 changes: 6 additions & 7 deletions lib/project_types/node/commands/create.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,11 @@ def call(args, _name)
scopes: 'write_products,write_customers,write_draft_orders',
).write(@ctx)

partners_url = "#{partners_endpoint}/#{form.organization_id}/apps/#{api_client['id']}"
partners_url = ShopifyCli::PartnersAPI.partners_url_for(form.organization_id, api_client['id'], local_debug?)

@ctx.puts(@ctx.message('node.create.info.created', form.title, partners_url))
@ctx.puts(@ctx.message('node.create.info.serve', form.name, ShopifyCli::TOOL_NAME))
@ctx.puts(@ctx.message('node.create.info.install', partners_url, form.title))
@ctx.puts(@ctx.message('apps.create.info.created', form.title, partners_url))
@ctx.puts(@ctx.message('apps.create.info.serve', form.name, ShopifyCli::TOOL_NAME))
@ctx.puts(@ctx.message('apps.create.info.install', partners_url, form.title))
end

def self.help
Expand Down Expand Up @@ -113,9 +113,8 @@ def build(name)
end
end

def partners_endpoint
return 'https://partners.myshopify.io' if @ctx.getenv(ShopifyCli::PartnersAPI::LOCAL_DEBUG)
'https://partners.shopify.com'
def local_debug?
@ctx.getenv(ShopifyCli::PartnersAPI::LOCAL_DEBUG)
end
end
end
Expand Down
6 changes: 0 additions & 6 deletions lib/project_types/node/messages/messages.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,6 @@ module Messages
npm_version_failure: "Failed to get the current npm version. Please make sure it is installed as per " \
"the instructions at https://www.npmjs.com/get-npm.",
},
info: {
created: "{{v}} {{green:%s}} was created in your Partner Dashboard {{underline:%s}}",
serve: "{{*}} Change directories to your new project folder {{green:%s}} and run {{command:%s serve}} " \
"to start a local server",
install: "{{*}} Then, visit {{underline:%s/test}} to install {{green:%s}} on your Dev Store",
},
node_version: "node %s",
npm_version: "npm %s",
},
Expand Down
13 changes: 6 additions & 7 deletions lib/project_types/rails/commands/create.rb
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,11 @@ def call(args, _name)
scopes: 'write_products,write_customers,write_draft_orders',
).write(@ctx)

partners_url = "#{partners_endpoint}/#{form.organization_id}/apps/#{api_client['id']}"
partners_url = ShopifyCli::PartnersAPI.partners_url_for(form.organization_id, api_client['id'], local_debug?)

@ctx.puts(@ctx.message('rails.create.info.created', form.title, partners_url))
@ctx.puts(@ctx.message('rails.create.info.serve', form.name, ShopifyCli::TOOL_NAME))
@ctx.puts(@ctx.message('rails.create.info.install', partners_url, form.title))
@ctx.puts(@ctx.message('apps.create.info.created', form.title, partners_url))
@ctx.puts(@ctx.message('apps.create.info.serve', form.name, ShopifyCli::TOOL_NAME))
@ctx.puts(@ctx.message('apps.create.info.install', partners_url, form.title))
end

def self.help
Expand Down Expand Up @@ -173,9 +173,8 @@ def install_gem(name, version = nil)
Gem.install(@ctx, name, version)
end

def partners_endpoint
return 'https://partners.myshopify.io' if @ctx.getenv(ShopifyCli::PartnersAPI::LOCAL_DEBUG)
'https://partners.shopify.com'
def local_debug?
@ctx.getenv(ShopifyCli::PartnersAPI::LOCAL_DEBUG)
end
end
end
Expand Down
4 changes: 0 additions & 4 deletions lib/project_types/rails/messages/messages.rb
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,6 @@ module Messages
},

info: {
created: "{{v}} {{green:%s}} was created in your Partner Dashboard {{underline:%s}}",
serve: "{{*}} Change directories to your new project folder {{green:%s}} and run {{command:%s serve}} " \
"to start a local server",
install: "{{*}} Then, visit {{underline:%s/test}} to install {{green:%s}} on your Dev Store",
open_new_shell: "{{*}} {{yellow:After installing %s, please open a new Command Prompt or PowerShell " \
"window to continue.}}",
},
Expand Down
4 changes: 3 additions & 1 deletion lib/shopify-cli/api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,9 @@ def current_sha
def default_headers
{
'User-Agent' => "Shopify App CLI #{ShopifyCli::VERSION} #{current_sha} | #{ctx.uname}",
}.merge(auth_headers(token))
}.tap do |headers|
headers['X-Shopify-Cli-Employee'] = '1' if Shopifolk.acting_as_shopify_organization?
end.merge(auth_headers(token))
end

def auth_headers(token)
Expand Down
12 changes: 12 additions & 0 deletions lib/shopify-cli/messages/messages.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,16 @@
module ShopifyCli
module Messages
MESSAGES = {
apps: {
create: {
info: {
created: "{{v}} {{green:%s}} was created in the organization's Partner Dashboard {{underline:%s}}",
serve: "{{*}} Change directories to your new project folder {{green:%s}} and run {{command:%s serve}} " \
"to start a local server",
install: "{{*}} Then, visit {{underline:%s/test}} to install {{green:%s}} on your Dev Store",
},
},
},
core: {
connect: {
help: <<~HELP,
Expand Down Expand Up @@ -296,6 +306,8 @@ module Messages
organization_not_found: "Cannot find a partner organization with that ID",
partners_notice: "Please visit https://partners.shopify.com/ to create a partners account",
},
first_party: "Are you working on a 1P (1st Party) app?",
identified_as_shopify: "We've identified you as a {{green:Shopify}} employee.",
organization: "Partner organization {{green:%s (%s)}}",
organization_select: "Select partner organization",
},
Expand Down
18 changes: 17 additions & 1 deletion lib/shopify-cli/partners_api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class << self
# #### Parameters
# - `ctx`: running context from your command
# - `query_name`: name of the query you want to use, loaded from the `lib/graphql` directory.
# - `**variable`: a hash of variables to be supplied to the query ro mutation
# - `**variables`: a hash of variables to be supplied to the query or mutation
#
# #### Raises
#
Expand All @@ -50,6 +50,13 @@ def query(ctx, query_name, **variables)
end
end

def partners_url_for(organization_id, api_client_id, local_debug)
if ShopifyCli::Shopifolk.acting_as_shopify_organization?
organization_id = 'internal'
end
"#{partners_endpoint(local_debug)}/#{organization_id}/apps/#{api_client_id}"
end

private

def authenticated_req(ctx)
Expand Down Expand Up @@ -105,6 +112,15 @@ def endpoint
return 'https://partners.shopify.com' if ENV[LOCAL_DEBUG].nil?
'https://partners.myshopify.io/'
end

def partners_endpoint(local_debug)
domain = if local_debug
'partners.myshopify.io'
else
'partners.shopify.com'
end
"https://#{domain}"
end
end

def auth_headers(token)
Expand Down
8 changes: 8 additions & 0 deletions lib/shopify-cli/task.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,13 @@ def self.call(*args, **kwargs)
task = new
task.call(*args, **kwargs)
end

private

def wants_to_run_against_shopify_org?
@ctx.puts(@ctx.message('core.tasks.select_org_and_shop.identified_as_shopify'))
message = @ctx.message('core.tasks.select_org_and_shop.first_party')
CLI::UI::Prompt.confirm(message, default: false)
end
end
end
9 changes: 9 additions & 0 deletions lib/shopify-cli/tasks/create_api_client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,15 @@ def call(ctx, org_id:, title:, type:)
redir: [OAuth::REDIRECT_HOST]
)

unless resp
ctx.abort("Error - empty response")
end

errors = resp.dig("errors")
if !errors.nil? && errors.any?
ctx.abort(errors.map { |err| "#{err['field']} #{err['message']}" }.join(", "))
end

user_errors = resp.dig("data", "appCreate", "userErrors")
if !user_errors.nil? && user_errors.any?
ctx.abort(user_errors.map { |err| "#{err['field']} #{err['message']}" }.join(", "))
Expand Down
3 changes: 3 additions & 0 deletions lib/shopify-cli/tasks/ensure_env.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ def call(ctx, regenerate: false, required: [:api_key, :secret])
private

def fetch_org
if Shopifolk.check && wants_to_run_against_shopify_org?
Shopifolk.act_as_shopify_organization
end
orgs = PartnersAPI::Organizations.fetch_with_app(@ctx)
org_id = if orgs.count == 1
orgs.first["id"]
Expand Down
3 changes: 3 additions & 0 deletions lib/shopify-cli/tasks/select_org_and_shop.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ class SelectOrgAndShop < ShopifyCli::Task
def call(ctx, organization_id: nil, shop_domain: nil)
@ctx = ctx
return response(organization_id.to_i, shop_domain) unless organization_id.nil? || shop_domain.nil?
if Shopifolk.check && wants_to_run_against_shopify_org?
Shopifolk.act_as_shopify_organization
end
org = get_organization(organization_id)
shop_domain ||= get_shop_domain(org)
ShopifyCli::Core::Monorail.metadata[:organization_id] = org["id"].to_i
Expand Down
1 change: 1 addition & 0 deletions test/minitest_ext.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ def setup
end

def teardown
ShopifyCli::Shopifolk.reset
# Some tests stub the File class, but we need to call the real methods when checking if the config file has
# changed.
#
Expand Down
1 change: 1 addition & 0 deletions test/project_types/extension/extension_project_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ def test_write_cli_file_create_shopify_cli_yml_file
FileUtils.cd(new_context.root)

ExtensionProject.write_cli_file(context: new_context, type: @test_extension_type.identifier)
::ShopifyCli::Project.clear

assert File.exist?('.shopify-cli.yml')
assert_equal :extension, ShopifyCli::Project.current_project_type
Expand Down
2 changes: 2 additions & 0 deletions test/project_types/node/forms/create_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ class CreateTest < MiniTest::Test

def setup
super
stub_shopify_org_confirmation
ShopifyCli::Shopifolk.stubs(:check)
ShopifyCli::ProjectType.load_type(:node)
end

Expand Down
6 changes: 6 additions & 0 deletions test/project_types/rails/forms/create_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ module Forms
class CreateTest < MiniTest::Test
include TestHelpers::Partners

def setup
super
ShopifyCli::Shopifolk.stubs(:check)
stub_shopify_org_confirmation
end

def test_returns_all_defined_attributes_if_valid
form = ask
assert_equal('test_app', form.name)
Expand Down
13 changes: 13 additions & 0 deletions test/shopify-cli/api_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -139,5 +139,18 @@ def test_load_query_will_not_read_project_type_queries_if_not_in_project
File.expects(:read).with(expected_path).returns('content')
assert_equal('content', new_api.call_load_query('my_query'))
end

def test_include_shopify_cli_header_if_acting_as_shopify_organization
Shopifolk.act_as_shopify_organization
File.stubs(:read)
.with(File.join(ShopifyCli::ROOT, "lib/graphql/api/mutation.graphql"))
.returns(@mutation)
response = stub('response', code: '200', body: '{}')
HttpRequest
.expects(:call)
.with(anything, @mutation, {}, has_entry({ 'X-Shopify-Cli-Employee' => '1' }))
.returns(response)
@api.query('api/mutation')
end
end
end
9 changes: 8 additions & 1 deletion test/shopify-cli/core/monorail_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ module Core
class MonorailTest < MiniTest::Test
def setup
super
CLI::UI::Prompt.stubs(:confirm).returns(true)
stub_shopify_org_confirmation
ShopifyCli::Core::Monorail.metadata = {}
end

Expand Down Expand Up @@ -190,6 +190,13 @@ def enabled_and_consented(enabled, consented)
ShopifyCli::Context.any_instance.stubs(:system?).returns(enabled)
ShopifyCli::Config.stubs(:get_bool).with('analytics', 'enabled').returns(consented)
end

def stub_shopify_org_confirmation(response: true)
CLI::UI::Prompt
.stubs(:confirm)
.with(includes("Are you working a 1P (1st Party) app?"), anything)
.returns(response)
end
end
end
end
4 changes: 3 additions & 1 deletion test/shopify-cli/project_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,12 @@ def test_current_fails_if_no_config
end

def test_write_writes_yaml
create_empty_config
Shopifolk.stubs(:acting_as_shopify_organization?).returns(false)
Dir.stubs(:pwd).returns(@context.root)
ShopifyCli::Project.write(@context, project_type: :node, organization_id: 42)
assert_equal :node, Project.current.config['project_type']
assert_equal 42, Project.current.config['organization_id']
refute Project.current.config['shopify_organization']
end

def test_write_writes_yaml_with_shopify_organization_field
Expand All @@ -59,6 +60,7 @@ def test_write_includes_identifiers
organization_id: 42,
other_option: true,
)
Project.clear
assert Project.current.config['other_option']
end

Expand Down
28 changes: 28 additions & 0 deletions test/shopify-cli/tasks/create_api_client_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,34 @@ def test_call_will_query_partners_dashboard
assert_equal("newapikey", ShopifyCli::Core::Monorail.metadata[:api_key])
end

def test_call_will_return_any_general_errors
stub_partner_req(
'create_app',
variables: {
org: 42,
title: 'Test app',
type: 'public',
app_url: ShopifyCli::Tasks::CreateApiClient::DEFAULT_APP_URL,
redir: ["http://127.0.0.1:3456"],
},
resp: {
'errors': [
{ 'field': 'title', 'message': 'is not a valid title' },
],
}
)

err = assert_raises ShopifyCli::Abort do
Tasks::CreateApiClient.call(
@context,
org_id: 42,
title: 'Test app',
type: 'public',
)
end
assert_equal("{{x}} title is not a valid title", err.message)
end

def test_call_will_return_any_user_errors
stub_partner_req(
'create_app',
Expand Down
1 change: 1 addition & 0 deletions test/shopify-cli/tasks/ensure_env_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ def setup
Project.write(@context, project_type: :fake, organization_id: 42)
FileUtils.cd(@context.root)
ShopifyCli::Tunnel.stubs(:start)
Shopifolk.stubs(:check)
end

def test_create_new_app_if_none_available
Expand Down
Loading

0 comments on commit e559d97

Please sign in to comment.