diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 7d20bc8a..00000000 --- a/.travis.yml +++ /dev/null @@ -1,15 +0,0 @@ -language: python - -python: - - "2.7" - -# command to install dependencies -install: "python setup.py install" - -# command to run tests -script: 'python setup.py test' - -notifications: - flowdock: - secure: "YD74L+41DEjv2Bq5fG8vvIsuMG5iu8CHctes3CSfQw0j5SaPcASsKe6twXBCLsGB5IwzgYk7RUEnOsqA6kLqfWChJ3kO4lBCYBKSaGSG84VjwUlMOVG4n0jt7SLiHRjQ6sBhwFhWE64yAZPbdDuYtrUtVbb9Ui+dLUyBab1o7Cw=" - diff --git a/CHANGELOG b/CHANGELOG deleted file mode 100644 index b62ef1ef..00000000 --- a/CHANGELOG +++ /dev/null @@ -1,148 +0,0 @@ -== Version 2.0.5 unreleased - -* Add a specific exception for signature validation failures - -== Version 2.0.4 - -* Bug fixes -* Added CarrierService resource -* Added Property resource to LineItem - -== Version 2.0.3 - -* Add Order Risk resource - -== Version 2.0.2 - -* Add access to FulfillmentService endpoint -* Fix some import bugs - -== Version 2.0.1 - -* Package bug fix - -== Version 2.0.0 - -* Removed support for legacy auth -* Updated to pyactiveresource v2.0.0 which changes the default form to JSON -* in Session::request_token params is no longer optional, you must pass all the params - and the method will now extract the code -* made create_permission_url an instance method, you'll need an instance - of session to call this method from now on -* Updated session.request_token -* Updated Session to better match the ShopifyAPI Ruby gem -* Updated the readme to better describe how to use the library -* Added support for CustomerSavedSearch (CustomerGroup is deprecated) - -== Version 1.0.7 - -* Fix thread local headers to store a copy of the default hash which -prevents activate_session in one thread from affecting other threads. - -== Version 1.0.6 - -* Fix deserializing and serializing fulfillments which can now contain -arrays of strings in the tracking_urls attribute. - -== Version 1.0.5 - -* Fix parameter passing for order cancellation. -* Fix Product.price_range method for variants with different prices. - -== Version 1.0.4 - -* Fixed another bug in Image size methods regex. - -== Version 1.0.3 - -* Fix bug in setting format attribute on Webhook instances. -* Fixed missing slash in return value of Image size methods -* Upgrade pyactiveresource to fix unicode encoding issues - -== Version 1.0.2 - -* Made ShopifyResource.clear_session idempotent. - -== Version 1.0.1 - -* Use the correct redirect parameter in Session.create_permission_url. -Was redirect_url but corrected to redirect_uri. - -== Version 1.0.0 - -* Added support for OAuth2. -* ShopifyResource.activate_session must now be used with OAuth2 instead -of setting ShopifyResource.site directly. -* Session.__init__ no longer allows params to be passed in as **params -* Session.__init__ now makes an HTTP request when using OAuth2 if -params are specified -* Session now exposes the access token through the token instance -variable to simplify session saving and resuming - -== Version 0.4.0 - -* Using setup.py no longer requires all dependancies -* More compatiblity fixes for using the latest pyactiveresource -* ShopifyResource.activate_session is not recommended over setting site -directly for forward compatibility with coming OAuth2 changes. - -== Version 0.3.1 - -* Compatiblity fixes for using latest (unreleased) pyactiveresource - -== Version 0.3.0 - -* Added support for customer search and customer group search. -* Resource erros are cleared on save from previous save attempt. -* Made the library thread-safe using thread-local connections. - -== Version 0.2.1 - -* Fixed a regression that caused a different connection -object to be created on each resource. - -== Version 0.2.0 - -* Made responses available through the connection object. - -== Version 0.1.8 - -* Added ability to add metafields on customers. - -== Version 0.1.7 - -* Fixed missing theme_id in return value of Asset.find. - -== Version 0.1.6 - -* Fixed attribute setting on Asset objects -* Strip path from shop_url to get just the shop's domain. - -== Version 0.1.5 - -* Fixed Asset.find() -* Fixed Variant.find(id) -* Allow running from source directory with PYTHONPATH=./lib - -== Version 0.1.4 - -* Fixed a bug in metafields method caused by missing import. -* Prefix options can be specified in the attributes dict on creation -* Allow count method to be used the same way as find - -== Version 0.1.3 - -* Fixed the automatic download of dependancies. -* Updated the README instructions. - -== Version 0.1.2 - -* Add python 2.5 compatibility - -== Version 0.1.1 - -* Make creating a session simpler with django - -== Version 0.1.0 - -* ported ShopifyAPI from ruby to python diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 8473a94a..00000000 --- a/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -Copyright (c) 2011 "JadedPixel inc." - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index 64affe7a..00000000 --- a/MANIFEST.in +++ /dev/null @@ -1,4 +0,0 @@ -include LICENSE -include CHANGELOG -include README.md -include bin/shopify_api.py diff --git a/README.md b/README.md deleted file mode 100644 index 1eac798b..00000000 --- a/README.md +++ /dev/null @@ -1,252 +0,0 @@ -# Shopify API - -The ShopifyAPI library allows Python developers to programmatically -access the admin section of stores. - -The API is accessed using pyactiveresource in order to provide an -interface similar to the -[ruby Shopify API](https://github.com/shopify/shopify_api) gem. -The data itself is sent as XML over HTTP to communicate with Shopify, -which provides a web service that follows the REST principles as -much as possible. - -## Usage - -### Requirements - -All API usage happens through Shopify applications, created by -either shop owners for their own shops, or by Shopify Partners for -use by other shop owners: - -* Shop owners can create applications for themselves through their -own admin: -* Shopify Partners create applications through their admin: - - -For more information and detailed documentation about the API visit - - -### Installation - -To easily install or upgrade to the latest release, use -[pip](http://www.pip-installer.org/) - -```shell -pip install --upgrade ShopifyAPI -``` - -or [easy_install](http://packages.python.org/distribute/easy_install.html) - -```shell -easy_install -U ShopifyAPI -``` - -### Getting Started - -ShopifyAPI uses pyactiveresource to communicate with the REST web -service. pyactiveresource has to be configured with a fully authorized -URL of a particular store first. To obtain that URL you can follow -these steps: - -1. First create a new application in either the partners admin or - your store admin. For a private App you'll need the API_KEY and - the PASSWORD otherwise you'll need the API_KEY and SHARED_SECRET. - -2. For a private App you just need to set the base site url as - follows: - - ```python - shop_url = "https://%s:%s@SHOP_NAME.myshopify.com/admin" % (API_KEY, PASSWORD) - shopify.ShopifyResource.set_site(shop_url) - ``` - - That's it you're done, skip to step 6 and start using the API! - For a partner App you will need to supply two parameters to the - Session class before you instantiate it: - - ```python - shopify.Session.setup(api_key=API_KEY, secret=SHARED_SECRET) - ``` - -3. In order to access a shop's data, apps need an access token from that - specific shop. This is a two-stage process. Before interacting with - a shop for the first time an app should redirect the user to the - following URL: - - `GET https://SHOP_NAME.myshopify.com/admin/oauth/authorize` - - with the following parameters: - - ``` - * client_id – Required – The API key for your app - * scope – Required – The list of required scopes (explained here: http://docs.shopify.com/api/tutorials/oauth) - * redirect_uri – Optional – The URL that the merchant will be sent to once authentication is complete. Defaults to the URL specified in the application settings and must be the same host as that URL. - ``` - - We've added the create_permision_url method to make this easier, first - instantiate your session object: - - ```python - session = shopify.Session("SHOP_NAME.myshopify.com") - ``` - - Then call: - - ```python - scope=["write_products"] - permission_url = session.create_permission_url(scope) - ``` - - or if you want a custom redirect_uri: - - ```python - permission_url = session.create_permission_url(scope, "https://my_redirect_uri.com") - ``` - -4. Once authorized, the shop redirects the owner to the return URL of your - application with a parameter named 'code'. This is a temporary token - that the app can exchange for a permanent access token. Make the following call: - - `POST https://SHOP_NAME.myshopify.com/admin/oauth/access_token` - - with the following parameters: - - ``` - * client_id – Required – The API key for your app - * client_secret – Required – The shared secret for your app - * code – Required – The code you received in step 3 - ``` - - and you'll get your permanent access token back in the response. - - There is a method to make the request and get the token for you. Pass - all the params received from the previous call (shop, code, timestamp, - signature) as a dictionary and the method will verify - the params, extract the temp code and then request your token: - - ```python - token = session.request_token(params) - ``` - - This method will save the token to the session object - and return it. For future sessions simply pass the token when - creating the session object. - - ```python - session = shopify.Session("SHOP_NAME.myshopify.com", token) - ``` - -5. The session must be activated before use: - - ```python - shopify.ShopifyResource.activate_session(session) - ``` - -6. Now you're ready to make authorized API requests to your shop! - Data is returned as ActiveResource instances: - - ```python - shop = shopify.Shop.current - - # Get a specific product - product = shopify.Product.find(179761209) - - # Create a new product - new_product = shopify.Product() - new_product.title = "Burton Custom Freestyle 151" - new_product.product_type = "Snowboard" - new_product.vendor = "Burton" - success = new_product.save() #returns false if the record is invalid - # or - if new_product.errors: - #something went wrong, see new_product.errors.full_messages() for example - - # Update a product - product.handle = "burton-snowboard" - product.save() - ``` - - Alternatively, you can use temp to initialize a Session and execute a command which also handles temporarily setting ActiveResource::Base.site: - - ```python - with shopify.Session.temp("SHOP_NAME.myshopify.com", token): - product = shopify.Product.find() - ``` - -7. If you want to work with another shop, you'll first need to clear the session:: - - ```python - shopify.ShopifyResource.clear_session - ``` - -### Console - -This package also includes the `shopify_api.py` script to make it easy to -open up an interactive console to use the API with a shop. - -1. Obtain a private API key and password to use with your shop - (step 2 in "Getting Started") - -2. Use the `shopify_api.py` script to save the credentials for the - shop to quickly log in. The script uses [PyYAML](http://pyyaml.org/) to save - and load connection configurations in the same format as the ruby - shopify\_api. - - ```shell - shopify_api.py add yourshopname - ``` - - Follow the prompts for the shop domain, API key and password. - -3. Start the console for the connection. - - ```shell - shopify_api.py console - ``` - -4. To see the full list of commands, type: - - ```shell - shopify_api.py help - ``` - -## Using Development Version - -The development version can be built using - -```shell -python setup.py sdist -``` - -then the package can be installed using pip - -```shell -pip install --upgrade dist/ShopifyAPI-*.tar.gz -``` - -or easy_install - -```shell -easy_install -U dist/ShopifyAPI-*.tar.gz -``` - -Note Use the `bin/shopify_api.py` script when running from the source tree. -It will add the lib directory to start of sys.path, so the installed -version won't be used. - -## Limitations - -Currently there is no support for: - -* python 3 -* asynchronous requests -* persistent connections - -## Additional Resources - -* [Shopify API](http://api.shopify.com) <= Read the tech docs! -* [Ask questions on the Shopify forums](http://ecommerce.shopify.com/c/shopify-apis-and-technology) <= Ask questions on the forums! - -## Copyright - -Copyright (c) 2012 "Shopify inc.". See LICENSE for details. diff --git a/RELEASING b/RELEASING deleted file mode 100644 index 7be62329..00000000 --- a/RELEASING +++ /dev/null @@ -1,13 +0,0 @@ -Releasing shopify_python_api - -1. Check the Semantic Versioning page for info on how to version the new release: http://semver.org -2. Update version in shopify/version.py -3. Update CHANGELOG entry for the release. -4. Commit the changes - git commit -m "Release vX.Y.Z" -5. Tag the release with the version - git tag -m "Release X.Y.Z" vX.Y.Z -6. Push the changes to github - git push --tags origin master -7. Upload the source package to pypi - python setup.py sdist upload diff --git a/_config.yml b/_config.yml new file mode 100644 index 00000000..911bafd8 --- /dev/null +++ b/_config.yml @@ -0,0 +1 @@ +markdown: kramdown \ No newline at end of file diff --git a/_includes/.DS_Store b/_includes/.DS_Store new file mode 100644 index 00000000..5008ddfc Binary files /dev/null and b/_includes/.DS_Store differ diff --git a/_includes/footer.html b/_includes/footer.html new file mode 100644 index 00000000..eb2c2acb --- /dev/null +++ b/_includes/footer.html @@ -0,0 +1,15 @@ + \ No newline at end of file diff --git a/_includes/head.html b/_includes/head.html new file mode 100644 index 00000000..6e4c3274 --- /dev/null +++ b/_includes/head.html @@ -0,0 +1,28 @@ + + + +Shopify Open Source > {{ site.github.project_title }} + +{% if page.description %} + +{% endif %} + + + + + + + + + + + + + + + + diff --git a/_layouts/.DS_Store b/_layouts/.DS_Store new file mode 100644 index 00000000..5008ddfc Binary files /dev/null and b/_layouts/.DS_Store differ diff --git a/_layouts/index.html b/_layouts/index.html new file mode 100644 index 00000000..874cad2a --- /dev/null +++ b/_layouts/index.html @@ -0,0 +1,58 @@ + + + + + + {% include head.html %} + + +
+
+
+
+
+ + Open Source > {{ site.github.project_title }} +
+
+ {{ site.github.language }} +
+
+
+
+
+

{{ site.github.project_title }}

+

{{ site.github.project_tagline }}

