disco_app 0.8.5 → 0.8.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/disco_app/components/filterable_shop_list.js.jsx +65 -0
  3. data/app/assets/javascripts/disco_app/components/shop_filter_tab.js.jsx +34 -0
  4. data/app/assets/javascripts/disco_app/components/shop_filter_tabs.js.jsx +21 -0
  5. data/app/assets/javascripts/disco_app/components/shop_list.js.jsx +140 -0
  6. data/app/assets/javascripts/disco_app/components/shop_row.js.jsx +27 -0
  7. data/app/controllers/disco_app/admin/app_settings_controller.rb +3 -0
  8. data/app/controllers/disco_app/admin/application_controller.rb +3 -0
  9. data/app/controllers/disco_app/admin/concerns/app_settings_controller.rb +24 -0
  10. data/app/controllers/disco_app/admin/concerns/authenticated_controller.rb +20 -0
  11. data/app/controllers/disco_app/admin/concerns/shops_controller.rb +7 -0
  12. data/app/controllers/disco_app/admin/resources/shops_controller.rb +3 -0
  13. data/app/controllers/disco_app/admin/shops_controller.rb +3 -0
  14. data/app/controllers/disco_app/charges_controller.rb +1 -1
  15. data/app/controllers/disco_app/concerns/app_proxy_controller.rb +40 -0
  16. data/app/controllers/disco_app/concerns/authenticated_controller.rb +42 -0
  17. data/app/controllers/disco_app/concerns/carrier_request_controller.rb +21 -0
  18. data/app/controllers/disco_app/install_controller.rb +1 -1
  19. data/app/models/disco_app/app_settings.rb +3 -0
  20. data/app/models/disco_app/concerns/app_settings.rb +7 -0
  21. data/app/models/disco_app/concerns/shop.rb +5 -0
  22. data/app/models/disco_app/concerns/{synchronises_with_shopify.rb → synchronises.rb} +16 -3
  23. data/app/resources/disco_app/admin/resources/concerns/shop_resource.rb +46 -0
  24. data/app/resources/disco_app/admin/resources/shop_resource.rb +4 -0
  25. data/app/views/disco_app/admin/app_settings/edit.html.erb +5 -0
  26. data/app/views/disco_app/admin/shops/index.html.erb +12 -0
  27. data/app/views/layouts/admin/_navbar.html.erb +24 -0
  28. data/app/views/layouts/admin.html.erb +27 -0
  29. data/config/routes.rb +15 -0
  30. data/db/migrate/20150525000000_create_shops_if_not_existent.rb +1 -1
  31. data/db/migrate/20160112233706_create_disco_app_sessions.rb +2 -2
  32. data/db/migrate/20160223111044_create_disco_app_settings.rb +8 -0
  33. data/lib/disco_app/engine.rb +1 -0
  34. data/lib/disco_app/version.rb +1 -1
  35. data/lib/generators/disco_app/adminify/adminify_generator.rb +35 -0
  36. data/lib/generators/disco_app/disco_app_generator.rb +1 -0
  37. data/lib/generators/disco_app/templates/controllers/home_controller.rb +2 -2
  38. data/test/controllers/disco_app/admin/shops_controller_test.rb +54 -0
  39. data/test/dummy/app/controllers/disco_app/admin/shops_controller.rb +8 -0
  40. data/test/dummy/app/controllers/home_controller.rb +2 -2
  41. data/test/dummy/app/controllers/proxy_controller.rb +1 -1
  42. data/test/dummy/app/jobs/products_create_job.rb +7 -0
  43. data/test/dummy/app/jobs/products_delete_job.rb +7 -0
  44. data/test/dummy/app/jobs/products_update_job.rb +7 -0
  45. data/test/dummy/app/models/product.rb +6 -0
  46. data/test/dummy/config/database.codeship.yml +23 -0
  47. data/test/dummy/config/database.yml +8 -13
  48. data/test/dummy/db/migrate/20160307182229_create_products.rb +11 -0
  49. data/test/dummy/db/schema.rb +27 -10
  50. data/test/fixtures/products.yml +4 -0
  51. data/test/fixtures/webhooks/product_created.json +167 -0
  52. data/test/fixtures/webhooks/product_deleted.json +3 -0
  53. data/test/fixtures/webhooks/product_updated.json +167 -0
  54. data/test/integration/synchronises_test.rb +55 -0
  55. metadata +69 -9
  56. data/app/controllers/disco_app/app_proxy_controller.rb +0 -42
  57. data/app/controllers/disco_app/authenticated_controller.rb +0 -44
  58. data/app/controllers/disco_app/carrier_request_controller.rb +0 -23
  59. data/test/integration/navigation_test.rb +0 -10
@@ -0,0 +1,27 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title><%= yield(:title) %></title>
5
+ <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true %>
6
+ <%= javascript_include_tag 'application', 'data-turbolinks-track' => true %>
7
+
8
+ <%= csrf_meta_tags %>
9
+ </head>
10
+ <body>
11
+
12
+ <%= render 'layouts/admin/navbar' %>
13
+
14
+ <div class="container-fluid">
15
+ <div class="page-header">
16
+ <h1><%= yield(:title) %></h1>
17
+ </div>
18
+
19
+ <% flash.each do |message_type, message| %>
20
+ <div class="alert alert-<%= message_type %>"><%= message %></div>
21
+ <% end %>
22
+
23
+ <%= yield %>
24
+ </div>
25
+
26
+ </body>
27
+ </html>
data/config/routes.rb CHANGED
@@ -16,6 +16,21 @@ DiscoApp::Engine.routes.draw do
16
16
  get 'uninstalling' => :uninstalling, as: :uninstalling
17
17
  end
18
18
 
19
+ controller 'admin' do
20
+ get 'admin', to: redirect('/admin/shops')
21
+ get 'admin/shops' => 'admin/shops#index'
22
+ end
23
+
24
+ namespace :admin do
25
+ resources :shops, path: '/shops', only: [:index, :edit, :update]
26
+ resource :app_settings, only: [:edit, :update]
27
+
28
+ # JSON-API resources for admins."
29
+ namespace :resources do
30
+ jsonapi_resources :shops
31
+ end
32
+ end
33
+
19
34
  # Make the embedded app frame emulator available in development.
20
35
  if Rails.env.development?
21
36
  controller :frame do
@@ -5,7 +5,7 @@ class CreateShopsIfNotExistent < ActiveRecord::Migration
5
5
  create_table :shops do |t|
6
6
  t.string :shopify_domain, null: false
7
7
  t.string :shopify_token, null: false
8
- t.timestamps
8
+ t.timestamps null: false
9
9
  end
10
10
 
11
11
  add_index :shops, :shopify_domain, unique: true
@@ -1,9 +1,9 @@
1
1
  class CreateDiscoAppSessions < ActiveRecord::Migration
2
2
  def change
3
3
  create_table :disco_app_sessions do |t|
4
- t.string :session_id, :null => false
4
+ t.string :session_id, null: false
5
5
  t.text :data
6
- t.timestamps
6
+ t.timestamps null: false
7
7
  end
8
8
 
9
9
  add_index :disco_app_sessions, :session_id, :unique => true
@@ -0,0 +1,8 @@
1
+ class CreateDiscoAppSettings < ActiveRecord::Migration
2
+ def change
3
+ create_table :disco_app_app_settings do |t|
4
+
5
+ t.timestamps null: false
6
+ end
7
+ end
8
+ end
@@ -4,6 +4,7 @@ require 'jquery-rails'
4
4
  require 'turbolinks'
5
5
  require 'activerecord/session_store'
6
6
  require 'disco_app/session'
7
+ require 'jsonapi-resources'
7
8
 