+ +
+
+
+ +
+
+ + {{ content }} + +
+
+ {% include footer.html %} + + + + + + diff --git a/bin/shopify_api.py b/bin/shopify_api.py deleted file mode 100755 index 1451af1b..00000000 --- a/bin/shopify_api.py +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env python -"""shopify_api.py wrapper script for running it the source directory""" - -import sys -import os.path - -# Use the development rather than installed version -project_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) -sys.path.insert(0, project_root) - -execfile(os.path.join(project_root, 'scripts', 'shopify_api.py')) diff --git a/index.md b/index.md new file mode 100644 index 00000000..efc26cff --- /dev/null +++ b/index.md @@ -0,0 +1,193 @@ +--- +layout: index +--- + +The ShopifyAPI library allows Python developers to programmatically access the admin section of stores. + +The API is accessed using pyactiveresource in order to provide an interface similar to the [ruby Shopify API](https://github.com/shopify/shopify_api) gem. The data itself is sent as XML over HTTP to communicate with Shopify, which provides a web service that follows the REST principles as much as possible. + +## Installation + +To easily install or upgrade to the latest release, use +[pip](http://www.pip-installer.org/) + + shell + pip install --upgrade ShopifyAPI + +or [easy_install](http://packages.python.org/distribute/easy_install.html) + + shell + easy_install -U ShopifyAPI + +## Usage + +### Requirements + +All API usage happens through Shopify applications, created by either shop owners for their own shops, or by Shopify Partners for use by other shop owners: + +* Shop owners can create applications for themselves through their own admin: +* Shopify Partners create applications through their admin: + +### Getting Started + +ShopifyAPI uses pyactiveresource to communicate with the REST web service. pyactiveresource has to be configured with a fully authorized URL of a particular store first. To obtain that URL you can follow these steps: + +1. First create a new application in either the partners admin or your store admin. For a private App you'll need the API_KEY and the PASSWORD otherwise you'll need the API_KEY and SHARED_SECRET. + +2. For a private App you just need to set the base site url as + follows: + + python + shop_url = "https://%s:%s@SHOP_NAME.myshopify.com/admin" % (API_KEY, PASSWORD) + shopify.ShopifyResource.set_site(shop_url) + + That's it you're done, skip to step 6 and start using the API! For a partner App you will need to supply two parameters to the Session class before you instantiate it: + + python + shopify.Session.setup(api_key=API_KEY, secret=SHARED_SECRET) + +3. In order to access a shop's data, apps need an access token from that specific shop. This is a two-stage process. Before interacting with a shop for the first time an app should redirect the user to the following URL: + + GET https://SHOP_NAME.myshopify.com/admin/oauth/authorize + + with the following parameters: + + * `client_id` – Required – The API key for your app + * `scope` – Required – The list of required scopes (explained here: http://docs.shopify.com/api/tutorials/oauth) + * `redirect_uri` – Optional – The URL that the merchant will be sent to once authentication is complete. Defaults to the URL specified in the application settings and must be the same host as that URL. + + We've added the create_permision_url method to make this easier, first instantiate your session object: + + python + session = shopify.Session("SHOP_NAME.myshopify.com") + + Then call: + + python + scope=["write_products"] + permission_url = session.create_permission_url(scope) + + or if you want a custom redirect_uri: + + python + permission_url = session.create_permission_url(scope, "https://my_redirect_uri.com") + +4. Once authorized, the shop redirects the owner to the return URL of your application with a parameter named 'code'. This is a temporary token that the app can exchange for a permanent access token. Make the following call: + + POST https://SHOP_NAME.myshopify.com/admin/oauth/access_token + + with the following parameters: + + * `client_id` – Required – The API key for your app + * `client_secret` – Required – The shared secret for your app + * `code` – Required – The code you received in step 3 + + and you'll get your permanent access token back in the response. + + There is a method to make the request and get the token for you. Pass all the params received from the previous call (shop, code, timestamp, signature) as a dictionary and the method will verify the params, extract the temp code and then request your token: + + python + token = session.request_token(params) + + This method will save the token to the session object and return it. For future sessions simply pass the token when creating the session object. + + python + session = shopify.Session("SHOP_NAME.myshopify.com", token) + +5. The session must be activated before use: + + python + shopify.ShopifyResource.activate_session(session) + +6. Now you're ready to make authorized API requests to your shop! + Data is returned as ActiveResource instances: + + python + shop = shopify.Shop.current + + # Get a specific product + product = shopify.Product.find(179761209) + + # Create a new product + new_product = shopify.Product() + new_product.title = "Burton Custom Freestyle 151" + new_product.product_type = "Snowboard" + new_product.vendor = "Burton" + success = new_product.save() #returns false if the record is invalid + # or + if new_product.errors: + #something went wrong, see new_product.errors.full_messages() for example + + # Update a product + product.handle = "burton-snowboard" + product.save() + + Alternatively, you can use temp to initialize a Session and execute a command which also handles temporarily setting ActiveResource::Base.site: + + python + with shopify.Session.temp("SHOP_NAME.myshopify.com", token): + product = shopify.Product.find() + +7. If you want to work with another shop, you'll first need to clear the session:: + + python + shopify.ShopifyResource.clear_session + +### Console + +This package also includes the `shopify_api.py` script to make it easy to open up an interactive console to use the API with a shop. + +1. Obtain a private API key and password to use with your shop (step 2 in "Getting Started") + +2. Use the `shopify_api.py` script to save the credentials for the shop to quickly log in. The script uses [PyYAML](http://pyyaml.org/) to save and load connection configurations in the same format as the ruby shopify\_api. + + shell + shopify_api.py add yourshopname + + Follow the prompts for the shop domain, API key and password. + +3. Start the console for the connection. + + shell + shopify_api.py console + +4. To see the full list of commands, type: + + shell + shopify_api.py help + +## Using Development Version + +The development version can be built using + + shell + python setup.py sdist + +then the package can be installed using pip + + shell + pip install --upgrade dist/ShopifyAPI-*.tar.gz + +or easy_install + + shell + easy_install -U dist/ShopifyAPI-*.tar.gz + +#### Note +Use the `bin/shopify_api.py` script when running from the source tree. It will add the lib directory to start of sys.path, so the installed version won't be used. + +## Limitations + +Currently there is no support for: + +* asynchronous requests +* persistent connections + +## Additional Resources + +* [Shopify API](http://api.shopify.com) <= Read the tech docs! +* [Ask questions on the Shopify forums](http://ecommerce.shopify.com/c/shopify-apis-and-technology) <= Ask questions on the forums! + +## Copyright + +Copyright (c) 2012 "Shopify inc.". See LICENSE for details. diff --git a/scripts/shopify_api.py b/scripts/shopify_api.py deleted file mode 100755 index bfabe29d..00000000 --- a/scripts/shopify_api.py +++ /dev/null @@ -1,246 +0,0 @@ -#!/usr/bin/env python - -import shopify -import code -import sys -import os -import os.path -import glob -import subprocess -import yaml - -def start_interpreter(**variables): - console = type('shopify ' + shopify.version.VERSION, (code.InteractiveConsole, object), {}) - import readline - console(variables).interact() - -class ConfigFileError(StandardError): - pass - -def usage(usage_string): - """Decorator to add a usage string to a function""" - def decorate(func): - func.usage = usage_string - return func - return decorate - -class TasksMeta(type): - _prog = os.path.basename(sys.argv[0]) - - def __new__(mcs, name, bases, new_attrs): - cls = type.__new__(mcs, name, bases, new_attrs) - - tasks = new_attrs.keys() - tasks.append("help") - def filter_func(item): - return not item.startswith("_") and hasattr(getattr(cls, item), "__call__") - tasks = filter(filter_func, tasks) - cls._tasks = sorted(tasks) - - return cls - - def run_task(cls, task=None, *args): - if task in [None, '-h', '--help']: - cls.help() - return - - # Allow unambigious abbreviations of tasks - if task not in cls._tasks: - matches = filter(lambda item: item.startswith(task), cls._tasks) - if len(matches) == 1: - task = matches[0] - else: - print >>sys.stderr, 'Could not find task "%s".' % (task) - - task_func = getattr(cls, task) - task_func(*args) - - @usage("help [TASK]") - def help(cls, task=None): - """Describe available tasks or one specific task""" - if task is None: - usage_list = [] - for task in iter(cls._tasks): - task_func = getattr(cls, task) - usage_string = " %s %s" % (cls._prog, task_func.usage) - desc = task_func.__doc__.splitlines()[0] - usage_list.append((usage_string, desc)) - max_len = reduce(lambda m, item: max(m, len(item[0])), usage_list, 0) - print("Tasks:") - cols = int(os.environ.get("COLUMNS", 80)) - for line, desc in usage_list: - task_func = getattr(cls, task) - if desc: - line = "%s%s # %s" % (line, " " * (max_len - len(line)), desc) - if len(line) > cols: - line = line[:cols - 3] + "..." - print(line) - else: - task_func = getattr(cls, task) - print("Usage:") - print(" %s %s" % (cls._prog, task_func.usage)) - print("") - print(task_func.__doc__) - - -class Tasks(object): - __metaclass__ = TasksMeta - - _shop_config_dir = os.path.join(os.environ["HOME"], ".shopify", "shops") - _default_symlink = os.path.join(_shop_config_dir, "default") - - @classmethod - @usage("list") - def list(cls): - """list available connections""" - for c in cls._available_connections(): - prefix = " * " if cls._is_default(c) else " " - print(prefix + c) - - @classmethod - @usage("add CONNECTION") - def add(cls, connection): - """create a config file for a connection named CONNECTION""" - filename = cls._get_config_filename(connection) - if os.path.exists(filename): - raise ConfigFileError("There is already a config file at " + filename) - else: - config = dict(protocol='https') - domain = raw_input("Domain? (leave blank for %s.myshopify.com) " % (connection)) - if not domain.strip(): - domain = "%s.myshopify.com" % (connection) - config['domain'] = domain - print("") - print("open https://%s/admin/api in your browser to get API credentials" % (domain)) - config['api_key'] = raw_input("API key? ") - config['password'] = raw_input("Password? ") - if not os.path.isdir(cls._shop_config_dir): - os.makedirs(cls._shop_config_dir) - file(filename, 'w').write(yaml.dump(config, default_flow_style=False, explicit_start="---")) - if len(cls._available_connections()) == 1: - cls.default(connection) - - @classmethod - @usage("remove CONNECTION") - def remove(cls, connection): - """remove the config file for CONNECTION""" - filename = cls._get_config_filename(connection) - if os.path.exists(filename): - if cls._is_default(connection): - os.remove(cls._default_symlink) - os.remove(filename) - else: - cls._no_config_file_error(filename) - - @classmethod - @usage("edit [CONNECTION]") - def edit(cls, connection=None): - """open the config file for CONNECTION with you default editor""" - filename = cls._get_config_filename(connection) - if os.path.exists(filename): - editor = os.environ.get("EDITOR") - if editor: - subprocess.call([editor, filename]) - else: - print("Please set an editor in the EDITOR environment variable") - else: - cls._no_config_file_error(filename) - - @classmethod - @usage("show [CONNECTION]") - def show(cls, connection=None): - """output the location and contents of the CONNECTION's config file""" - if connection is None: - connection = cls._default_connection() - filename = cls._get_config_filename(connection) - if os.path.exists(filename): - print(filename) - print(file(filename).read()) - else: - cls._no_config_file_error(filename) - - @classmethod - @usage("default [CONNECTION]") - def default(cls, connection=None): - """show the default connection, or make CONNECTION the default""" - if connection is not None: - target = cls._get_config_filename(connection) - if os.path.exists(target): - if os.path.exists(cls._default_symlink): - os.remove(cls._default_symlink) - os.symlink(target, cls._default_symlink) - else: - cls._no_config_file_error(target) - if os.path.exists(cls._default_symlink): - print("Default connection is " + cls._default_connection()) - else: - print("There is no default connection set") - - @classmethod - @usage("console [CONNECTION]") - def console(cls, connection=None): - """start an API console for CONNECTION""" - filename = cls._get_config_filename(connection) - if not os.path.exists(filename): - cls._no_config_file_error(filename) - - config = yaml.safe_load(file(filename).read()) - print("using %s" % (config["domain"])) - session = cls._session_from_config(config) - shopify.ShopifyResource.activate_session(session) - - start_interpreter(shopify=shopify) - - @classmethod - @usage("version") - def version(cls, connection=None): - """output the shopify library version""" - print(shopify.version.VERSION) - - @classmethod - def _available_connections(cls): - return map(lambda item: os.path.splitext(os.path.basename(item))[0], - glob.glob(os.path.join(cls._shop_config_dir, "*.yml"))) - - @classmethod - def _default_connection_target(cls): - if not os.path.exists(cls._default_symlink): - return None - target = os.readlink(cls._default_symlink) - return os.path.join(cls._shop_config_dir, target) - - - @classmethod - def _default_connection(cls): - target = cls._default_connection_target() - if not target: - return None - return os.path.splitext(os.path.basename(target))[0] - - @classmethod - def _get_config_filename(cls, connection): - if connection is None: - return cls._default_symlink - else: - return os.path.join(cls._shop_config_dir, connection + ".yml") - - @classmethod - def _session_from_config(cls, config): - session = shopify.Session(config.get("domain")) - session.protocol = config.get("protocol", "https") - session.api_key = config.get("api_key") - session.token = config.get("password") - return session - - @classmethod - def _is_default(cls, connection): - return connection == cls._default_connection() - - @classmethod - def _no_config_file_error(cls, filename): - raise ConfigFileError("There is no config file at " + filename) - -try: - Tasks.run_task(*sys.argv[1:]) -except ConfigFileError, e: - print(e) diff --git a/setup.py b/setup.py deleted file mode 100644 index c24d949b..00000000 --- a/setup.py +++ /dev/null @@ -1,40 +0,0 @@ -from setuptools import setup - -NAME='ShopifyAPI' -execfile('shopify/version.py') -DESCRIPTION='Shopify API for Python' -LONG_DESCRIPTION="""\ -The ShopifyAPI library allows python developers to programmatically -access the admin section of stores using an ActiveResource like -interface similar the ruby Shopify API gem. The library makes HTTP -requests to Shopify in order to list, create, update, or delete -resources (e.g. Order, Product, Collection).""" - -setup(name=NAME, - version=VERSION, - description=DESCRIPTION, - long_description=LONG_DESCRIPTION, - author='Shopify', - author_email='developers@shopify.com', - url='https://github.com/Shopify/shopify_python_api', - packages=['shopify', 'shopify/resources'], - scripts=['scripts/shopify_api.py'], - license='MIT License', - install_requires=[ - 'pyactiveresource>=2.0.0', - 'PyYAML', - ], - test_suite='test', - tests_require=[ - 'mock>=1.0.1', - ], - platforms='Any', - classifiers=['Development Status :: 5 - Production/Stable', - 'Intended Audience :: Developers', - 'License :: OSI Approved :: MIT License', - 'Operating System :: OS Independent', - 'Programming Language :: Python', - 'Topic :: Software Development', - 'Topic :: Software Development :: Libraries', - 'Topic :: Software Development :: Libraries :: Python Modules'] - ) diff --git a/shopify/__init__.py b/shopify/__init__.py deleted file mode 100644 index 5bad753c..00000000 --- a/shopify/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from shopify.version import VERSION -from shopify.session import Session, ValidationException -from shopify.resources import * diff --git a/shopify/base.py b/shopify/base.py deleted file mode 100644 index d514eace..00000000 --- a/shopify/base.py +++ /dev/null @@ -1,151 +0,0 @@ -import pyactiveresource.connection -from pyactiveresource.activeresource import ActiveResource, ResourceMeta, formats -import shopify.yamlobjects -import shopify.mixins as mixins -import shopify -import threading -import urllib -import urllib2 -import urlparse -import sys - -# Store the response from the last request in the connection object -class ShopifyConnection(pyactiveresource.connection.Connection): - response = None - - def __init__(self, site, user=None, password=None, timeout=None, - format=formats.JSONFormat): - super(ShopifyConnection, self).__init__(site, user, password, timeout, format) - - def _open(self, *args, **kwargs): - self.response = None - try: - self.response = super(ShopifyConnection, self)._open(*args, **kwargs) - except pyactiveresource.connection.ConnectionError, err: - self.response = err.response - raise - return self.response - -# Inherit from pyactiveresource's metaclass in order to use ShopifyConnection -class ShopifyResourceMeta(ResourceMeta): - - @property - def connection(cls): - """HTTP connection for the current thread""" - local = cls._threadlocal - if not getattr(local, 'connection', None): - # Make sure these variables are no longer affected by other threads. - local.user = cls.user - local.password = cls.password - local.site = cls.site - local.timeout = cls.timeout - local.headers = cls.headers - local.format = cls.format - if cls.site is None: - raise ValueError("No shopify session is active") - local.connection = ShopifyConnection( - cls.site, cls.user, cls.password, cls.timeout, cls.format) - return local.connection - - def get_user(cls): - return getattr(cls._threadlocal, 'user', ShopifyResource._user) - - def set_user(cls, value): - cls._threadlocal.connection = None - ShopifyResource._user = cls._threadlocal.user = value - - user = property(get_user, set_user, None, - "The username for HTTP Basic Auth.") - - def get_password(cls): - return getattr(cls._threadlocal, 'password', ShopifyResource._password) - - def set_password(cls, value): - cls._threadlocal.connection = None - ShopifyResource._password = cls._threadlocal.password = value - - password = property(get_password, set_password, None, - "The password for HTTP Basic Auth.") - - def get_site(cls): - return getattr(cls._threadlocal, 'site', ShopifyResource._site) - - def set_site(cls, value): - cls._threadlocal.connection = None - ShopifyResource._site = cls._threadlocal.site = value - if value is not None: - host = urlparse.urlsplit(value)[1] - auth_info, host = urllib2.splituser(host) - if auth_info: - user, password = urllib2.splitpasswd(auth_info) - if user: - cls.user = urllib.unquote(user) - if password: - cls.password = urllib.unquote(password) - - site = property(get_site, set_site, None, - 'The base REST site to connect to.') - - def get_timeout(cls): - return getattr(cls._threadlocal, 'timeout', ShopifyResource._timeout) - - def set_timeout(cls, value): - cls._threadlocal.connection = None - ShopifyResource._timeout = cls._threadlocal.timeout = value - - timeout = property(get_timeout, set_timeout, None, - 'Socket timeout for HTTP requests') - - def get_headers(cls): - if not hasattr(cls._threadlocal, 'headers'): - cls._threadlocal.headers = ShopifyResource._headers.copy() - return cls._threadlocal.headers - - def set_headers(cls, value): - cls._threadlocal.headers = value - - headers = property(get_headers, set_headers, None, - 'The headers sent with HTTP requests') - - def get_format(cls): - return getattr(cls._threadlocal, 'format', ShopifyResource._format) - - def set_format(cls, value): - cls._threadlocal.connection = None - ShopifyResource._format = cls._threadlocal.format = value - - format = property(get_format, set_format, None, - 'Encoding used for request and responses') - - -class ShopifyResource(ActiveResource, mixins.Countable): - __metaclass__ = ShopifyResourceMeta - _format = formats.JSONFormat - _threadlocal = threading.local() - _headers = {'User-Agent': 'ShopifyPythonAPI/%s Python/%s' % (shopify.VERSION, sys.version.split(' ', 1)[0])} - - def __init__(self, attributes=None, prefix_options=None): - if attributes is not None and prefix_options is None: - prefix_options, attributes = self.__class__._split_options(attributes) - return super(ShopifyResource, self).__init__(attributes, prefix_options) - - def is_new(self): - return not self.id - - def _load_attributes_from_response(self, response): - if response.body.strip(): - self._update(self.__class__.format.decode(response.body)) - - @classmethod - def activate_session(cls, session): - cls.site = session.site - cls.user = None - cls.password = None - cls.headers['X-Shopify-Access-Token'] = session.token - - @classmethod - def clear_session(cls): - cls.site = None - cls.user = None - cls.password = None - cls.headers.pop('X-Shopify-Access-Token', None) diff --git a/shopify/mixins.py b/shopify/mixins.py deleted file mode 100644 index 9d3c1796..00000000 --- a/shopify/mixins.py +++ /dev/null @@ -1,29 +0,0 @@ -import shopify.resources - -class Countable(object): - - @classmethod - def count(cls, _options=None, **kwargs): - if _options is None: - _options = kwargs - return int(cls.get("count", **_options)) - - -class Metafields(object): - - def metafields(self): - return shopify.resources.Metafield.find(resource=self.__class__.plural, resource_id=self.id) - - def add_metafield(self, metafield): - if self.is_new(): - raise ValueError("You can only add metafields to a resource that has been saved") - - metafield._prefix_options = dict(resource=self.__class__.plural, resource_id=self.id) - metafield.save() - return metafield - - -class Events(object): - - def events(self): - return shopify.resources.Event.find(resource=self.__class__.plural, resource_id=self.id) diff --git a/shopify/resources/__init__.py b/shopify/resources/__init__.py deleted file mode 100644 index 0884ff89..00000000 --- a/shopify/resources/__init__.py +++ /dev/null @@ -1,46 +0,0 @@ -from shop import Shop -from product import Product -from cart import Cart -from custom_collection import CustomCollection -from collect import Collect -from shipping_address import ShippingAddress -from billing_address import BillingAddress -from line_item import LineItem -from shipping_line import ShippingLine -from note_attribute import NoteAttribute -from address import Address -from option import Option -from payment_details import PaymentDetails -from receipt import Receipt -from rule import Rule -from tax_line import TaxLine -from script_tag import ScriptTag -from product_search_engine import ProductSearchEngine -from application_charge import ApplicationCharge -from recurring_application_charge import RecurringApplicationCharge -from asset import Asset -from theme import Theme -from customer_saved_search import CustomerSavedSearch -from customer_group import CustomerGroup -from customer import Customer -from event import Event -from webhook import Webhook -from redirect import Redirect -from province import Province -from comment import Comment -from metafield import Metafield -from article import Article -from blog import Blog -from page import Page -from country import Country -from fulfillment import Fulfillment -from fulfillment_service import FulfillmentService -from carrier_service import CarrierService -from transaction import Transaction -from image import Image -from variant import Variant -from order import Order -from order_risk import OrderRisk -from smart_collection import SmartCollection - -from ..base import ShopifyResource diff --git a/shopify/resources/address.py b/shopify/resources/address.py deleted file mode 100644 index b5d10718..00000000 --- a/shopify/resources/address.py +++ /dev/null @@ -1,5 +0,0 @@ -from ..base import ShopifyResource - - -class Address(ShopifyResource): - pass diff --git a/shopify/resources/application_charge.py b/shopify/resources/application_charge.py deleted file mode 100644 index df19e18c..00000000 --- a/shopify/resources/application_charge.py +++ /dev/null @@ -1,7 +0,0 @@ -from ..base import ShopifyResource - - -class ApplicationCharge(ShopifyResource): - - def activate(self): - self._load_attributes_from_response(self.post("activate")) diff --git a/shopify/resources/article.py b/shopify/resources/article.py deleted file mode 100644 index cec30d5a..00000000 --- a/shopify/resources/article.py +++ /dev/null @@ -1,26 +0,0 @@ -from ..base import ShopifyResource -from shopify import mixins -from comment import Comment - - -class Article(ShopifyResource, mixins.Metafields, mixins.Events): - _prefix_source = "/admin/blogs/$blog_id/" - - @classmethod - def _prefix(cls, options={}): - blog_id = options.get("blog_id") - if blog_id: - return "/admin/blogs/%s" % (blog_id) - else: - return "/admin" - - def comments(self): - return Comment.find(article_id=self.id) - - @classmethod - def authors(cls, **kwargs): - return cls.get('authors', **kwargs) - - @classmethod - def tags(cls, **kwargs): - return cls.get('tags', **kwargs) diff --git a/shopify/resources/asset.py b/shopify/resources/asset.py deleted file mode 100644 index 19a7bd76..00000000 --- a/shopify/resources/asset.py +++ /dev/null @@ -1,78 +0,0 @@ -from ..base import ShopifyResource -import base64 - - -class Asset(ShopifyResource): - _primary_key = "key" - _prefix_source = "/admin/themes/$theme_id/" - - @classmethod - def _prefix(cls, options={}): - theme_id = options.get("theme_id") - if theme_id: - return "/admin/themes/%s" % theme_id - else: - return "/admin" - - @classmethod - def _element_path(cls, id, prefix_options={}, query_options=None): - if query_options is None: - prefix_options, query_options = cls._split_options(prefix_options) - return "%s%s.%s%s" % (cls._prefix(prefix_options)+'/', cls.plural, - cls.format.extension, cls._query_string(query_options)) - - @classmethod - def find(cls, key=None, **kwargs): - """ - Find an asset by key - E.g. - shopify.Asset.find('layout/theme.liquid', theme_id=99) - """ - if not key: - return super(Asset, cls).find(**kwargs) - - params = {"asset[key]": key} - params.update(kwargs) - theme_id = params.get("theme_id") - path_prefix = "/admin/themes/%s" % (theme_id) if theme_id else "/admin" - - resource = cls.find_one("%s/assets.%s" % (path_prefix, cls.format.extension), **params) - - if theme_id and resource: - resource._prefix_options["theme_id"] = theme_id - return resource - - def __get_value(self): - data = self.attributes.get("value") - if data: - return data - data = self.attributes.get("attachment") - if data: - return base64.b64decode(data) - - def __set_value(self, data): - self.__wipe_value_attributes() - self.attributes["value"] = data - - value = property(__get_value, __set_value, None, "The asset's value or attachment") - - def attach(self, data): - self.attachment = base64.b64encode(data) - - def destroy(self): - options = {"asset[key]": self.key} - options.update(self._prefix_options) - return self.__class__.connection.delete(self._element_path(self.key, options), self.__class__.headers) - - def is_new(self): - return False - - def __setattr__(self, name, value): - if name in ("value", "attachment", "src", "source_key"): - self.__wipe_value_attributes() - return super(Asset, self).__setattr__(name, value) - - def __wipe_value_attributes(self): - for attr in ("value", "attachment", "src", "source_key"): - if attr in self.attributes: - del self.attributes[attr] diff --git a/shopify/resources/billing_address.py b/shopify/resources/billing_address.py deleted file mode 100644 index 4bf328db..00000000 --- a/shopify/resources/billing_address.py +++ /dev/null @@ -1,5 +0,0 @@ -from ..base import ShopifyResource - - -class BillingAddress(ShopifyResource): - pass diff --git a/shopify/resources/blog.py b/shopify/resources/blog.py deleted file mode 100644 index c094560e..00000000 --- a/shopify/resources/blog.py +++ /dev/null @@ -1,9 +0,0 @@ -from ..base import ShopifyResource -from shopify import mixins -from article import Article - - -class Blog(ShopifyResource, mixins.Metafields, mixins.Events): - - def articles(self): - return Article.find(blog_id=self.id) diff --git a/shopify/resources/carrier_service.py b/shopify/resources/carrier_service.py deleted file mode 100644 index 1dfdbb08..00000000 --- a/shopify/resources/carrier_service.py +++ /dev/null @@ -1,5 +0,0 @@ -from ..base import ShopifyResource - - -class CarrierService(ShopifyResource): - pass diff --git a/shopify/resources/cart.py b/shopify/resources/cart.py deleted file mode 100644 index da611d52..00000000 --- a/shopify/resources/cart.py +++ /dev/null @@ -1,5 +0,0 @@ -from ..base import ShopifyResource - - -class Cart(ShopifyResource): - pass diff --git a/shopify/resources/collect.py b/shopify/resources/collect.py deleted file mode 100644 index f40b45ed..00000000 --- a/shopify/resources/collect.py +++ /dev/null @@ -1,5 +0,0 @@ -from ..base import ShopifyResource - - -class Collect(ShopifyResource): - pass diff --git a/shopify/resources/comment.py b/shopify/resources/comment.py deleted file mode 100644 index 0015eb74..00000000 --- a/shopify/resources/comment.py +++ /dev/null @@ -1,19 +0,0 @@ -from ..base import ShopifyResource - - -class Comment(ShopifyResource): - - def remove(self): - self._load_attributes_from_response(self.post("remove")) - - def spam(self): - self._load_attributes_from_response(self.post("spam")) - - def approve(self): - self._load_attributes_from_response(self.post("approve")) - - def restore(self): - self._load_attributes_from_response(self.post("restore")) - - def not_spam(self): - self._load_attributes_from_response(self.post("not_spam")) diff --git a/shopify/resources/country.py b/shopify/resources/country.py deleted file mode 100644 index 39839808..00000000 --- a/shopify/resources/country.py +++ /dev/null @@ -1,5 +0,0 @@ -from ..base import ShopifyResource - - -class Country(ShopifyResource): - pass diff --git a/shopify/resources/custom_collection.py b/shopify/resources/custom_collection.py deleted file mode 100644 index fe648bee..00000000 --- a/shopify/resources/custom_collection.py +++ /dev/null @@ -1,18 +0,0 @@ -from ..base import ShopifyResource -from shopify import mixins -from collect import Collect -import product - - -class CustomCollection(ShopifyResource, mixins.Metafields, mixins.Events): - - def products(self): - return product.Product.find(collection_id=self.id) - - def add_product(self, product): - return Collect.create({'collection_id': self.id, 'product_id': product.id}) - - def remove_product(self, product): - collect = Collect.find_first(collection_id=self.id, product_id=product.id) - if collect: - collect.destroy() diff --git a/shopify/resources/customer.py b/shopify/resources/customer.py deleted file mode 100644 index 2855962a..00000000 --- a/shopify/resources/customer.py +++ /dev/null @@ -1,20 +0,0 @@ -from ..base import ShopifyResource -from shopify import mixins - - -class Customer(ShopifyResource, mixins.Metafields): - - @classmethod - def search(cls, **kwargs): - """ - Search for customers matching supplied query - - Args: - q: Text to search for customers ("q" is short for query) - f: Filters to apply to customers ("f" is short for query) - page: Page to show (default: 1) - limit: Maximum number of results to show (default: 50, maximum: 250) - Returns: - An array of customers. - """ - return cls._build_list(cls.get("search", **kwargs)) diff --git a/shopify/resources/customer_group.py b/shopify/resources/customer_group.py deleted file mode 100644 index 900c0b4a..00000000 --- a/shopify/resources/customer_group.py +++ /dev/null @@ -1,5 +0,0 @@ -from customer_saved_search import CustomerSavedSearch - - -class CustomerGroup(CustomerSavedSearch): - pass diff --git a/shopify/resources/customer_saved_search.py b/shopify/resources/customer_saved_search.py deleted file mode 100644 index a19dae48..00000000 --- a/shopify/resources/customer_saved_search.py +++ /dev/null @@ -1,8 +0,0 @@ -from ..base import ShopifyResource -from customer import Customer - - -class CustomerSavedSearch(ShopifyResource): - - def customers(cls, **kwargs): - return Customer._build_list(cls.get("customers", **kwargs)) diff --git a/shopify/resources/event.py b/shopify/resources/event.py deleted file mode 100644 index 8b25ce38..00000000 --- a/shopify/resources/event.py +++ /dev/null @@ -1,12 +0,0 @@ -from ..base import ShopifyResource - -class Event(ShopifyResource): - _prefix_source = "/admin/$resource/$resource_id/" - - @classmethod - def _prefix(cls, options={}): - resource = options.get("resource") - if resource: - return "/admin/%s/%s" % (resource, options["resource_id"]) - else: - return "/admin" diff --git a/shopify/resources/fulfillment.py b/shopify/resources/fulfillment.py deleted file mode 100644 index 10096a8e..00000000 --- a/shopify/resources/fulfillment.py +++ /dev/null @@ -1,11 +0,0 @@ -from ..base import ShopifyResource - - -class Fulfillment(ShopifyResource): - _prefix_source = "/admin/orders/$order_id/" - - def cancel(self): - self._load_attributes_from_response(self.post("cancel")) - - def complete(self): - self._load_attributes_from_response(self.post("complete")) diff --git a/shopify/resources/fulfillment_service.py b/shopify/resources/fulfillment_service.py deleted file mode 100644 index f303fe38..00000000 --- a/shopify/resources/fulfillment_service.py +++ /dev/null @@ -1,5 +0,0 @@ -from ..base import ShopifyResource - - -class FulfillmentService(ShopifyResource): - pass diff --git a/shopify/resources/image.py b/shopify/resources/image.py deleted file mode 100644 index 755c09e7..00000000 --- a/shopify/resources/image.py +++ /dev/null @@ -1,18 +0,0 @@ -from ..base import ShopifyResource -import base64 -import re - - -class Image(ShopifyResource): - _prefix_source = "/admin/products/$product_id/" - - def __getattr__(self, name): - if name in ["pico", "icon", "thumb", "small", "compact", "medium", "large", "grande", "original"]: - return re.sub(r"/(.*)\.(\w{2,4})", r"/\1_%s.\2" % (name), self.src) - else: - return super(Image, self).__getattr__(name) - - def attach_image(self, data, filename=None): - self.attributes["attachment"] = base64.b64encode(data) - if filename: - self.attributes["filename"] = filename diff --git a/shopify/resources/line_item.py b/shopify/resources/line_item.py deleted file mode 100644 index cac2ceed..00000000 --- a/shopify/resources/line_item.py +++ /dev/null @@ -1,6 +0,0 @@ -from ..base import ShopifyResource - - -class LineItem(ShopifyResource): - class Property(ShopifyResource): - pass diff --git a/shopify/resources/metafield.py b/shopify/resources/metafield.py deleted file mode 100644 index f37af161..00000000 --- a/shopify/resources/metafield.py +++ /dev/null @@ -1,13 +0,0 @@ -from ..base import ShopifyResource - - -class Metafield(ShopifyResource): - _prefix_source = "/admin/$resource/$resource_id/" - - @classmethod - def _prefix(cls, options={}): - resource = options.get("resource") - if resource: - return "/admin/%s/%s" % (resource, options["resource_id"]) - else: - return "/admin" diff --git a/shopify/resources/note_attribute.py b/shopify/resources/note_attribute.py deleted file mode 100644 index 9e997e5e..00000000 --- a/shopify/resources/note_attribute.py +++ /dev/null @@ -1,5 +0,0 @@ -from ..base import ShopifyResource - - -class NoteAttribute(ShopifyResource): - pass diff --git a/shopify/resources/option.py b/shopify/resources/option.py deleted file mode 100644 index 3ff640d5..00000000 --- a/shopify/resources/option.py +++ /dev/null @@ -1,5 +0,0 @@ -from ..base import ShopifyResource - - -class Option(ShopifyResource): - pass diff --git a/shopify/resources/order.py b/shopify/resources/order.py deleted file mode 100644 index c902a5d0..00000000 --- a/shopify/resources/order.py +++ /dev/null @@ -1,21 +0,0 @@ -from ..base import ShopifyResource -from shopify import mixins -from transaction import Transaction - - -class Order(ShopifyResource, mixins.Metafields, mixins.Events): - - def close(self): - self._load_attributes_from_response(self.post("close")) - - def open(self): - self._load_attributes_from_response(self.post("open")) - - def cancel(self, **kwargs): - self._load_attributes_from_response(self.post("cancel", **kwargs)) - - def transactions(self): - return Transaction.find(order_id=self.id) - - def capture(self, amount=""): - return Transaction.create({"amount": amount, "kind": "capture", "order_id": self.id}) diff --git a/shopify/resources/order_risk.py b/shopify/resources/order_risk.py deleted file mode 100644 index 2cc4c268..00000000 --- a/shopify/resources/order_risk.py +++ /dev/null @@ -1,5 +0,0 @@ -from ..base import ShopifyResource - -class OrderRisk(ShopifyResource): - _prefix_source = "/admin/orders/$order_id/" - _plural = "risks" diff --git a/shopify/resources/page.py b/shopify/resources/page.py deleted file mode 100644 index aa2711b2..00000000 --- a/shopify/resources/page.py +++ /dev/null @@ -1,6 +0,0 @@ -from ..base import ShopifyResource -from shopify import mixins - - -class Page(ShopifyResource, mixins.Metafields, mixins.Events): - pass diff --git a/shopify/resources/payment_details.py b/shopify/resources/payment_details.py deleted file mode 100644 index 24bed6d0..00000000 --- a/shopify/resources/payment_details.py +++ /dev/null @@ -1,5 +0,0 @@ -from ..base import ShopifyResource - - -class PaymentDetails(ShopifyResource): - pass diff --git a/shopify/resources/product.py b/shopify/resources/product.py deleted file mode 100644 index f824e252..00000000 --- a/shopify/resources/product.py +++ /dev/null @@ -1,33 +0,0 @@ -from ..base import ShopifyResource -from shopify import mixins -from custom_collection import CustomCollection -from smart_collection import SmartCollection - - -class Product(ShopifyResource, mixins.Metafields, mixins.Events): - - def price_range(self): - prices = [float(variant.price) for variant in self.variants] - f = "%0.2f" - min_price = min(prices) - max_price = max(prices) - if min_price != max_price: - return "%s - %s" % (f % min_price, f % max_price) - else: - return f % min_price - - def collections(self): - return CustomCollection.find(product_id=self.id) - - def smart_collections(self): - return SmartCollection.find(product_id=self.id) - - def add_to_collection(self, collection): - return collection.add_product(self) - - def remove_from_collection(self, collection): - return collection.remove_product(self) - - def add_variant(self, variant): - variant.attributes['product_id'] = self.id - return variant.save() diff --git a/shopify/resources/product_search_engine.py b/shopify/resources/product_search_engine.py deleted file mode 100644 index 44976245..00000000 --- a/shopify/resources/product_search_engine.py +++ /dev/null @@ -1,5 +0,0 @@ -from ..base import ShopifyResource - - -class ProductSearchEngine(ShopifyResource): - pass diff --git a/shopify/resources/province.py b/shopify/resources/province.py deleted file mode 100644 index d34496f9..00000000 --- a/shopify/resources/province.py +++ /dev/null @@ -1,5 +0,0 @@ -from ..base import ShopifyResource - - -class Province(ShopifyResource): - _prefix_source = "/admin/countries/$country_id/" diff --git a/shopify/resources/receipt.py b/shopify/resources/receipt.py deleted file mode 100644 index 0dd86a39..00000000 --- a/shopify/resources/receipt.py +++ /dev/null @@ -1,5 +0,0 @@ -from ..base import ShopifyResource - - -class Receipt(ShopifyResource): - pass diff --git a/shopify/resources/recurring_application_charge.py b/shopify/resources/recurring_application_charge.py deleted file mode 100644 index f0e30045..00000000 --- a/shopify/resources/recurring_application_charge.py +++ /dev/null @@ -1,14 +0,0 @@ -from ..base import ShopifyResource - - -class RecurringApplicationCharge(ShopifyResource): - - @classmethod - def current(cls): - return cls.find_first(status="active") - - def cancel(self): - self._load_attributes_from_response(self.destroy) - - def activate(self): - self._load_attributes_from_response(self.post("activate")) diff --git a/shopify/resources/redirect.py b/shopify/resources/redirect.py deleted file mode 100644 index 041ecb81..00000000 --- a/shopify/resources/redirect.py +++ /dev/null @@ -1,5 +0,0 @@ -from ..base import ShopifyResource - - -class Redirect(ShopifyResource): - pass diff --git a/shopify/resources/rule.py b/shopify/resources/rule.py deleted file mode 100644 index 27c460c3..00000000 --- a/shopify/resources/rule.py +++ /dev/null @@ -1,5 +0,0 @@ -from ..base import ShopifyResource - - -class Rule(ShopifyResource): - pass diff --git a/shopify/resources/script_tag.py b/shopify/resources/script_tag.py deleted file mode 100644 index b627778c..00000000 --- a/shopify/resources/script_tag.py +++ /dev/null @@ -1,5 +0,0 @@ -from ..base import ShopifyResource - - -class ScriptTag(ShopifyResource): - pass diff --git a/shopify/resources/shipping_address.py b/shopify/resources/shipping_address.py deleted file mode 100644 index 283aff34..00000000 --- a/shopify/resources/shipping_address.py +++ /dev/null @@ -1,5 +0,0 @@ -from ..base import ShopifyResource - - -class ShippingAddress(ShopifyResource): - pass diff --git a/shopify/resources/shipping_line.py b/shopify/resources/shipping_line.py deleted file mode 100644 index 1f0d4c2b..00000000 --- a/shopify/resources/shipping_line.py +++ /dev/null @@ -1,5 +0,0 @@ -from ..base import ShopifyResource - - -class ShippingLine(ShopifyResource): - pass diff --git a/shopify/resources/shop.py b/shopify/resources/shop.py deleted file mode 100644 index e179eef4..00000000 --- a/shopify/resources/shop.py +++ /dev/null @@ -1,22 +0,0 @@ -from ..base import ShopifyResource -from metafield import Metafield -from event import Event - - -class Shop(ShopifyResource): - - @classmethod - def current(cls): - return cls.find_one("/admin/shop." + cls.format.extension) - - def metafields(self): - return Metafield.find() - - def add_metafield(self, metafield): - if self.is_new(): - raise ValueError("You can only add metafields to a resource that has been saved") - metafield.save() - return metafield - - def events(self): - return Event.find() diff --git a/shopify/resources/smart_collection.py b/shopify/resources/smart_collection.py deleted file mode 100644 index 1ff4e607..00000000 --- a/shopify/resources/smart_collection.py +++ /dev/null @@ -1,9 +0,0 @@ -from ..base import ShopifyResource -from shopify import mixins -import product - - -class SmartCollection(ShopifyResource, mixins.Metafields, mixins.Events): - - def products(self): - return product.Product.find(collection_id=self.id) diff --git a/shopify/resources/tax_line.py b/shopify/resources/tax_line.py deleted file mode 100644 index b9cda0c7..00000000 --- a/shopify/resources/tax_line.py +++ /dev/null @@ -1,5 +0,0 @@ -from ..base import ShopifyResource - - -class TaxLine(ShopifyResource): - pass diff --git a/shopify/resources/theme.py b/shopify/resources/theme.py deleted file mode 100644 index d0ea8655..00000000 --- a/shopify/resources/theme.py +++ /dev/null @@ -1,5 +0,0 @@ -from ..base import ShopifyResource - - -class Theme(ShopifyResource): - pass diff --git a/shopify/resources/transaction.py b/shopify/resources/transaction.py deleted file mode 100644 index aaed92b9..00000000 --- a/shopify/resources/transaction.py +++ /dev/null @@ -1,5 +0,0 @@ -from ..base import ShopifyResource - - -class Transaction(ShopifyResource): - _prefix_source = "/admin/orders/$order_id/" diff --git a/shopify/resources/variant.py b/shopify/resources/variant.py deleted file mode 100644 index cc4f5485..00000000 --- a/shopify/resources/variant.py +++ /dev/null @@ -1,19 +0,0 @@ -from ..base import ShopifyResource -from shopify import mixins - - -class Variant(ShopifyResource, mixins.Metafields): - _prefix_source = "/admin/products/$product_id/" - - @classmethod - def _prefix(cls, options={}): - product_id = options.get("product_id") - if product_id: - return "/admin/products/%s" % (product_id) - else: - return "/admin" - - def save(self): - if 'product_id' not in self._prefix_options: - self._prefix_options['product_id'] = self.product_id - return super(ShopifyResource, self).save() diff --git a/shopify/resources/webhook.py b/shopify/resources/webhook.py deleted file mode 100644 index 452934f3..00000000 --- a/shopify/resources/webhook.py +++ /dev/null @@ -1,12 +0,0 @@ -from ..base import ShopifyResource - - -class Webhook(ShopifyResource): - - def __get_format(self): - return self.attributes.get("format") - - def __set_format(self, data): - self.attributes["format"] = data - - format = property(__get_format, __set_format, None, "Format attribute") diff --git a/shopify/session.py b/shopify/session.py deleted file mode 100644 index 14c1cbac..00000000 --- a/shopify/session.py +++ /dev/null @@ -1,111 +0,0 @@ -import time -import urllib -import urllib2 -try: - from hashlib import md5 -except ImportError: - from md5 import md5 -try: - import simplejson as json -except ImportError: - import json -import re -from contextlib import contextmanager - -class ValidationException(Exception): - pass - -class Session(object): - api_key = None - secret = None - protocol = 'https' - - @classmethod - def setup(cls, **kwargs): - for k, v in kwargs.iteritems(): - setattr(cls, k, v) - - @classmethod - @contextmanager - def temp(cls, domain, token): - import shopify - original_domain = shopify.ShopifyResource.get_site() - original_token = shopify.ShopifyResource.get_headers().get('X-Shopify-Access-Token') - original_session = shopify.Session(original_domain, original_token) - - session = Session(domain, token) - shopify.ShopifyResource.activate_session(session) - yield - shopify.ShopifyResource.activate_session(original_session) - - def __init__(self, shop_url, token=None, params=None): - self.url = self.__prepare_url(shop_url) - self.token = token - return - - def create_permission_url(self, scope, redirect_uri=None): - query_params = dict(client_id=self.api_key, scope=",".join(scope)) - if redirect_uri: query_params['redirect_uri'] = redirect_uri - return "%s://%s/admin/oauth/authorize?%s" % (self.protocol, self.url, urllib.urlencode(query_params)) - - def request_token(self, params): - if self.token: - return self.token - - if not self.validate_params(params): - raise ValidationException('Invalid Signature: Possibly malicious login') - - code = params['code'] - - url = "%s://%s/admin/oauth/access_token?" % (self.protocol, self.url) - query_params = dict(client_id=self.api_key, client_secret=self.secret, code=code) - request = urllib2.Request(url, urllib.urlencode(query_params)) - response = urllib2.urlopen(request) - - if response.code == 200: - self.token = json.loads(response.read())['access_token'] - return self.token - else: - raise Exception(response.msg) - - @property - def site(self): - return "%s://%s/admin" % (self.protocol, self.url) - - @property - def valid(self): - return self.url is not None and self.token is not None - - @staticmethod - def __prepare_url(url): - if not url or (url.strip() == ""): - return None - url = re.sub("https?://", "", url) - url = re.sub("/.*", "", url) - if url.find(".") == -1: - url += ".myshopify.com" - return url - - @classmethod - def validate_params(cls, params): - # Avoid replay attacks by making sure the request - # isn't more than a day old. - one_day = 24 * 60 * 60 - if int(params['timestamp']) < time.time() - one_day: - return False - - return cls.validate_signature(params) - - @classmethod - def validate_signature(cls, params): - if "signature" not in params: - return False - - sorted_params = "" - signature = params['signature'] - - for k in sorted(params.keys()): - if k != "signature": - sorted_params += k + "=" + str(params[k]) - - return md5(cls.secret + sorted_params).hexdigest() == signature diff --git a/shopify/version.py b/shopify/version.py deleted file mode 100644 index f80f88dc..00000000 --- a/shopify/version.py +++ /dev/null @@ -1 +0,0 @@ -VERSION = '2.0.4' diff --git a/shopify/yamlobjects.py b/shopify/yamlobjects.py deleted file mode 100644 index d4e7cd3a..00000000 --- a/shopify/yamlobjects.py +++ /dev/null @@ -1,18 +0,0 @@ -try: - # Shopify serializes receipts in YAML format, and yaml.safe_load will - # not automatically load custom types because of security purpose, - # so create safe loaders for types returned from Shopify here. - # - # The YAMLObject metaclass will automatically add these classes to - # the list of constructors for yaml.safe_load to use. - import yaml - - class YAMLHashWithIndifferentAccess(yaml.YAMLObject): - yaml_tag = '!map:ActiveSupport::HashWithIndifferentAccess' - yaml_loader = yaml.SafeLoader - - @classmethod - def from_yaml(cls, loader, node): - return loader.construct_mapping(node, cls) -except ImportError: - pass diff --git a/test/__init__.py b/test/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/test/article_test.py b/test/article_test.py deleted file mode 100644 index e8f6f234..00000000 --- a/test/article_test.py +++ /dev/null @@ -1,65 +0,0 @@ -import shopify -from test_helper import TestCase - -class ArticleTest(TestCase): - - def test_create_article(self): - self.fake("blogs/1008414260/articles", method='POST', body=self.load_fixture('article'), headers={'Content-type': 'application/json'}) - article = shopify.Article({'blog_id':1008414260}) - article.save() - self.assertEqual("First Post", article.title) - - def test_get_article(self): - self.fake('articles/6242736', method='GET', body=self.load_fixture('article')) - article = shopify.Article.find(6242736) - self.assertEqual("First Post", article.title) - - def test_update_article(self): - self.fake('articles/6242736', method='GET', body=self.load_fixture('article')) - article = shopify.Article.find(6242736) - - self.fake('articles/6242736', method='PUT', body=self.load_fixture('article'), headers={'Content-type': 'application/json'}) - article.save() - - def test_get_articles(self): - self.fake("articles", method='GET', body=self.load_fixture('articles')) - articles = shopify.Article.find() - self.assertEqual(3, len(articles)) - - def test_get_articles_namespaced(self): - self.fake("blogs/1008414260/articles", method='GET', body=self.load_fixture('articles')) - articles = shopify.Article.find(blog_id=1008414260) - self.assertEqual(3, len(articles)) - - def test_get_article_namespaced(self): - self.fake("blogs/1008414260/articles/6242736", method='GET', body=self.load_fixture('article')) - article = shopify.Article.find(6242736, blog_id=1008414260) - self.assertEqual("First Post", article.title) - - def test_get_authors(self): - self.fake("articles/authors", method='GET', body=self.load_fixture('authors')) - authors = shopify.Article.authors() - self.assertEqual("Shopify", authors[0]) - self.assertEqual("development shop", authors[-1]) - - def test_get_authors_for_blog_id(self): - self.fake("blogs/1008414260/articles/authors", method='GET', body=self.load_fixture('authors')) - authors = shopify.Article.authors(blog_id=1008414260) - self.assertEqual(3, len(authors)) - - def test_get_tags(self): - self.fake("articles/tags", method='GET', body=self.load_fixture('tags')) - tags = shopify.Article.tags() - self.assertEqual("consequuntur", tags[0]) - self.assertEqual("repellendus", tags[-1]) - - def test_get_tags_for_blog_id(self): - self.fake("blogs/1008414260/articles/tags", method='GET', body=self.load_fixture('tags')) - tags = shopify.Article.tags(blog_id=1008414260) - self.assertEqual("consequuntur", tags[0]) - self.assertEqual("repellendus", tags[-1]) - - def test_get_popular_tags(self): - self.fake("articles/tags.json?limit=1&popular=1", extension=False, method='GET', body=self.load_fixture('tags')) - tags = shopify.Article.tags(popular=1, limit=1) - self.assertEqual(3, len(tags)) diff --git a/test/asset_test.py b/test/asset_test.py deleted file mode 100644 index 00ce1396..00000000 --- a/test/asset_test.py +++ /dev/null @@ -1,41 +0,0 @@ -import shopify -from test_helper import TestCase - -class AssetTest(TestCase): - - def test_get_assets(self): - self.fake("assets", method='GET', body=self.load_fixture('assets')) - v = shopify.Asset.find() - - def test_get_asset(self): - self.fake("assets.json?asset%5Bkey%5D=templates%2Findex.liquid", extension=False, method='GET', body=self.load_fixture('asset')) - v = shopify.Asset.find('templates/index.liquid') - - def test_update_asset(self): - self.fake("assets.json?asset%5Bkey%5D=templates%2Findex.liquid", extension=False, method='GET', body=self.load_fixture('asset')) - v = shopify.Asset.find('templates/index.liquid') - - self.fake("assets", method='PUT', body=self.load_fixture('asset'), headers={'Content-type': 'application/json'}) - v.save() - - def test_get_assets_namespaced(self): - self.fake("themes/1/assets", method='GET', body=self.load_fixture('assets')) - v = shopify.Asset.find(theme_id = 1) - - def test_get_asset_namespaced(self): - self.fake("themes/1/assets.json?asset%5Bkey%5D=templates%2Findex.liquid&theme_id=1", extension=False, method='GET', body=self.load_fixture('asset')) - v = shopify.Asset.find('templates/index.liquid', theme_id=1) - - def test_update_asset_namespaced(self): - self.fake("themes/1/assets.json?asset%5Bkey%5D=templates%2Findex.liquid&theme_id=1", extension=False, method='GET', body=self.load_fixture('asset')) - v = shopify.Asset.find('templates/index.liquid', theme_id=1) - - self.fake("themes/1/assets", method='PUT', body=self.load_fixture('asset'), headers={'Content-type': 'application/json'}) - v.save() - - def test_delete_asset_namespaced(self): - self.fake("themes/1/assets.json?asset%5Bkey%5D=templates%2Findex.liquid&theme_id=1", extension=False, method='GET', body=self.load_fixture('asset')) - v = shopify.Asset.find('templates/index.liquid', theme_id=1) - - self.fake("themes/1/assets.json?asset%5Bkey%5D=templates%2Findex.liquid", extension=False, method='DELETE', body="{}") - v.destroy() diff --git a/test/base_test.py b/test/base_test.py deleted file mode 100644 index 9e022a94..00000000 --- a/test/base_test.py +++ /dev/null @@ -1,84 +0,0 @@ -import shopify -from test_helper import TestCase -from pyactiveresource.activeresource import ActiveResource -from mock import patch -import threading - -class BaseTest(TestCase): - - @classmethod - def setUpClass(self): - self.session1 = shopify.Session('shop1.myshopify.com', 'token1') - self.session2 = shopify.Session('shop2.myshopify.com', 'token2') - - def setUp(self): - super(BaseTest, self).setUp() - - def tearDown(self): - shopify.ShopifyResource.clear_session() - - def test_activate_session_should_set_site_and_headers_for_given_session(self): - shopify.ShopifyResource.activate_session(self.session1) - - self.assertIsNone(ActiveResource.site) - self.assertEqual('https://shop1.myshopify.com/admin', shopify.ShopifyResource.site) - self.assertEqual('https://shop1.myshopify.com/admin', shopify.Shop.site) - self.assertIsNone(ActiveResource.headers) - self.assertEqual('token1', shopify.ShopifyResource.headers['X-Shopify-Access-Token']) - self.assertEqual('token1', shopify.Shop.headers['X-Shopify-Access-Token']) - - def test_clear_session_should_clear_site_and_headers_from_Base(self): - shopify.ShopifyResource.activate_session(self.session1) - shopify.ShopifyResource.clear_session() - - self.assertIsNone(ActiveResource.site) - self.assertIsNone(shopify.ShopifyResource.site) - self.assertIsNone(shopify.Shop.site) - - self.assertIsNone(ActiveResource.headers) - self.assertFalse('X-Shopify-Access-Token' in shopify.ShopifyResource.headers) - self.assertFalse('X-Shopify-Access-Token' in shopify.Shop.headers) - - def test_activate_session_with_one_session_then_clearing_and_activating_with_another_session_shoul_request_to_correct_shop(self): - shopify.ShopifyResource.activate_session(self.session1) - shopify.ShopifyResource.clear_session - shopify.ShopifyResource.activate_session(self.session2) - - self.assertIsNone(ActiveResource.site) - self.assertEqual('https://shop2.myshopify.com/admin', shopify.ShopifyResource.site) - self.assertEqual('https://shop2.myshopify.com/admin', shopify.Shop.site) - - self.assertIsNone(ActiveResource.headers) - self.assertEqual('token2', shopify.ShopifyResource.headers['X-Shopify-Access-Token']) - self.assertEqual('token2', shopify.Shop.headers['X-Shopify-Access-Token']) - - def test_delete_should_send_custom_headers_with_request(self): - shopify.ShopifyResource.activate_session(self.session1) - - org_headers=shopify.ShopifyResource.headers - shopify.ShopifyResource.set_headers({'X-Custom': 'abc'}) - - with patch('shopify.ShopifyResource.connection.delete') as mock: - shopify.ShopifyResource.delete('1') - mock.assert_called_with('/admin/shopify_resources/1.json', {'X-Custom': 'abc'}) - - shopify.ShopifyResource.set_headers(org_headers) - - def test_headers_includes_user_agent(self): - self.assertTrue('User-Agent' in shopify.ShopifyResource.headers) - t = threading.Thread(target=lambda: self.assertTrue('User-Agent' in shopify.ShopifyResource.headers)) - t.start() - t.join() - - def test_headers_is_thread_safe(self): - def testFunc(): - shopify.ShopifyResource.headers['X-Custom'] = 'abc' - self.assertTrue('X-Custom' in shopify.ShopifyResource.headers) - - t1 = threading.Thread(target=testFunc) - t1.start() - t1.join() - - t2 = threading.Thread(target=lambda: self.assertFalse('X-Custom' in shopify.ShopifyResource.headers)) - t2.start() - t2.join() diff --git a/test/blog_test.py b/test/blog_test.py deleted file mode 100644 index 465df603..00000000 --- a/test/blog_test.py +++ /dev/null @@ -1,9 +0,0 @@ -import shopify -from test_helper import TestCase - -class BlogTest(TestCase): - - def test_blog_creation(self): - self.fake('blogs', method='POST', code=202, body=self.load_fixture('blog'), headers={'Content-type': 'application/json'}) - blog = shopify.Blog.create({'title': "Test Blog"}) - self.assertEqual("Test Blog", blog.title) diff --git a/test/carrier_service_test.py b/test/carrier_service_test.py deleted file mode 100644 index a97e1bb5..00000000 --- a/test/carrier_service_test.py +++ /dev/null @@ -1,15 +0,0 @@ -import shopify -from test_helper import TestCase - -class CarrierServiceTest(TestCase): - def test_create_new_carrier_service(self): - self.fake("carrier_services", method='POST', body=self.load_fixture('carrier_service'), headers={'Content-type': 'application/json'}) - - carrier_service = shopify.CarrierService.create({'name': "Some Postal Service"}) - self.assertEqual("Some Postal Service", carrier_service.name) - - def test_get_carrier_service(self): - self.fake("carrier_services/123456", method='GET', body=self.load_fixture('carrier_service')) - - carrier_service = shopify.CarrierService.find(123456) - self.assertEqual("Some Postal Service", carrier_service.name) diff --git a/test/cart_test.py b/test/cart_test.py deleted file mode 100644 index 424c5b6a..00000000 --- a/test/cart_test.py +++ /dev/null @@ -1,13 +0,0 @@ -import shopify -from test_helper import TestCase - -class CartTest(TestCase): - - def test_all_should_return_all_carts(self): - self.fake('carts') - carts = shopify.Cart.find() - self.assertEqual(2, len(carts)) - self.assertEqual(2, carts[0].id) - self.assertEqual("3eed8183d4281db6ea82ee2b8f23e9cc", carts[0].token) - self.assertEqual(1, len(carts[0].line_items)) - self.assertEqual('test', carts[0].line_items[0].title) diff --git a/test/customer_saved_search_test.py b/test/customer_saved_search_test.py deleted file mode 100644 index 4f5f6e2f..00000000 --- a/test/customer_saved_search_test.py +++ /dev/null @@ -1,23 +0,0 @@ -import shopify -from test_helper import TestCase - -class CustomerSavedSearchTest(TestCase): - - def setUp(self): - super(CustomerSavedSearchTest, self).setUp() - self.load_customer_saved_search() - - def test_get_customers_from_customer_saved_search(self): - self.fake('customer_saved_searches/8899730/customers', body=self.load_fixture('customer_saved_search_customers')) - self.assertEqual(1, len(self.customer_saved_search.customers())) - self.assertEqual(112223902, self.customer_saved_search.customers()[0].id) - - def test_get_customers_from_customer_saved_search_with_params(self): - self.fake('customer_saved_searches/8899730/customers.json?limit=1', extension=False, body=self.load_fixture('customer_saved_search_customers')) - customers = self.customer_saved_search.customers(limit = 1) - self.assertEqual(1, len(customers)) - self.assertEqual(112223902, customers[0].id) - - def load_customer_saved_search(self): - self.fake('customer_saved_searches/8899730', body=self.load_fixture('customer_saved_search')) - self.customer_saved_search = shopify.CustomerSavedSearch.find(8899730) diff --git a/test/fixtures/article.json b/test/fixtures/article.json deleted file mode 100644 index 65ac8c6d..00000000 --- a/test/fixtures/article.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "article": { - "author": "Shopify", - "blog_id": 1008414260, - "body_html": null, - "created_at": "2012-07-06T13:57:28-04:00", - "id": 6242736, - "published_at": "2012-07-06T13:57:28-04:00", - "summary_html": null, - "title": "First Post", - "updated_at": "2012-07-06T13:57:51-04:00", - "user_id": null, - "tags": "consequuntur, cupiditate, repellendus" - } -} \ No newline at end of file diff --git a/test/fixtures/articles.json b/test/fixtures/articles.json deleted file mode 100644 index df83a9cb..00000000 --- a/test/fixtures/articles.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "articles": [{ - "author": "Shopify", - "blog_id": 1008414260, - "body_html": null, - "created_at": "2012-07-06T13:57:28-04:00", - "id": 6242736, - "published_at": "2012-07-06T13:57:28-04:00", - "summary_html": null, - "title": "First Post", - "updated_at": "2012-07-06T13:57:51-04:00", - "user_id": null, - "tags": "consequuntur, cupiditate, repellendus" - }, { - "author": "development shop", - "blog_id": 1008414260, - "body_html": null, - "created_at": "2013-04-21T18:10:35-04:00", - "id": 7739673, - "published_at": "2013-04-21T18:10:22-04:00", - "summary_html": null, - "title": "My second blog post", - "updated_at": "2013-04-21T18:10:35-04:00", - "user_id": 2221540, - "tags": "" - }, { - "author": "development shop", - "blog_id": 1008414260, - "body_html": null, - "created_at": "2013-04-21T18:11:19-04:00", - "id": 7739683, - "published_at": "2013-04-21T18:10:45-04:00", - "summary_html": null, - "title": "50% off sale", - "updated_at": "2013-04-21T18:11:19-04:00", - "user_id": 2221540, - "tags": "" - }] -} \ No newline at end of file diff --git a/test/fixtures/asset.json b/test/fixtures/asset.json deleted file mode 100644 index fe29133a..00000000 --- a/test/fixtures/asset.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "asset": { - "created_at": "2010-07-12T15:31:50-04:00", - "updated_at": "2010-07-12T15:31:50-04:00", - "public_url": null, - "value": "\n