8
9
  module DiscoApp
9
10
  class Engine < ::Rails::Engine
@@ -1,3 +1,3 @@
1
1
  module DiscoApp
2
- VERSION = '0.8.5'
2
+ VERSION = '0.8.6'
3
3
  end
@@ -0,0 +1,35 @@
1
+ module DiscoApp
2
+ module Generators
3
+ class AdminifyGenerator < Rails::Generators::Base
4
+
5
+ source_root File.expand_path('../templates', __FILE__)
6
+
7
+ # Run disco_app:reactfy since the admin section needs react
8
+ def reactify_install
9
+ generate 'disco_app:reactify'
10
+ end
11
+
12
+ def configure_gems
13
+ gem 'active_link_to', '~> 1.0.2'
14
+ end
15
+
16
+ def add_env_variables
17
+ env_variables = <<-CONFIG.strip_heredoc
18
+
19
+ ADMIN_APP_USERNAME=
20
+ ADMIN_APP_PASSWORD=
21
+ CONFIG
22
+ append_to_file '.env', env_variables
23
+ append_to_file '.env.local', env_variables
24
+ end
25
+
26
+ # Run bundle install to add our new gems before running tasks.
27
+ def bundle_install
28
+ Bundler.with_clean_env do
29
+ run 'bundle install'
30
+ end
31
+ end
32
+
33
+ end
34
+ end
35
+ end
@@ -34,6 +34,7 @@ class DiscoAppGenerator < Rails::Generators::Base
34
34
  gem 'activerecord-session_store', '~> 0.1.2'
35
35
  gem 'activeresource', github: 'shopify/activeresource', tag: '4.2-threadsafe'
36
36
  gem 'rails-bigint-pk', '~> 1.2.0'
37
+ gem 'acts_as_singleton', '~> 0.0.8'
37
38
 
38
39
  # Add gems for development and testing only.
39
40
  gem_group :development, :test do
@@ -1,7 +1,7 @@
1
1
  class HomeController < ApplicationController
2
- include DiscoApp::AuthenticatedController
2
+ include DiscoApp::Concerns::AuthenticatedController
3
3
 
4
4
  def index
5
5
  end
6
6
 