Featured Products

\n\n{% tablerow product in collections.frontpage.products cols:3 %}\n {{ product.featured_image | product_img_url: 'small' | img_tag }}\n

{{product.title}}

\n
    \n
  • {{product.price_min | money}}
  • \n
\n{% endtablerow %}\n
\n\n\n
\n \t{% assign article = pages.frontpage %}\n\n
\n {% if article.content != \"\" %}\n\t\t

{{ article.title }}

\n
\n \t\t {{ article.content }}\n \t\t
\n \t{% else %}\n
\n \t In Admin > Blogs & Pages, create a page with the handle frontpage and it will show up here.
\n \t {{ \"Learn more about handles\" | link_to \"http://wiki.shopify.com/Handle\" }}\n
\n \t{% endif %}\n
\n\n
\n\n", - "key": "templates/index.liquid" - } -} diff --git a/test/fixtures/assets.json b/test/fixtures/assets.json deleted file mode 100644 index 282ecafd..00000000 --- a/test/fixtures/assets.json +++ /dev/null @@ -1,136 +0,0 @@ -{ - "assets": [ - { - "created_at": "2010-07-12T15:31:50-04:00", - "updated_at": "2010-07-12T15:31:50-04:00", - "public_url": "http://static.shopify.com/s/files/1/6909/3384/t/1/assets/bg-body-green.gif?1", - "key": "assets/bg-body-green.gif" - }, - { - "created_at": "2010-07-12T15:31:50-04:00", - "updated_at": "2010-07-12T15:31:50-04:00", - "public_url": "http://static.shopify.com/s/files/1/6909/3384/t/1/assets/bg-body-orange.gif?1", - "key": "assets/bg-body-orange.gif" - }, - { - "created_at": "2010-07-12T15:31:50-04:00", - "updated_at": "2010-07-12T15:31:50-04:00", - "public_url": "http://static.shopify.com/s/files/1/6909/3384/t/1/assets/bg-body-pink.gif?1", - "key": "assets/bg-body-pink.gif" - }, - { - "created_at": "2010-07-12T15:31:50-04:00", - "updated_at": "2010-07-12T15:31:50-04:00", - "public_url": "http://static.shopify.com/s/files/1/6909/3384/t/1/assets/bg-body.gif?1", - "key": "assets/bg-body.gif" - }, - { - "created_at": "2010-07-12T15:31:50-04:00", - "updated_at": "2010-07-12T15:31:50-04:00", - "public_url": "http://static.shopify.com/s/files/1/6909/3384/t/1/assets/bg-content.gif?1", - "key": "assets/bg-content.gif" - }, - { - "created_at": "2010-07-12T15:31:50-04:00", - "updated_at": "2010-07-12T15:31:50-04:00", - "public_url": "http://static.shopify.com/s/files/1/6909/3384/t/1/assets/bg-footer.gif?1", - "key": "assets/bg-footer.gif" - }, - { - "created_at": "2010-07-12T15:31:50-04:00", - "updated_at": "2010-07-12T15:31:50-04:00", - "public_url": "http://static.shopify.com/s/files/1/6909/3384/t/1/assets/bg-main.gif?1", - "key": "assets/bg-main.gif" - }, - { - "created_at": "2010-07-12T15:31:50-04:00", - "updated_at": "2010-07-12T15:31:50-04:00", - "public_url": "http://static.shopify.com/s/files/1/6909/3384/t/1/assets/bg-sidebar.gif?1", - "key": "assets/bg-sidebar.gif" - }, - { - "created_at": "2010-07-12T15:31:50-04:00", - "updated_at": "2010-07-12T15:31:50-04:00", - "public_url": "http://static.shopify.com/s/files/1/6909/3384/t/1/assets/shop.css?1", - "key": "assets/shop.css" - }, - { - "created_at": "2010-07-12T15:31:50-04:00", - "updated_at": "2010-07-12T15:31:50-04:00", - "public_url": "http://static.shopify.com/s/files/1/6909/3384/t/1/assets/shop.css.liquid?1", - "key": "assets/shop.css.liquid" - }, - { - "created_at": "2010-07-12T15:31:50-04:00", - "updated_at": "2010-07-12T15:31:50-04:00", - "public_url": "http://static.shopify.com/s/files/1/6909/3384/t/1/assets/shop.js?1", - "key": "assets/shop.js" - }, - { - "created_at": "2010-07-12T15:31:50-04:00", - "updated_at": "2010-07-12T15:31:50-04:00", - "public_url": "http://static.shopify.com/s/files/1/6909/3384/t/1/assets/sidebar-devider.gif?1", - "key": "assets/sidebar-devider.gif" - }, - { - "created_at": "2010-07-12T15:31:50-04:00", - "updated_at": "2010-07-12T15:31:50-04:00", - "public_url": "http://static.shopify.com/s/files/1/6909/3384/t/1/assets/sidebar-menu.jpg?1", - "key": "assets/sidebar-menu.jpg" - }, - { - "created_at": "2010-07-12T15:31:50-04:00", - "updated_at": "2010-07-12T15:31:50-04:00", - "public_url": null, - "key": "config/settings.html" - }, - { - "created_at": "2010-07-12T15:31:50-04:00", - "updated_at": "2010-07-12T15:31:50-04:00", - "public_url": null, - "key": "layout/theme.liquid" - }, - { - "created_at": "2010-07-12T15:31:50-04:00", - "updated_at": "2010-07-12T15:31:50-04:00", - "public_url": null, - "key": "templates/article.liquid" - }, - { - "created_at": "2010-07-12T15:31:50-04:00", - "updated_at": "2010-07-12T15:31:50-04:00", - "public_url": null, - "key": "templates/blog.liquid" - }, - { - "created_at": "2010-07-12T15:31:50-04:00", - "updated_at": "2010-07-12T15:31:50-04:00", - "public_url": null, - "key": "templates/cart.liquid" - }, - { - "created_at": "2010-07-12T15:31:50-04:00", - "updated_at": "2010-07-12T15:31:50-04:00", - "public_url": null, - "key": "templates/collection.liquid" - }, - { - "created_at": "2010-07-12T15:31:50-04:00", - "updated_at": "2010-07-12T15:31:50-04:00", - "public_url": null, - "key": "templates/index.liquid" - }, - { - "created_at": "2010-07-12T15:31:50-04:00", - "updated_at": "2010-07-12T15:31:50-04:00", - "public_url": null, - "key": "templates/page.liquid" - }, - { - "created_at": "2010-07-12T15:31:50-04:00", - "updated_at": "2010-07-12T15:31:50-04:00", - "public_url": null, - "key": "templates/product.liquid" - } - ] -} diff --git a/test/fixtures/authors.json b/test/fixtures/authors.json deleted file mode 100644 index fc9db30e..00000000 --- a/test/fixtures/authors.json +++ /dev/null @@ -1 +0,0 @@ -{"authors": ["Shopify", "development shop", "development shop"]} diff --git a/test/fixtures/blog.json b/test/fixtures/blog.json deleted file mode 100644 index df94412c..00000000 --- a/test/fixtures/blog.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "blog": { - "handle": "test-blog", - "created_at": "2012-01-10T17:45:19-05:00", - "title": "Test Blog", - "template_suffix": null, - "updated_at": "2012-01-10T17:45:19-05:00", - "feedburner_location": null, - "id": 1008414260, - "feedburner": null, - "commentable": "no" - } -} \ No newline at end of file diff --git a/test/fixtures/blogs.json b/test/fixtures/blogs.json deleted file mode 100644 index 3f779b25..00000000 --- a/test/fixtures/blogs.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "blogs": [{ - "handle": "test-blog", - "created_at": "2012-01-10T17:45:19-05:00", - "title": "Test Blog", - "template_suffix": null, - "updated_at": "2012-01-10T17:45:19-05:00", - "feedburner_location": null, - "id": 1008414260, - "feedburner": null, - "commentable": "no" - }] -} \ No newline at end of file diff --git a/test/fixtures/carrier_service.json b/test/fixtures/carrier_service.json deleted file mode 100644 index b3f3b5f4..00000000 --- a/test/fixtures/carrier_service.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "carrier_service": { - "name": "Some Postal Service", - "id": 123456, - "callback_url": "http://google.com", - "format": "json", - "service_discovery": true - } -} diff --git a/test/fixtures/carts.json b/test/fixtures/carts.json deleted file mode 100644 index 64d51246..00000000 --- a/test/fixtures/carts.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "carts": [ - { - "id": 2, - "note": null, - "token": "3eed8183d4281db6ea82ee2b8f23e9cc", - "updated_at": "2012-02-13T14:39:37-05:00", - "line_items": - [ - { - "id": 1, - "title": "test", - "price": "1.00", - "line_price": "1.00", - "quantity": 1, - "sku": "", - "grams": 1000, - "vendor": "test", - "variant_id": 1 - } - ] - }, - { - "id": 1, - "note": "", - "token": "49801807939c296be1e9a4bf6783a705", - "updated_at": "2012-02-13T14:39:12-05:00", - "line_items":[ - { - "id": 1, - "title": "test", - "price": "1.00", - "line_price": "1.00", - "quantity": 1, - "sku": "", - "grams": 1000, - "vendor": "test", - "variant_id": 1 - } - ] - } - ] -} \ No newline at end of file diff --git a/test/fixtures/customer_saved_search.json b/test/fixtures/customer_saved_search.json deleted file mode 100644 index c8b0cbaa..00000000 --- a/test/fixtures/customer_saved_search.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "customer_saved_search": { - "created_at": "2013-01-21T13:26:12-05:00", - "id": 8899730, - "name": "Accepts Marketing", - "updated_at": "2013-01-21T13:26:12-05:00", - "query": "accepts_marketing:1" - } -} diff --git a/test/fixtures/customer_saved_search_customers.json b/test/fixtures/customer_saved_search_customers.json deleted file mode 100644 index ca07c424..00000000 --- a/test/fixtures/customer_saved_search_customers.json +++ /dev/null @@ -1,60 +0,0 @@ -{ - "customers": [ - { - "accepts_marketing": true, - "created_at": "2013-01-29T16:33:35-05:00", - "email": "john.smith@gmail.com", - "first_name": "John", - "id": 112223902, - "last_name": "Smith", - "last_order_id": null, - "multipass_identifier": null, - "note": "This is sample note", - "orders_count": 0, - "state": "disabled", - "total_spent": "0.00", - "updated_at": "2013-03-05T17:00:05-05:00", - "verified_email": true, - "tags": "Buyer, Canadian", - "last_order_name": null, - "default_address": { - "address1": "1234 Simple Road", - "address2": null, - "city": "Ottawa", - "company": null, - "country": "Canada", - "first_name": "John", - "id": 152583148, - "last_name": "Smith", - "phone": "555-555-5555", - "province": "Ontario", - "zip": "K2H 0A0", - "name": "John Smith", - "province_code": "ON", - "country_code": "CA", - "country_name": "Canada", - "default": true - }, - "addresses": [ - { - "address1": "1234 Simple Road", - "address2": null, - "city": "Ottawa", - "company": null, - "country": "Canada", - "first_name": "John", - "id": 152583148, - "last_name": "Smith", - "phone": "555-555-5555", - "province": "Ontario", - "zip": "K2H 0A0", - "name": "John Smith", - "province_code": "ON", - "country_code": "CA", - "country_name": "Canada", - "default": true - } - ] - } - ] -} diff --git a/test/fixtures/events.json b/test/fixtures/events.json deleted file mode 100644 index 24561f50..00000000 --- a/test/fixtures/events.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "events": [ - { - "verb": "placed", - "created_at": "2008-01-10T11:00:00-05:00", - "body": null, - "subject_id": 450789469, - "id": 852065041, - "subject_type": "Order", - "message": "Order was placed" - }, - { - "verb": "confirmed", - "created_at": "2008-01-10T11:00:00-05:00", - "body": null, - "subject_id": 450789469, - "id": 164748010, - "subject_type": "Order", - "message": "Received new order #1001 by Bob Norman" - }, - { - "verb": "authorization_success", - "created_at": "2008-01-10T11:00:00-05:00", - "body": null, - "subject_id": 450789469, - "id": 103105390, - "subject_type": "Order", - "message": "The customer successfully authorized us to capture 210.94 USD" - } - ] -} diff --git a/test/fixtures/fulfillment.json b/test/fixtures/fulfillment.json deleted file mode 100644 index 9d7e04d3..00000000 --- a/test/fixtures/fulfillment.json +++ /dev/null @@ -1,49 +0,0 @@ -{ - "fulfillment": { - "created_at": "2013-11-01T16:06:08-04:00", - "id": 255858046, - "order_id": 450789469, - "service": "manual", - "status": "pending", - "tracking_company": null, - "updated_at": "2013-11-01T16:06:08-04:00", - "tracking_number": "1Z2345", - "tracking_numbers": [ - "1Z2345" - ], - "tracking_url": "http://www.google.com/search?q=1Z2345", - "tracking_urls": [ - "http://www.google.com/search?q=1Z2345" - ], - "receipt": { - "testcase": true, - "authorization": "123456" - }, - "line_items": [ - { - "fulfillment_service": "manual", - "fulfillment_status": null, - "grams": 200, - "id": 466157049, - "price": "199.00", - "product_id": 632910392, - "quantity": 1, - "requires_shipping": true, - "sku": "IPOD2008GREEN", - "title": "IPod Nano - 8gb", - "variant_id": 39072856, - "variant_title": "green", - "vendor": null, - "name": "IPod Nano - 8gb - green", - "variant_inventory_management": "shopify", - "properties": [ - { - "name": "Custom Engraving", - "value": "Happy Birthday" - } - ], - "product_exists": true - } - ] - } -} \ No newline at end of file diff --git a/test/fixtures/fulfillment_service.json b/test/fixtures/fulfillment_service.json deleted file mode 100644 index ae6e9c10..00000000 --- a/test/fixtures/fulfillment_service.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "fulfillment_service": { - "name": "SomeService", - "id": 123456, - "inventory_management": false, - "tracking_support": true, - "requires_shipping_method": false, - "format": "json" - } -} diff --git a/test/fixtures/image.json b/test/fixtures/image.json deleted file mode 100644 index 8da79add..00000000 --- a/test/fixtures/image.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "image": { - "created_at": "2014-01-10T16:15:40-05:00", - "id": 850703190, - "position": 1, - "product_id": 632910392, - "updated_at": "2014-01-10T16:15:40-05:00", - "src": "http://cdn.shopify.com/s/files/1/0006/9093/3842/products/ipod-nano.png?v=1389388540" - } -} diff --git a/test/fixtures/images.json b/test/fixtures/images.json deleted file mode 100644 index 57ff16c8..00000000 --- a/test/fixtures/images.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "images": [ - { - "created_at": "2014-01-17T16:11:52-05:00", - "id": 850703190, - "position": 1, - "product_id": 632910392, - "updated_at": "2014-01-17T16:11:52-05:00", - "src": "http://cdn.shopify.com/s/files/1/0006/9093/3842/products/ipod-nano.png?v=1389993112" - }, - { - "created_at": "2014-01-17T16:11:52-05:00", - "id": 562641783, - "position": 2, - "product_id": 632910392, - "updated_at": "2014-01-17T16:11:52-05:00", - "src": "http://cdn.shopify.com/s/files/1/0006/9093/3842/products/ipod-nano-2.png?v=1389993112" - } - ] -} diff --git a/test/fixtures/metafield.json b/test/fixtures/metafield.json deleted file mode 100644 index b096d9df..00000000 --- a/test/fixtures/metafield.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "metafields": { - "created_at": "2011-10-20T14:05:13-04:00", - "updated_at": "2011-10-20T14:05:13-04:00", - "namespace": "contact", - "id": 721389482, - "value": "123@example.com", - "description": null, - "key": "email", - "value_type": "string" - } -} diff --git a/test/fixtures/metafields.json b/test/fixtures/metafields.json deleted file mode 100644 index 78995109..00000000 --- a/test/fixtures/metafields.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "metafields": [ - { - "created_at": "2011-10-20T14:05:13-04:00", - "updated_at": "2011-10-20T14:05:13-04:00", - "namespace": "affiliates", - "id": 721389482, - "value": "app_key", - "description": null, - "key": "app_key", - "value_type": "string" - }, - { - "created_at": "2011-10-20T14:05:13-04:00", - "updated_at": "2011-10-20T14:05:13-04:00", - "namespace": "contact", - "id": 721389480, - "value": "1231231231", - "description": null, - "key": "phone", - "value_type": "string" - } - ] -} diff --git a/test/fixtures/order.json b/test/fixtures/order.json deleted file mode 100644 index 967b5498..00000000 --- a/test/fixtures/order.json +++ /dev/null @@ -1,275 +0,0 @@ -{ - "order": { - "buyer_accepts_marketing": false, - "cancel_reason": null, - "cancelled_at": null, - "cart_token": "68778783ad298f1c80c3bafcddeea02f", - "checkout_token": null, - "closed_at": null, - "confirmed": false, - "created_at": "2008-01-10T11:00:00-05:00", - "currency": "USD", - "email": "bob.norman@hostmail.com", - "financial_status": "authorized", - "fulfillment_status": null, - "gateway": "authorize_net", - "id": 450789469, - "landing_site": "http://www.example.com?source=abc", - "location_id": null, - "name": "#1001", - "note": null, - "number": 1, - "reference": "fhwdgads", - "referring_site": "http://www.otherexample.com", - "source": null, - "subtotal_price": "398.00", - "taxes_included": false, - "test": false, - "token": "b1946ac92492d2347c6235b4d2611184", - "total_discounts": "0.00", - "total_line_items_price": "398.00", - "total_price": "409.94", - "total_price_usd": "409.94", - "total_tax": "11.94", - "total_weight": 0, - "updated_at": "2008-01-10T11:00:00-05:00", - "user_id": null, - "browser_ip": null, - "landing_site_ref": "abc", - "order_number": 1001, - "discount_codes": [ - { - "code": "TENOFF", - "amount": "10.00" - } - ], - "note_attributes": [ - { - "name": "custom engraving", - "value": "Happy Birthday" - }, - { - "name": "colour", - "value": "green" - } - ], - "processing_method": "direct", - "checkout_id": 450789469, - "source_name": "web", - "tax_lines": [ - { - "price": "11.94", - "rate": 0.06, - "title": "State Tax" - } - ], - "line_items": [ - { - "fulfillment_service": "manual", - "fulfillment_status": null, - "grams": 200, - "id": 466157049, - "price": "199.00", - "product_id": 632910392, - "quantity": 1, - "requires_shipping": true, - "sku": "IPOD2008GREEN", - "title": "IPod Nano - 8gb", - "variant_id": 39072856, - "variant_title": "green", - "vendor": null, - "name": "IPod Nano - 8gb - green", - "variant_inventory_management": "shopify", - "properties": [ - { - "name": "Custom Engraving", - "value": "Happy Birthday" - } - ], - "product_exists": true - }, - { - "fulfillment_service": "manual", - "fulfillment_status": null, - "grams": 200, - "id": 518995019, - "price": "199.00", - "product_id": 632910392, - "quantity": 1, - "requires_shipping": true, - "sku": "IPOD2008RED", - "title": "IPod Nano - 8gb", - "variant_id": 49148385, - "variant_title": "red", - "vendor": null, - "name": "IPod Nano - 8gb - red", - "variant_inventory_management": "shopify", - "properties": [ - - ], - "product_exists": true - }, - { - "fulfillment_service": "manual", - "fulfillment_status": null, - "grams": 200, - "id": 703073504, - "price": "199.00", - "product_id": 632910392, - "quantity": 1, - "requires_shipping": true, - "sku": "IPOD2008BLACK", - "title": "IPod Nano - 8gb", - "variant_id": 457924702, - "variant_title": "black", - "vendor": null, - "name": "IPod Nano - 8gb - black", - "variant_inventory_management": "shopify", - "properties": [ - - ], - "product_exists": true - } - ], - "shipping_lines": [ - { - "code": "Free Shipping", - "price": "0.00", - "source": "shopify", - "title": "Free Shipping" - } - ], - "payment_details": { - "avs_result_code": null, - "credit_card_bin": null, - "cvv_result_code": null, - "credit_card_number": "XXXX-XXXX-XXXX-4242", - "credit_card_company": "Visa" - }, - "billing_address": { - "address1": "Chestnut Street 92", - "address2": "", - "city": "Louisville", - "company": null, - "country": "United States", - "first_name": "Bob", - "last_name": "Norman", - "latitude": "45.41634", - "longitude": "-75.6868", - "phone": "555-625-1199", - "province": "Kentucky", - "zip": "40202", - "name": "Bob Norman", - "country_code": "US", - "province_code": "KY" - }, - "shipping_address": { - "address1": "Chestnut Street 92", - "address2": "", - "city": "Louisville", - "company": null, - "country": "United States", - "first_name": "Bob", - "last_name": "Norman", - "latitude": "45.41634", - "longitude": "-75.6868", - "phone": "555-625-1199", - "province": "Kentucky", - "zip": "40202", - "name": "Bob Norman", - "country_code": "US", - "province_code": "KY" - }, - "fulfillments": [ - { - "created_at": "2014-01-22T15:58:27-05:00", - "id": 255858046, - "order_id": 450789469, - "service": "manual", - "status": "failure", - "tracking_company": null, - "updated_at": "2014-01-22T15:58:27-05:00", - "tracking_number": "1Z2345", - "tracking_numbers": [ - "1Z2345" - ], - "tracking_url": "http://wwwapps.ups.com/etracking/tracking.cgi?InquiryNumber1=1Z2345&TypeOfInquiryNumber=T&AcceptUPSLicenseAgreement=yes&submit=Track", - "tracking_urls": [ - "http://wwwapps.ups.com/etracking/tracking.cgi?InquiryNumber1=1Z2345&TypeOfInquiryNumber=T&AcceptUPSLicenseAgreement=yes&submit=Track" - ], - "receipt": { - "testcase": true, - "authorization": "123456" - }, - "line_items": [ - { - "fulfillment_service": "manual", - "fulfillment_status": null, - "grams": 200, - "id": 466157049, - "price": "199.00", - "product_id": 632910392, - "quantity": 1, - "requires_shipping": true, - "sku": "IPOD2008GREEN", - "title": "IPod Nano - 8gb", - "variant_id": 39072856, - "variant_title": "green", - "vendor": null, - "name": "IPod Nano - 8gb - green", - "variant_inventory_management": "shopify", - "properties": [ - { - "name": "Custom Engraving", - "value": "Happy Birthday" - } - ], - "product_exists": true - } - ] - } - ], - "client_details": { - "accept_language": null, - "browser_ip": "0.0.0.0", - "session_hash": null, - "user_agent": null - }, - "customer": { - "accepts_marketing": false, - "created_at": "2014-01-22T15:58:27-05:00", - "email": "bob.norman@hostmail.com", - "first_name": "Bob", - "id": 207119551, - "last_name": "Norman", - "last_order_id": null, - "multipass_identifier": null, - "note": null, - "orders_count": 0, - "state": "disabled", - "total_spent": "0.00", - "updated_at": "2014-01-22T15:58:27-05:00", - "verified_email": true, - "tags": "", - "last_order_name": null, - "default_address": { - "address1": "Chestnut Street 92", - "address2": "", - "city": "Louisville", - "company": null, - "country": "United States", - "first_name": null, - "id": 207119551, - "last_name": null, - "phone": "555-625-1199", - "province": "Kentucky", - "zip": "40202", - "name": null, - "province_code": "KY", - "country_code": "US", - "country_name": "United States", - "default": true - } - } - } -} diff --git a/test/fixtures/order_risk.json b/test/fixtures/order_risk.json deleted file mode 100644 index 3e5118a3..00000000 --- a/test/fixtures/order_risk.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "risk": { - "cause_cancel": true, - "checkout_id": null, - "display": true, - "id": 284138680, - "message": "This order was placed from a proxy IP", - "order_id": 450789469, - "recommendation": "cancel", - "score": "1.0", - "source": "External", - "merchant_message": "This order was placed from a proxy IP" - } -} diff --git a/test/fixtures/order_risks.json b/test/fixtures/order_risks.json deleted file mode 100644 index 7ecd2dcd..00000000 --- a/test/fixtures/order_risks.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "risks": [ - { - "cause_cancel": true, - "checkout_id": null, - "display": true, - "id": 284138680, - "message": "This order was placed from a proxy IP", - "order_id": 450789469, - "recommendation": "cancel", - "score": "1.0", - "source": "External", - "merchant_message": "This order was placed from a proxy IP" - }, - { - "cause_cancel": true, - "checkout_id": null, - "display": true, - "id": 432527878, - "message": "This order came from an anonymous proxy", - "order_id": 450789469, - "recommendation": "cancel", - "score": "1.0", - "source": "External", - "merchant_message": "This order came from an anonymous proxy" - } - ] -} diff --git a/test/fixtures/product.json b/test/fixtures/product.json deleted file mode 100644 index 52dadedd..00000000 --- a/test/fixtures/product.json +++ /dev/null @@ -1,116 +0,0 @@ -{ - "product": { - "product_type": "Cult Products", - "handle": "ipod-nano", - "created_at": "2011-10-20T14:05:13-04:00", - "body_html": "

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.

", - "title": "IPod Nano - 8GB", - "template_suffix": null, - "updated_at": "2011-10-20T14:05:13-04:00", - "id": 632910392, - "tags": "Emotive, Flash Memory, MP3, Music", - "images": [ - { - "position": 1, - "created_at": "2011-10-20T14:05:13-04:00", - "product_id": 632910392, - "updated_at": "2011-10-20T14:05:13-04:00", - "src": "http://static.shopify.com/s/files/1/6909/3384/products/ipod-nano.png?0", - "id": 850703190 - } - ], - "variants": [ - { - "position": 1, - "price": "199.00", - "product_id": 632910392, - "created_at": "2011-10-20T14:05:13-04:00", - "requires_shipping": true, - "title": "Pink", - "inventory_quantity": 10, - "compare_at_price": null, - "inventory_policy": "continue", - "updated_at": "2011-10-20T14:05:13-04:00", - "inventory_management": "shopify", - "id": 808950810, - "taxable": true, - "grams": 200, - "sku": "IPOD2008PINK", - "option1": "Pink", - "fulfillment_service": "manual", - "option2": null, - "option3": null - }, - { - "position": 2, - "price": "199.00", - "product_id": 632910392, - "created_at": "2011-10-20T14:05:13-04:00", - "requires_shipping": true, - "title": "Red", - "inventory_quantity": 20, - "compare_at_price": null, - "inventory_policy": "continue", - "updated_at": "2011-10-20T14:05:13-04:00", - "inventory_management": "shopify", - "id": 49148385, - "taxable": true, - "grams": 200, - "sku": "IPOD2008RED", - "option1": "Red", - "fulfillment_service": "manual", - "option2": null, - "option3": null - }, - { - "position": 3, - "price": "199.00", - "product_id": 632910392, - "created_at": "2011-10-20T14:05:13-04:00", - "requires_shipping": true, - "title": "Green", - "inventory_quantity": 30, - "compare_at_price": null, - "inventory_policy": "continue", - "updated_at": "2011-10-20T14:05:13-04:00", - "inventory_management": "shopify", - "id": 39072856, - "taxable": true, - "grams": 200, - "sku": "IPOD2008GREEN", - "option1": "Green", - "fulfillment_service": "manual", - "option2": null, - "option3": null - }, - { - "position": 4, - "price": "199.00", - "product_id": 632910392, - "created_at": "2011-10-20T14:05:13-04:00", - "requires_shipping": true, - "title": "Black", - "inventory_quantity": 40, - "compare_at_price": null, - "inventory_policy": "continue", - "updated_at": "2011-10-20T14:05:13-04:00", - "inventory_management": "shopify", - "id": 457924702, - "taxable": true, - "grams": 200, - "sku": "IPOD2008BLACK", - "option1": "Black", - "fulfillment_service": "manual", - "option2": null, - "option3": null - } - ], - "vendor": "Apple", - "published_at": "2007-12-31T19:00:00-05:00", - "options": [ - { - "name": "Title" - } - ] - } -} diff --git a/test/fixtures/shop.json b/test/fixtures/shop.json deleted file mode 100644 index ee925166..00000000 --- a/test/fixtures/shop.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "shop": { - "name": "Apple Computers", - "city": "Cupertino", - "address1": "1 Infinite Loop", - "zip": "95014", - "created_at": "2007-12-31T19:00:00-05:00", - "shop_owner": "Steve Jobs", - "plan_name": "enterprise", - "public": false, - "country": "US", - "money_with_currency_format": "$ {{amount}} USD", - "money_format": "$ {{amount}}", - "domain": "shop.apple.com", - "taxes_included": null, - "id": 690933842, - "timezone": "(GMT-05:00) Eastern Time (US & Canada)", - "tax_shipping": null, - "phone": null, - "currency": "USD", - "myshopify_domain": "apple.myshopify.com", - "source": null, - "province": "CA", - "email": "steve@apple.com" - } -} diff --git a/test/fixtures/tags.json b/test/fixtures/tags.json deleted file mode 100644 index 876652ef..00000000 --- a/test/fixtures/tags.json +++ /dev/null @@ -1 +0,0 @@ -{"tags": ["consequuntur", "cupiditate", "repellendus"]} diff --git a/test/fixtures/transaction.json b/test/fixtures/transaction.json deleted file mode 100644 index 33a70e9d..00000000 --- a/test/fixtures/transaction.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "transaction": { - "amount": "409.94", - "authorization": "authorization-key", - "created_at": "2005-08-01T11:57:11-04:00", - "gateway": "bogus", - "id": 389404469, - "kind": "authorization", - "location_id": null, - "message": null, - "order_id": 450789469, - "parent_id": null, - "status": "success", - "test": false, - "user_id": null, - "device_id": null, - "receipt": { - "testcase": true, - "authorization": "123456" - }, - "payment_details": { - "avs_result_code": null, - "credit_card_bin": null, - "cvv_result_code": null, - "credit_card_number": "XXXX-XXXX-XXXX-4242", - "credit_card_company": "Visa" - } - } -} \ No newline at end of file diff --git a/test/fixtures/variant.json b/test/fixtures/variant.json deleted file mode 100644 index 173d7c33..00000000 --- a/test/fixtures/variant.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "variant": { - "price": "199.00", - "position": 1, - "created_at": "2011-10-20T14:05:13-04:00", - "title": "Pink", - "requires_shipping": true, - "updated_at": "2011-10-20T14:05:13-04:00", - "inventory_policy": "continue", - "compare_at_price": null, - "inventory_quantity": 10, - "inventory_management": "shopify", - "taxable": true, - "id": 808950810, - "grams": 200, - "sku": "IPOD2008PINK", - "option1": "Pink", - "option2": null, - "fulfillment_service": "manual", - "option3": null - } -} diff --git a/test/fixtures/variants.json b/test/fixtures/variants.json deleted file mode 100644 index 1128180a..00000000 --- a/test/fixtures/variants.json +++ /dev/null @@ -1,84 +0,0 @@ -{ - "variants": [ - { - "price": "199.00", - "position": 1, - "created_at": "2011-10-20T14:05:13-04:00", - "title": "Pink", - "requires_shipping": true, - "updated_at": "2011-10-20T14:05:13-04:00", - "inventory_policy": "continue", - "compare_at_price": null, - "inventory_quantity": 10, - "inventory_management": "shopify", - "taxable": true, - "id": 808950810, - "grams": 200, - "sku": "IPOD2008PINK", - "option1": "Pink", - "option2": null, - "fulfillment_service": "manual", - "option3": null - }, - { - "price": "199.00", - "position": 2, - "created_at": "2011-10-20T14:05:13-04:00", - "title": "Red", - "requires_shipping": true, - "updated_at": "2011-10-20T14:05:13-04:00", - "inventory_policy": "continue", - "compare_at_price": null, - "inventory_quantity": 20, - "inventory_management": "shopify", - "taxable": true, - "id": 49148385, - "grams": 200, - "sku": "IPOD2008RED", - "option1": "Red", - "option2": null, - "fulfillment_service": "manual", - "option3": null - }, - { - "price": "199.00", - "position": 3, - "created_at": "2011-10-20T14:05:13-04:00", - "title": "Green", - "requires_shipping": true, - "updated_at": "2011-10-20T14:05:13-04:00", - "inventory_policy": "continue", - "compare_at_price": null, - "inventory_quantity": 30, - "inventory_management": "shopify", - "taxable": true, - "id": 39072856, - "grams": 200, - "sku": "IPOD2008GREEN", - "option1": "Green", - "option2": null, - "fulfillment_service": "manual", - "option3": null - }, - { - "price": "199.00", - "position": 4, - "created_at": "2011-10-20T14:05:13-04:00", - "title": "Black", - "requires_shipping": true, - "updated_at": "2011-10-20T14:05:13-04:00", - "inventory_policy": "continue", - "compare_at_price": null, - "inventory_quantity": 40, - "inventory_management": "shopify", - "taxable": true, - "id": 457924702, - "grams": 200, - "sku": "IPOD2008BLACK", - "option1": "Black", - "option2": null, - "fulfillment_service": "manual", - "option3": null - } - ] -} diff --git a/test/fulfillment_service_test.py b/test/fulfillment_service_test.py deleted file mode 100644 index 12bb42bf..00000000 --- a/test/fulfillment_service_test.py +++ /dev/null @@ -1,15 +0,0 @@ -import shopify -from test_helper import TestCase - -class FulfillmentServiceTest(TestCase): - def test_create_new_fulfillment_service(self): - self.fake("fulfillment_services", method='POST', body=self.load_fixture('fulfillment_service'), headers={'Content-type': 'application/json'}) - - fulfillment_service = shopify.FulfillmentService.create({'name': "SomeService"}) - self.assertEqual("SomeService", fulfillment_service.name) - - def test_get_fulfillment_service(self): - self.fake("fulfillment_services/123456", method='GET', body=self.load_fixture('fulfillment_service')) - - fulfillment_service = shopify.FulfillmentService.find(123456) - self.assertEqual("SomeService", fulfillment_service.name) diff --git a/test/fulfillment_test.py b/test/fulfillment_test.py deleted file mode 100644 index 91c6bed6..00000000 --- a/test/fulfillment_test.py +++ /dev/null @@ -1,31 +0,0 @@ -import shopify -from test_helper import TestCase -from pyactiveresource.activeresource import ActiveResource - -class FulFillmentTest(TestCase): - - def setUp(self): - super(FulFillmentTest, self).setUp() - self.fake("orders/450789469/fulfillments/255858046", method='GET', body=self.load_fixture('fulfillment')) - - def test_able_to_complete_fulfillment(self): - fulfillment = shopify.Fulfillment.find(255858046, order_id=450789469) - - success = self.load_fixture('fulfillment') - success = success.replace('pending','success') - self.fake("orders/450789469/fulfillments/255858046/complete", method='POST', headers={'Content-length':'0', 'Content-type': 'application/json'}, body=success) - - self.assertEqual('pending', fulfillment.status) - fulfillment.complete() - self.assertEqual('success', fulfillment.status) - - def test_able_to_cancel_fulfillment(self): - fulfillment = shopify.Fulfillment.find(255858046, order_id=450789469) - - cancelled = self.load_fixture('fulfillment') - cancelled = cancelled.replace('pending', 'cancelled') - self.fake("orders/450789469/fulfillments/255858046/cancel", method='POST', headers={'Content-length':'0', 'Content-type': 'application/json'}, body=cancelled) - - self.assertEqual('pending', fulfillment.status) - fulfillment.cancel() - self.assertEqual('cancelled', fulfillment.status) diff --git a/test/image_test.py b/test/image_test.py deleted file mode 100644 index 92aabfa0..00000000 --- a/test/image_test.py +++ /dev/null @@ -1,24 +0,0 @@ -import shopify -from test_helper import TestCase - -class ImageTest(TestCase): - - def test_create_image(self): - self.fake("products/632910392/images", method='POST', body=self.load_fixture('image'), headers={'Content-type': 'application/json'}) - image = shopify.Image({'product_id':632910392}) - image.position = 1 - image.attachment = "R0lGODlhbgCMAPf/APbr48VySrxTO7IgKt2qmKQdJeK8lsFjROG5p/nz7Zg3MNmnd7Q1MLNVS9GId71hSJMZIuzTu4UtKbeEeakhKMl8U8WYjfr18YQaIbAf==" - image.save() - - self.assertEqual('http://cdn.shopify.com/s/files/1/0006/9093/3842/products/ipod-nano.png?v=1389388540', image.src) - self.assertEqual(850703190, image.id) - - def test_get_images(self): - self.fake("products/632910392/images", method='GET', body=self.load_fixture('images')) - image = shopify.Image.find(product_id=632910392) - self.assertEqual(2, len(image)) - - def test_get_image(self): - self.fake("products/632910392/images/850703190", method='GET', body=self.load_fixture('image')) - image = shopify.Image.find(850703190, product_id=632910392) - self.assertEqual(850703190, image.id) diff --git a/test/order_risk_test.py b/test/order_risk_test.py deleted file mode 100644 index ad790105..00000000 --- a/test/order_risk_test.py +++ /dev/null @@ -1,42 +0,0 @@ -import shopify -from test_helper import TestCase - -class OrderRiskTest(TestCase): - - def test_create_order_risk(self): - self.fake("orders/450789469/risks", method='POST', body= self.load_fixture('order_risk'), headers={'Content-type': 'application/json'}) - v = shopify.OrderRisk({'order_id':450789469}) - v.message = "This order was placed from a proxy IP" - v.recommendation = "cancel" - v.score = "1.0" - v.source = "External" - v.merchant_message = "This order was placed from a proxy IP" - v.display = True - v.cause_cancel = True - v.save() - - self.assertEqual(284138680, v.id) - - def test_get_order_risks(self): - self.fake("orders/450789469/risks", method='GET', body= self.load_fixture('order_risks')) - v = shopify.OrderRisk.find(order_id=450789469) - self.assertEqual(2, len(v)) - - def test_get_order_risk(self): - self.fake("orders/450789469/risks/284138680", method='GET', body= self.load_fixture('order_risk')) - v = shopify.OrderRisk.find(284138680, order_id=450789469) - self.assertEqual(284138680, v.id) - - def test_delete_order_risk(self): - self.fake("orders/450789469/risks/284138680", method='GET', body= self.load_fixture('order_risk')) - self.fake("orders/450789469/risks/284138680", method='DELETE', body="destroyed") - v = shopify.OrderRisk.find(284138680, order_id=450789469) - v.destroy() - - def test_delete_order_risk(self): - self.fake("orders/450789469/risks/284138680", method='GET', body= self.load_fixture('order_risk')) - self.fake("orders/450789469/risks/284138680", method='PUT', body= self.load_fixture('order_risk'), headers={'Content-type': 'application/json'}) - - v = shopify.OrderRisk.find(284138680, order_id=450789469) - v.position = 3 - v.save() diff --git a/test/order_test.py b/test/order_test.py deleted file mode 100644 index 8b7bca96..00000000 --- a/test/order_test.py +++ /dev/null @@ -1,49 +0,0 @@ -import shopify -from test_helper import TestCase -from pyactiveresource.activeresource import ActiveResource -from pyactiveresource.util import xml_to_dict - -class OrderTest(TestCase): - - def test_should_be_loaded_correctly_from_order_xml(self): - order_xml = """ - - - - size - large - - - """ - order = shopify.Order(xml_to_dict(order_xml)["order"]) - - self.assertEqual(1, len(order.note_attributes)) - - note_attribute = order.note_attributes[0] - self.assertEqual("size", note_attribute.name) - self.assertEqual("large", note_attribute.value) - - def test_should_be_able_to_add_note_attributes_to_an_order(self): - order = shopify.Order() - order.note_attributes = [] - order.note_attributes.append(shopify.NoteAttribute({'name': "color", 'value': "blue"})) - - order_xml = xml_to_dict(order.to_xml()) - note_attributes = order_xml["order"]["note_attributes"] - self.assertTrue(isinstance(note_attributes, list)) - - attribute = note_attributes[0] - self.assertEqual("color", attribute["name"]) - self.assertEqual("blue", attribute["value"]) - - def test_get_order(self): - self.fake('orders/450789469', method='GET', body=self.load_fixture('order')) - order = shopify.Order.find(450789469) - self.assertEqual('bob.norman@hostmail.com', order.email) - - def test_get_order_transaction(self): - self.fake('orders/450789469', method='GET', body=self.load_fixture('order')) - order = shopify.Order.find(450789469) - self.fake('orders/450789469/transactions', method='GET', body=self.load_fixture('transaction')) - transactions = order.transactions() - self.assertEqual("409.94", transactions[0].amount) diff --git a/test/product_test.py b/test/product_test.py deleted file mode 100644 index 89bfbb32..00000000 --- a/test/product_test.py +++ /dev/null @@ -1,42 +0,0 @@ -import shopify -from test_helper import TestCase - -class ProductTest(TestCase): - - def setUp(self): - super(ProductTest, self).setUp() - - self.fake("products/632910392", body=self.load_fixture('product')) - self.product = shopify.Product.find(632910392) - - def test_add_metafields_to_product(self): - self.fake("products/632910392/metafields", method='POST', code=201, body=self.load_fixture('metafield'), headers={'Content-type': 'application/json'}) - - field = self.product.add_metafield(shopify.Metafield({'namespace': "contact", 'key': "email", 'value': "123@example.com", 'value_type': "string"})) - - self.assertFalse(field.is_new()) - self.assertEqual("contact", field.namespace) - self.assertEqual("email", field.key) - self.assertEqual("123@example.com", field.value) - - def test_get_metafields_for_product(self): - self.fake("products/632910392/metafields", body=self.load_fixture('metafields')) - - metafields = self.product.metafields() - - self.assertEqual(2, len(metafields)) - for field in metafields: - self.assertTrue(isinstance(field, shopify.Metafield)) - - def test_update_loaded_variant(self): - self.fake("products/632910392/variants/808950810", method='PUT', code=200, body=self.load_fixture('variant')) - - variant = self.product.variants[0] - variant.price = "0.50" - variant.save - - def test_add_variant_to_product(self): - self.fake("products/632910392/variants", method='POST', body=self.load_fixture('variant'), headers={'Content-type': 'application/json'}) - self.fake("products/632910392/variants/808950810", method='PUT', code=200, body=self.load_fixture('variant'), headers={'Content-type': 'application/json'}) - v = shopify.Variant() - self.assertTrue(self.product.add_variant(v)) diff --git a/test/recurring_charge_test.py b/test/recurring_charge_test.py deleted file mode 100644 index d453c548..00000000 --- a/test/recurring_charge_test.py +++ /dev/null @@ -1,9 +0,0 @@ -import shopify -from test_helper import TestCase - -class RecurringApplicationChargeTest(TestCase): - def test_activate_charge(self): - # Just check that calling activate doesn't raise an exception. - self.fake("recurring_application_charges/35463/activate", method='POST',headers={'Content-length':'0', 'Content-type': 'application/json'}, body=" ") - charge = shopify.RecurringApplicationCharge({'id': 35463}) - charge.activate() diff --git a/test/session_test.py b/test/session_test.py deleted file mode 100644 index 23c51866..00000000 --- a/test/session_test.py +++ /dev/null @@ -1,158 +0,0 @@ -import shopify -from test_helper import TestCase -try: - from hashlib import md5 -except ImportError: - from md5 import md5 -import time - -class SessionTest(TestCase): - - def test_not_be_valid_without_a_url(self): - session = shopify.Session("", "any-token") - self.assertFalse(session.valid) - - def test_not_be_valid_without_token(self): - session = shopify.Session("testshop.myshopify.com") - self.assertFalse(session.valid) - - def test_be_valid_with_any_token_and_any_url(self): - session = shopify.Session("testshop.myshopify.com", "any-token") - self.assertTrue(session.valid) - - def test_not_raise_error_without_params(self): - session = shopify.Session("testshop.myshopify.com", "any-token") - - def test_raise_error_if_params_passed_but_signature_omitted(self): - with self.assertRaises(shopify.ValidationException): - session = shopify.Session("testshop.myshopify.com") - token = session.request_token({'code':'any_code', 'foo': 'bar', 'timestamp':'1234'}) - - def test_setup_api_key_and_secret_for_all_sessions(self): - shopify.Session.setup(api_key="My test key", secret="My test secret") - self.assertEqual("My test key", shopify.Session.api_key) - self.assertEqual("My test secret", shopify.Session.secret) - - def test_use_https_protocol_by_default_for_all_sessions(self): - self.assertEqual('https', shopify.Session.protocol) - - def test_temp_reset_shopify_ShopifyResource_site_to_original_value(self): - shopify.Session.setup(api_key="key", secret="secret") - session1 = shopify.Session('fakeshop.myshopify.com', 'token1') - shopify.ShopifyResource.activate_session(session1) - - assigned_site = "" - with shopify.Session.temp("testshop.myshopify.com", "any-token"): - assigned_site = shopify.ShopifyResource.site - - self.assertEqual('https://testshop.myshopify.com/admin', assigned_site) - self.assertEqual('https://fakeshop.myshopify.com/admin', shopify.ShopifyResource.site) - - def test_temp_reset_shopify_ShopifyResource_site_to_original_value_when_using_a_non_standard_port(self): - shopify.Session.setup(api_key="key", secret="secret") - session1 = shopify.Session('fakeshop.myshopify.com:3000', 'token1') - shopify.ShopifyResource.activate_session(session1) - - assigned_site = "" - with shopify.Session.temp("testshop.myshopify.com", "any-token"): - assigned_site = shopify.ShopifyResource.site - - self.assertEqual('https://testshop.myshopify.com/admin', assigned_site) - self.assertEqual('https://fakeshop.myshopify.com:3000/admin', shopify.ShopifyResource.site) - - def test_temp_works_without_currently_active_session(self): - shopify.ShopifyResource.clear_session() - - assigned_site = "" - with shopify.Session.temp("testshop.myshopify.com", "any-token"): - assigned_site = shopify.ShopifyResource.site - - self.assertEqual('https://testshop.myshopify.com/admin', assigned_site) - self.assertEqual('https://None/admin', shopify.ShopifyResource.site) - - def test_create_permission_url_returns_correct_url_with_single_scope_no_redirect_uri(self): - shopify.Session.setup(api_key="My_test_key", secret="My test secret") - session = shopify.Session('http://localhost.myshopify.com') - scope = ["write_products"] - permission_url = session.create_permission_url(scope) - self.assertEqual("https://localhost.myshopify.com/admin/oauth/authorize?scope=write_products&client_id=My_test_key", permission_url) - - def test_create_permission_url_returns_correct_url_with_single_scope_and_redirect_uri(self): - shopify.Session.setup(api_key="My_test_key", secret="My test secret") - session = shopify.Session('http://localhost.myshopify.com') - scope = ["write_products"] - permission_url = session.create_permission_url(scope, "my_redirect_uri.com") - self.assertEqual("https://localhost.myshopify.com/admin/oauth/authorize?scope=write_products&redirect_uri=my_redirect_uri.com&client_id=My_test_key", permission_url) - - def test_create_permission_url_returns_correct_url_with_dual_scope_no_redirect_uri(self): - shopify.Session.setup(api_key="My_test_key", secret="My test secret") - session = shopify.Session('http://localhost.myshopify.com') - scope = ["write_products","write_customers"] - permission_url = session.create_permission_url(scope) - self.assertEqual("https://localhost.myshopify.com/admin/oauth/authorize?scope=write_products%2Cwrite_customers&client_id=My_test_key", permission_url) - - def test_create_permission_url_returns_correct_url_with_no_scope_no_redirect_uri(self): - shopify.Session.setup(api_key="My_test_key", secret="My test secret") - session = shopify.Session('http://localhost.myshopify.com') - scope = [] - permission_url = session.create_permission_url(scope) - self.assertEqual("https://localhost.myshopify.com/admin/oauth/authorize?scope=&client_id=My_test_key", permission_url) - - def test_raise_exception_if_code_invalid_in_request_token(self): - shopify.Session.setup(api_key="My test key", secret="My test secret") - session = shopify.Session('http://localhost.myshopify.com') - self.fake(None, url='https://localhost.myshopify.com/admin/oauth/access_token', method='POST', code=404, body='{"error" : "invalid_request"}', has_user_agent=False) - - with self.assertRaises(shopify.ValidationException): - session.request_token({'code':'any-code', 'timestamp':'1234'}) - - self.assertFalse(session.valid) - - def test_return_site_for_session(self): - session = shopify.Session("testshop.myshopify.com", "any-token") - self.assertEqual("https://testshop.myshopify.com/admin", session.site) - - def test_return_token_if_signature_is_valid(self): - shopify.Session.secret='secret' - params = {'code': 'any-code', 'timestamp': time.time()} - sorted_params = self.make_sorted_params(params) - signature = md5(shopify.Session.secret + sorted_params).hexdigest() - params['signature'] = signature - - self.fake(None, url='https://localhost.myshopify.com/admin/oauth/access_token', method='POST', body='{"access_token" : "token"}', has_user_agent=False) - session = shopify.Session('http://localhost.myshopify.com') - token = session.request_token(params) - self.assertEqual("token", token) - - def test_raise_error_if_signature_does_not_match_expected(self): - shopify.Session.secret='secret' - params = {'foo': 'hello', 'timestamp': time.time()} - sorted_params = self.make_sorted_params(params) - signature = md5(shopify.Session.secret + sorted_params).hexdigest() - params['signature'] = signature - params['bar'] = 'world' - params['code'] = 'code' - - with self.assertRaises(shopify.ValidationException): - session = shopify.Session('http://localhost.myshopify.com') - session = session.request_token(params) - - def test_raise_error_if_timestamp_is_too_old(self): - shopify.Session.secret='secret' - one_day = 24 * 60 * 60 - params = {'code': 'any-code', 'timestamp': time.time()-(2*one_day)} - sorted_params = self.make_sorted_params(params) - signature = md5(shopify.Session.secret + sorted_params).hexdigest() - params['signature'] = signature - - with self.assertRaises(shopify.ValidationException): - session = shopify.Session('http://localhost.myshopify.com') - session = session.request_token(params) - - - def make_sorted_params(self, params): - sorted_params = "" - for k in sorted(params.keys()): - if k != "signature": - sorted_params += k + "=" + str(params[k]) - return sorted_params diff --git a/test/shop_test.py b/test/shop_test.py deleted file mode 100644 index 66be7d4a..00000000 --- a/test/shop_test.py +++ /dev/null @@ -1,44 +0,0 @@ -import shopify -from test_helper import TestCase - -class ShopTest(TestCase): - def setUp(self): - super(ShopTest, self).setUp() - self.fake("shop") - self.shop = shopify.Shop.current() - - def test_current_should_return_current_shop(self): - self.assertTrue(isinstance(self.shop,shopify.Shop)) - self.assertEqual("Apple Computers", self.shop.name) - self.assertEqual("apple.myshopify.com", self.shop.myshopify_domain) - self.assertEqual(690933842, self.shop.id) - self.assertEqual("2007-12-31T19:00:00-05:00", self.shop.created_at) - self.assertIsNone(self.shop.tax_shipping) - - def test_get_metafields_for_shop(self): - self.fake("metafields") - - metafields = self.shop.metafields() - - self.assertEqual(2, len(metafields)) - for field in metafields: - self.assertTrue(isinstance(field, shopify.Metafield)) - - def test_add_metafield(self): - self.fake("metafields", method='POST', code=201, body=self.load_fixture('metafield'), headers={'Content-type': 'application/json'}) - - field = self.shop.add_metafield( shopify.Metafield({'namespace': "contact", 'key': "email", 'value': "123@example.com", 'value_type': "string"})) - - self.assertFalse(field.is_new()) - self.assertEqual("contact", field.namespace) - self.assertEqual("email", field.key) - self.assertEqual("123@example.com", field.value) - - def test_events(self): - self.fake("events") - - events = self.shop.events() - - self.assertEqual(3, len(events)) - for event in events: - self.assertTrue(isinstance(event, shopify.Event)) diff --git a/test/test_helper.py b/test/test_helper.py deleted file mode 100644 index bd89edcb..00000000 --- a/test/test_helper.py +++ /dev/null @@ -1,56 +0,0 @@ -import os -import sys -import unittest -from pyactiveresource.activeresource import ActiveResource -from pyactiveresource.testing import http_fake -import shopify - -class TestCase(unittest.TestCase): - - def setUp(self): - ActiveResource.site = None - ActiveResource.headers=None - - shopify.ShopifyResource.clear_session() - shopify.ShopifyResource.site = "https://this-is-my-test-show.myshopify.com/admin" - shopify.ShopifyResource.password = None - shopify.ShopifyResource.user = None - - http_fake.initialize() - self.http = http_fake.TestHandler - self.http.set_response(Exception('Bad request')) - self.http.site = 'https://this-is-my-test-show.myshopify.com' - - def load_fixture(self, name, format='json'): - return open(os.path.dirname(__file__)+'/fixtures/%s.%s' % (name, format), 'r').read() - - def fake(self, endpoint, **kwargs): - body = kwargs.pop('body', None) or self.load_fixture(endpoint) - format = kwargs.pop('format','json') - method = kwargs.pop('method','GET') - - if ('extension' in kwargs and not kwargs['extension']): - extension = "" - else: - extension = ".%s" % (kwargs.pop('extension', 'json')) - - url = "https://this-is-my-test-show.myshopify.com/admin/%s%s" % (endpoint, extension) - try: - url = kwargs['url'] - except KeyError: - pass - - headers = {} - if kwargs.pop('has_user_agent', True): - userAgent = 'ShopifyPythonAPI/%s Python/%s' % (shopify.VERSION, sys.version.split(' ', 1)[0]) - headers['User-agent'] = userAgent - - try: - headers.update(kwargs['headers']) - except KeyError: - pass - - code = kwargs.pop('code', 200) - - self.http.respond_to( - method, url, headers, body=body, code=code) diff --git a/test/transaction_test.py b/test/transaction_test.py deleted file mode 100644 index ae32677f..00000000 --- a/test/transaction_test.py +++ /dev/null @@ -1,11 +0,0 @@ -import shopify -from test_helper import TestCase - -class TransactionTest(TestCase): - def setUp(self): - super(TransactionTest, self).setUp() - self.fake("orders/450789469/transactions/389404469", method='GET', body=self.load_fixture('transaction')) - - def test_should_find_a_specific_transaction(self): - transaction = shopify.Transaction.find(389404469, order_id=450789469) - self.assertEqual("409.94", transaction.amount) diff --git a/test/variant_test.py b/test/variant_test.py deleted file mode 100644 index dd9d55c2..00000000 --- a/test/variant_test.py +++ /dev/null @@ -1,34 +0,0 @@ -import shopify -from test_helper import TestCase - -class VariantTest(TestCase): - - def test_get_variants(self): - self.fake("products/632910392/variants", method='GET', body=self.load_fixture('variants')) - v = shopify.Variant.find(product_id = 632910392) - - def test_get_variant_namespaced(self): - self.fake("products/632910392/variants/808950810", method='GET', body=self.load_fixture('variant')) - v = shopify.Variant.find(808950810, product_id = 632910392) - - def test_update_variant_namespace(self): - self.fake("products/632910392/variants/808950810", method='GET', body=self.load_fixture('variant')) - v = shopify.Variant.find(808950810, product_id = 632910392) - - self.fake("products/632910392/variants/808950810", method='PUT', body=self.load_fixture('variant'), headers={'Content-type': 'application/json'}) - v.save() - - def test_create_variant(self): - self.fake("products/632910392/variants", method='POST', body=self.load_fixture('variant'), headers={'Content-type': 'application/json'}) - v = shopify.Variant({'product_id':632910392}) - v.save() - - def test_create_variant_then_add_parent_id(self): - self.fake("products/632910392/variants", method='POST', body=self.load_fixture('variant'), headers={'Content-type': 'application/json'}) - v = shopify.Variant() - v.product_id = 632910392 - v.save() - - def test_get_variant(self): - self.fake("variants/808950810", method='GET', body=self.load_fixture('variant')) - v = shopify.Variant.find(808950810)