7
- end
7
+ end
@@ -0,0 +1,54 @@
1
+ require 'test_helper'
2
+
3
+ class DiscoApp::Admin::ShopsControllerTest < ActionController::TestCase
4
+ include ActiveJob::TestHelper
5
+
6
+ def setup
7
+ ENV['ADMIN_APP_USERNAME'] = 'admin_app_username'
8
+ ENV['ADMIN_APP_PASSWORD'] = 'admin_app_password'
9
+ @routes = DiscoApp::Engine.routes
10
+ end
11
+
12
+ def teardown
13
+ ENV['ADMIN_APP_USERNAME'] = nil
14
+ ENV['ADMIN_APP_PASSWORD'] = nil
15
+ end
16
+
17
+ test 'can not access shops list without authorization' do
18
+ get(:index)
19
+ assert_response :unauthorized
20
+ end
21
+
22
+ test 'can not access shops list with incorrect username/password' do
23
+ authenticate('fakeuser', 'blah blah')
24
+ get(:index)
25
+ assert_response :unauthorized
26
+ end
27
+
28
+ test 'can not access shops list with blank username/password' do
29
+ authenticate('', '')
30
+ get(:index)
31
+ assert_response :unauthorized
32
+ end
33
+
34
+ test 'can not access shops list with blank username/password when env variables are blank' do
35
+ ENV['ADMIN_APP_USERNAME'] = ''
36
+ ENV['ADMIN_APP_PASSWORD'] = ''
37
+ authenticate('', '')
38
+ get(:index)
39
+ assert_response :unauthorized
40
+ end
41
+
42
+ test 'can access shops list with correct username/password' do
43
+ authenticate('admin_app_username', 'admin_app_password')
44
+ get(:index)
45
+ assert_response :ok
46
+ end
47
+
48
+ private
49
+
50
+ def authenticate(username, password)
51
+ request.env['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Basic.encode_credentials(username, password)
52
+ end
53
+
54
+ end
@@ -0,0 +1,8 @@
1
+ class DiscoApp::Admin::ShopsController < DiscoApp::Admin::ApplicationController
2
+ include DiscoApp::Admin::Concerns::ShopsController
3
+
4
+ def index
5
+ render text: 'ok'
6
+ end
7
+
8
+ end
@@ -1,7 +1,7 @@
1
1
  class HomeController < ApplicationController
2
- include DiscoApp::AuthenticatedController
2
+ include DiscoApp::Concerns::AuthenticatedController
3
3
 
4
4
  def index
5
5
  end
6
6
 
7
- end
7
+ end
@@ -1,5 +1,5 @@
1
1
  class ProxyController < ActionController::Base
2
- include DiscoApp::AppProxyController
2
+ include DiscoApp::Concerns::AppProxyController
3
3
 
4
4
  def index
5
5
  render text: 'ok'
@@ -0,0 +1,7 @@
1
+ class ProductsCreateJob < DiscoApp::ShopJob
2
+
3
+ def perform(shopify_domain, product_data)
4
+ Product.synchronise(@shop, product_data)
5
+ end
6
+
7
+ end
@@ -0,0 +1,7 @@
1
+ class ProductsDeleteJob < DiscoApp::ShopJob
2
+
3
+ def perform(shopify_domain, product_data)
4
+ Product.synchronise_deletion(@shop, product_data)
5
+ end
6
+
7
+ end
@@ -0,0 +1,7 @@
1
+ class ProductsUpdateJob < DiscoApp::ShopJob
2
+
3
+ def perform(shopify_domain, product_data)
4
+ Product.synchronise(@shop, product_data)
5
+ end
6
+
7
+ end
@@ -0,0 +1,6 @@
1
+ class Product < ActiveRecord::Base
2
+ include DiscoApp::Concerns::Synchronises
3
+
4
+ belongs_to :shop, class_name: 'DiscoApp::Shop'
5
+
6
+ end
@@ -0,0 +1,23 @@
1
+ development:
2
+ adapter: postgresql
3
+ host: localhost
4
+ encoding: unicode
5
+ pool: 10
6
+ username: <%= ENV['PG_USER'] %>
7
+ template: template1
8
+ password: <%= ENV['PG_PASSWORD'] %>
9
+ database: development<%= ENV['TEST_ENV_NUMBER'] %>
10
+ port: 5432
11
+ sslmode: disable
12
+
13
+ test:
14
+ adapter: postgresql
15
+ host: localhost
16
+ encoding: unicode
17
+ pool: 10
18
+ username: <%= ENV['PG_USER'] %>
19
+ template: template1
20
+ password: <%= ENV['PG_PASSWORD'] %>
21
+ database: test<%= ENV['TEST_ENV_NUMBER'] %>
22
+ port: 5432
23
+ sslmode: disable
@@ -1,25 +1,20 @@
1
- # SQLite version 3.x
2
- # gem install sqlite3
3
- #
4
- # Ensure the SQLite 3 gem is defined in your Gemfile
5
- # gem 'sqlite3'
6
- #
7
1
  default: &default
8
- adapter: sqlite3
2
+ adapter: postgresql
3
+ encoding: unicode
9
4
  pool: 5
10
5
  timeout: 5000
11
6
 
12
7
  development:
13
8
  <<: *default
14
- database: db/development.sqlite3
9
+ host: localhost
10
+ port: 5432
11
+ database: disco_app
15
12
 
16
- # Warning: The database defined as "test" will be erased and
17
- # re-generated from your development database when you run "rake".
18
- # Do not set this db to the same as development or production.
19
13
  test:
20
14
  <<: *default
21
- database: db/test.sqlite3
15
+ host: localhost
16
+ port: 5432
17
+ database: disco_app_test
22
18
 
23
19
  production:
24
20
  <<: *default
25
- database: db/production.sqlite3
@@ -0,0 +1,11 @@
1
+ class CreateProducts < ActiveRecord::Migration
2
+ def change
3
+ create_table :products do |t|
4
+ t.integer :shop_id, limit: 8
5
+ t.jsonb :data
6
+
7
+ t.timestamps null: false
8
+ end
9
+ add_foreign_key :products, :disco_app_shops, column: :shop_id
10
+ end
11
+ end
@@ -11,7 +11,15 @@
11
11
  #
12
12
  # It's strongly recommended that you check this file into your version control system.
13
13
 
14
- ActiveRecord::Schema.define(version: 20160113194418) do
14
+ ActiveRecord::Schema.define(version: 20160307182229) do
15
+
16
+ # These are extensions that must be enabled in order to support this database
17
+ enable_extension "plpgsql"
18
+
19
+ create_table "disco_app_app_settings", force: :cascade do |t|
20
+ t.datetime "created_at", null: false
21
+ t.datetime "updated_at", null: false
22
+ end
15
23
 
16
24
  create_table "disco_app_plans", force: :cascade do |t|
17
25
  t.integer "status"
@@ -26,19 +34,19 @@ ActiveRecord::Schema.define(version: 20160113194418) do
26
34
  create_table "disco_app_sessions", force: :cascade do |t|
27
35
  t.string "session_id", null: false
28
36
  t.text "data"
29
- t.datetime "created_at"
30
- t.datetime "updated_at"
37
+ t.datetime "created_at", null: false
38
+ t.datetime "updated_at", null: false
31
39
  t.integer "shop_id"
32
40
  end
33
41
 
34
- add_index "disco_app_sessions", ["session_id"], name: "index_disco_app_sessions_on_session_id", unique: true
35
- add_index "disco_app_sessions", ["updated_at"], name: "index_disco_app_sessions_on_updated_at"
42
+ add_index "disco_app_sessions", ["session_id"], name: "index_disco_app_sessions_on_session_id", unique: true, using: :btree
43
+ add_index "disco_app_sessions", ["updated_at"], name: "index_disco_app_sessions_on_updated_at", using: :btree
36
44
 
37
45
  create_table "disco_app_shops", force: :cascade do |t|
38
46
  t.string "shopify_domain", null: false
39
47
  t.string "shopify_token", null: false
40
- t.datetime "created_at"
41
- t.datetime "updated_at"
48
+ t.datetime "created_at", null: false
49
+ t.datetime "updated_at", null: false
42
50
  t.integer "status", default: 0
43
51
  t.string "email"
44
52
  t.string "country_name"
@@ -61,7 +69,7 @@ ActiveRecord::Schema.define(version: 20160113194418) do
61
69
  t.boolean "has_storefront"
62
70
  end
63
71
 
64
- add_index "disco_app_shops", ["shopify_domain"], name: "index_disco_app_shops_on_shopify_domain", unique: true
72
+ add_index "disco_app_shops", ["shopify_domain"], name: "index_disco_app_shops_on_shopify_domain", unique: true, using: :btree
65
73
 
66
74
  create_table "disco_app_subscriptions", force: :cascade do |t|
67
75
  t.integer "shop_id"
@@ -75,7 +83,16 @@ ActiveRecord::Schema.define(version: 20160113194418) do
75
83
  t.datetime "updated_at", null: false
76
84
  end
77
85
 
78
- add_index "disco_app_subscriptions", ["plan_id"], name: "index_disco_app_subscriptions_on_plan_id"
79
- add_index "disco_app_subscriptions", ["shop_id"], name: "index_disco_app_subscriptions_on_shop_id"
86
+ add_index "disco_app_subscriptions", ["plan_id"], name: "index_disco_app_subscriptions_on_plan_id", using: :btree
87
+ add_index "disco_app_subscriptions", ["shop_id"], name: "index_disco_app_subscriptions_on_shop_id", using: :btree
88
+
89
+ create_table "products", force: :cascade do |t|
90
+ t.integer "shop_id", limit: 8
91
+ t.jsonb "data"
92
+ t.datetime "created_at", null: false
93
+ t.datetime "updated_at", null: false
94
+ end
80
95
 
96
+ add_foreign_key "disco_app_sessions", "disco_app_shops", column: "shop_id", on_delete: :cascade
97
+ add_foreign_key "products", "disco_app_shops", column: "shop_id"
81
98
  end
@@ -0,0 +1,4 @@
1
+ ipod:
2
+ id: 632910393
3
+ shop: widget_store
4
+ data: {}
@@ -0,0 +1,167 @@
1
+ {
2
+ "id": 632910392,
3
+ "title": "IPod Nano - 8GB",
4
+ "body_html": "<p>It's the small iPod with one very big idea: Video. Now the world's most popular music player, available in 4GB and 8GB models, lets you enjoy TV shows, movies, video podcasts, and more. The larger, brighter display means amazing picture quality. In six eye-catching colors, iPod nano is stunning all around. And with models starting at just $149, little speaks volumes.<\/p>",
5
+ "vendor": "Apple",
6
+ "product_type": "Cult Products",
7
+ "created_at": "2015-09-02T14:50:32-04:00",
8
+ "handle": "ipod-nano",
9
+ "updated_at": "2015-09-02T14:50:32-04:00",
10
+ "published_at": "2007-12-31T19:00:00-05:00",
11
+ "template_suffix": null,
12
+ "published_scope": "web",
13
+ "tags": "Emotive, Flash Memory, MP3, Music",
14
+ "variants": [
15
+ {
16
+ "id": 808950810,
17
+ "product_id": 632910392,
18
+ "title": "Pink",
19
+ "sku": "IPOD2008PINK",
20
+ "position": 1,
21
+ "grams": 200,
22
+ "inventory_policy": "continue",
23
+ "fulfillment_service": "manual",
24
+ "inventory_management": "shopify",
25
+ "price": "199.00",
26
+ "compare_at_price": null,
27
+ "option1": "Pink",
28
+ "option2": null,
29
+ "option3": null,
30
+ "created_at": "2015-09-02T14:50:32-04:00",
31
+ "updated_at": "2015-09-02T14:50:32-04:00",
32
+ "taxable": true,
33
+ "requires_shipping": true,
34
+ "barcode": "1234_pink",
35
+ "inventory_quantity": 10,
36
+ "old_inventory_quantity": 10,
37
+ "image_id": 562641783,
38
+ "weight": 0.2,
39
+ "weight_unit": "kg"
40
+ },
41
+ {
42
+ "id": 49148385,
43
+ "product_id": 632910392,
44
+ "title": "Red",
45
+ "sku": "IPOD2008RED",
46
+ "position": 2,
47
+ "grams": 200,
48
+ "inventory_policy": "continue",
49
+ "fulfillment_service": "manual",
50
+ "inventory_management": "shopify",
51
+ "price": "199.00",
52
+ "compare_at_price": null,
53
+ "option1": "Red",
54
+ "option2": null,
55
+ "option3": null,
56
+ "created_at": "2015-09-02T14:50:32-04:00",
57
+ "updated_at": "2015-09-02T14:50:32-04:00",
58
+ "taxable": true,
59
+ "requires_shipping": true,
60
+ "barcode": "1234_red",
61
+ "inventory_quantity": 20,
62
+ "old_inventory_quantity": 20,
63
+ "image_id": null,
64
+ "weight": 0.2,
65
+ "weight_unit": "kg"
66
+ },
67
+ {
68
+ "id": 39072856,
69
+ "product_id": 632910392,
70
+ "title": "Green",
71
+ "sku": "IPOD2008GREEN",
72
+ "position": 3,
73
+ "grams": 200,
74
+ "inventory_policy": "continue",
75
+ "fulfillment_service": "manual",
76
+ "inventory_management": "shopify",
77
+ "price": "199.00",
78
+ "compare_at_price": null,
79
+ "option1": "Green",
80
+ "option2": null,
81
+ "option3": null,
82
+ "created_at": "2015-09-02T14:50:32-04:00",
83
+ "updated_at": "2015-09-02T14:50:32-04:00",
84
+ "taxable": true,
85
+ "requires_shipping": true,
86
+ "barcode": "1234_green",
87
+ "inventory_quantity": 30,
88
+ "old_inventory_quantity": 30,
89
+ "image_id": null,
90
+ "weight": 0.2,
91
+ "weight_unit": "kg"
92
+ },
93
+ {
94
+ "id": 457924702,
95
+ "product_id": 632910392,
96
+ "title": "Black",
97
+ "sku": "IPOD2008BLACK",
98
+ "position": 4,
99
+ "grams": 200,
100
+ "inventory_policy": "continue",
101
+ "fulfillment_service": "manual",
102
+ "inventory_management": "shopify",
103
+ "price": "199.00",
104
+ "compare_at_price": null,
105
+ "option1": "Black",
106
+ "option2": null,
107
+ "option3": null,
108
+ "created_at": "2015-09-02T14:50:32-04:00",
109
+ "updated_at": "2015-09-02T14:50:32-04:00",
110
+ "taxable": true,
111
+ "requires_shipping": true,
112
+ "barcode": "1234_black",
113
+ "inventory_quantity": 40,
114
+ "old_inventory_quantity": 40,
115
+ "image_id": null,
116
+ "weight": 0.2,
117
+ "weight_unit": "kg"
118
+ }
119
+ ],
120
+ "options": [
121
+ {
122
+ "id": 594680422,
123
+ "product_id": 632910392,
124
+ "name": "Color",
125
+ "position": 1,
126
+ "values": [
127
+ "Pink",
128
+ "Red",
129
+ "Green",
130
+ "Black"
131
+ ]
132
+ }
133
+ ],
134
+ "images": [
135
+ {
136
+ "id": 850703190,
137
+ "product_id": 632910392,
138
+ "position": 1,
139
+ "created_at": "2015-09-02T14:50:32-04:00",
140
+ "updated_at": "2015-09-02T14:50:32-04:00",
141
+ "src": "https:\/\/cdn.shopify.com\/s\/files\/1\/0006\/9093\/3842\/products\/ipod-nano.png?v=1441219832",
142
+ "variant_ids": [
143
+ ]
144
+ },
145
+ {
146
+ "id": 562641783,
147
+ "product_id": 632910392,
148
+ "position": 2,
149
+ "created_at": "2015-09-02T14:50:32-04:00",
150
+ "updated_at": "2015-09-02T14:50:32-04:00",
151
+ "src": "https:\/\/cdn.shopify.com\/s\/files\/1\/0006\/9093\/3842\/products\/ipod-nano-2.png?v=1441219832",
152
+ "variant_ids": [
153
+ 808950810
154
+ ]
155
+ }
156
+ ],
157
+ "image": {
158
+ "id": 850703190,
159
+ "product_id": 632910392,
160
+ "position": 1,
161
+ "created_at": "2015-09-02T14:50:32-04:00",
162
+ "updated_at": "2015-09-02T14:50:32-04:00",
163
+ "src": "https:\/\/cdn.shopify.com\/s\/files\/1\/0006\/9093\/3842\/products\/ipod-nano.png?v=1441219832",
164
+ "variant_ids": [
165
+ ]
166
+ }
167
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "id": 632910393
3
+ }