Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

initial load up

  • Loading branch information...
commit 38dc901b252ddccaced4c0c5c9bc1d3d9292953a 0 parents
SubSonic Project subsonic authored
102 Api.cs
@@ -0,0 +1,102 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Net;
+using System.IO;
+using System.Dynamic;
+
+namespace Shopify {
+ public class Api : DynamicObject {
+
+ public string _apiKey { get; set; }
+ public string _password { get; set; }
+ public string _baseUrl { get; set; }
+ public Api(string apiKey, string password, string storeUrl)
+ {
+ //https://api_key:password@some-store.myshopify.com/admin/some-resource
+ _apiKey = apiKey;
+ _password = password;
+ _baseUrl = storeUrl; //some-store.myshopify.com
+
+ if (_baseUrl.StartsWith("http"))
+ _baseUrl = _baseUrl.Replace("http", "https");
+
+ if (!_baseUrl.StartsWith("https://"))
+ _baseUrl = "https://" + _baseUrl;
+
+ if (!_baseUrl.EndsWith("/"))
+ _baseUrl += "/admin/";
+ else
+ _baseUrl += "admin/";
+ }
+ /// <summary>
+ /// A simple GET request to the Shopify API
+ /// </summary>
+ string Send(string url) {
+ HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
+ var creds = new NetworkCredential(userName: _apiKey, password: _password);
+ request.Credentials = creds;
+ var response = (HttpWebResponse)request.GetResponse();
+ string result = "";
+
+ using (Stream stream = response.GetResponseStream()) {
+ StreamReader sr = new StreamReader(stream);
+ result = sr.ReadToEnd();
+ sr.Close();
+ }
+ return result;
+ }
+
+ /// <summary>
+ /// This allows you to work with the data at Shopify using a Property, which represents Products, Customers, etc
+ /// </summary>
+ public override bool TryGetMember(GetMemberBinder binder, out object result) {
+
+ var name = binder.Name.ToLower();
+
+ //we can do this because the Shopify stuff is all pluralized with "s" :)
+ if (!name.EndsWith("s"))
+ name += "s";
+
+ result = new ShopifyObject(name,_baseUrl,_apiKey,_password);
+ return true;
+ }
+
+ /// <summary>
+ /// This builds a query with the passed in named arguments - shopify.Products(collection_id:121212)
+ /// </summary>
+ public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) {
+ var name = binder.Name.ToLower() + ".json";
+ var url = _baseUrl + name;
+
+ //params?
+ var info = binder.CallInfo;
+ var looper = 0;
+ if (info.ArgumentNames.Count > 0) {
+
+ for (int i = 0; i < args.Length; i++) {
+ var argName = info.ArgumentNames[i].ToLower();
+ var val = args[i];
+ //the ID is a singular call
+ //with a special format
+ if (argName == "id") {
+ url = url.Replace(".json", "/" + val + ".json");
+ } else {
+ if (looper == 0)
+ url += "?";
+ else
+ url += "&";
+ url += string.Format("{0}={1}", argName, val);
+ }
+ looper++;
+ }
+ }
+ var json = Send(url);
+ result = JsonHelper.Decode(json);
+ return true;
+ }
+
+
+ }
+}
81 JsonHelper.cs
@@ -0,0 +1,81 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Web.Script.Serialization;
+using System.Collections.ObjectModel;
+using System.Dynamic;
+using System.Collections;
+
+namespace Shopify {
+ public class JsonHelper {
+ // json -> dynamic decoder adopted from Shawn Weisfeld, http://bit.ly/jPqVsQ
+ public static dynamic Decode(string json) {
+ var serializer = new JavaScriptSerializer();
+ serializer.RegisterConverters(new[] { new DynamicJsonConverter() });
+
+ dynamic obj = serializer.Deserialize(json, typeof(object));
+
+ return obj;
+ }
+ public static dynamic Encode(dynamic item) {
+ var serializer = new JavaScriptSerializer();
+ serializer.RegisterConverters(new JavaScriptConverter[]{ new DynamicJsonConverter() });
+ dynamic obj = serializer.Serialize(item);
+
+ return obj;
+ }
+ private sealed class DynamicJsonConverter : JavaScriptConverter {
+ public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer) {
+ if (dictionary == null)
+ throw new ArgumentNullException("dictionary");
+
+ return type == typeof(object) ? new DynamicJsonObject(dictionary) : null;
+ }
+
+ public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer) {
+ return null;
+ }
+
+ public override IEnumerable<Type> SupportedTypes {
+ get { return new ReadOnlyCollection<Type>(new List<Type>(new Type[] { typeof(object) })); }
+ }
+ }
+
+ private sealed class DynamicJsonObject : DynamicObject {
+ private readonly IDictionary<string, object> _dictionary;
+
+ public DynamicJsonObject(IDictionary<string, object> dictionary) {
+ if (dictionary == null)
+ throw new ArgumentNullException("dictionary");
+ _dictionary = dictionary;
+ }
+
+ public override bool TryGetMember(GetMemberBinder binder, out object result) {
+ if (!_dictionary.TryGetValue(binder.Name, out result)) {
+ // return null to avoid exception. caller can check for null this way...
+ result = null;
+ return true;
+ }
+
+ var dictionary = result as IDictionary<string, object>;
+ if (dictionary != null) {
+ result = new DynamicJsonObject(dictionary);
+ return true;
+ }
+
+ var arrayList = result as ArrayList;
+ if (arrayList != null && arrayList.Count > 0) {
+ if (arrayList[0] is IDictionary<string, object>)
+ result = new List<object>(arrayList.Cast<IDictionary<string, object>>().Select(x => new DynamicJsonObject(x)));
+ else
+ result = new List<object>(arrayList.Cast<object>());
+ }
+
+ return true;
+ }
+ }
+
+
+ }
+}
11 LICENSE.txt
@@ -0,0 +1,11 @@
+New BSD License
+http://www.opensource.org/licenses/bsd-license.php
+Copyright (c) 2009, Rob Conery (robconery@gmail.com)
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+Neither the name of the SubSonic nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("Shopify")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Microsoft")]
+[assembly: AssemblyProduct("Shopify")]
+[assembly: AssemblyCopyright("Copyright © Microsoft 2011")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("8ede8944-352a-4730-9a74-e63fc80d61b4")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
57 README.markdown
@@ -0,0 +1,57 @@
+The Shopify API Wrapper for .NET 4.0
+=====================================
+
+Shopify is great for running a store - but up until now there haven't been many API tools for .NET. This simple library is useful for querying your Shopify store as well as adding, editing, and deleting items.
+
+How To Install It?
+------------------
+Reference the Shopify.dll in your project.
+
+How Do You Use It?
+------------------
+This might come as a big surprise to people... but it's all dynamic. You structure queries based on what you need from the API.
+
+For instance - if you want to query for products, you make a JSON request to the Shopify API (the docs are here: http://api.shopify.com/product.html):
+
+ var shopify = new Shopify.Api("somestore.myshopify.com","myAPIKey","myAPIPassword");
+ var query = shopify.Products();
+ foreach(var product in query.products){
+ Console.Writeline(product.title);
+ }
+
+In this example, the method "Products()" doesn't exist - it's "caught" by the API wrapper and a URL constructed for you (with the necessary credentials). What you get back is a dynamic bit of JSON that you can then loop over. You'll need to know the structure of the return call - but it's all documented up in the Shopify API.
+
+You can also query by passing in parameters:
+
+ var shopify = new Shopify.Api("somestore.myshopify.com","myAPIKey","myAPIPassword");
+ var query = shopify.Products(collection_id: 12121212);
+ foreach(var product in query.products){
+ Console.Writeline(product.title);
+ }
+
+Same thing - the call here is dynamicall inferred based on the name of the named argument.
+
+Adding, Editing, Deleting
+-------------------------
+Again, all dynamic. For this you use an ExpandoObject and pass it into the API:
+
+ //use an Anon type here if you like... it just needs to match the casing and structure of the Shopify bits
+ var p = new {
+ title = "My new Product";
+ handle = "sex-appeal";
+ //add variants and price here using arrays
+ }
+
+ var shopify = new Shopify.Api("somestore.myshopify.com","myAPIKey","myAPIPassword");
+ shopify.Products.Save(p);
+
+The last call there is, once again, dynamically inferred. "Products" doesn't exist as a property on "shopify" - so a bunch of goodness happens which results in you being able to execute an HTTP POST to the Shopify API.
+
+If there's an "id" present on the product, a "PUT" will be executed, which will update the item (Product, Collection, Blog, Page... whatever).
+
+To delete, you simply need to know the ID. You can do that easily:
+
+ var shopify = new Shopify.Api("somestore.myshopify.com","myAPIKey","myAPIPassword");
+ var query = shopify.Products(handle: "sex-appeal");
+ var id = query.products[0].id
+ shopify.Products.Delete(id);
62 Shopify.csproj
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProductVersion>8.0.30703</ProductVersion>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{E7CE8AD8-6F2B-4161-940F-304AB815AB7A}</ProjectGuid>
+ <OutputType>Library</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <RootNamespace>Shopify</RootNamespace>
+ <AssemblyName>Shopify</AssemblyName>
+ <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
+ <FileAlignment>512</FileAlignment>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\Debug\</OutputPath>
+ <DefineConstants>DEBUG;TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>bin\Release\</OutputPath>
+ <DefineConstants>TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="System" />
+ <Reference Include="System.Core" />
+ <Reference Include="System.Web" />
+ <Reference Include="System.Web.Extensions" />
+ <Reference Include="System.Web.Helpers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL" />
+ <Reference Include="System.Xml.Linq" />
+ <Reference Include="System.Data.DataSetExtensions" />
+ <Reference Include="Microsoft.CSharp" />
+ <Reference Include="System.Data" />
+ <Reference Include="System.Xml" />
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="Api.cs" />
+ <Compile Include="JsonHelper.cs" />
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ <Compile Include="ShopifyObject.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="README.markdown" />
+ </ItemGroup>
+ <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+ <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
+ Other similar extension points exist, see Microsoft.Common.targets.
+ <Target Name="BeforeBuild">
+ </Target>
+ <Target Name="AfterBuild">
+ </Target>
+ -->
+</Project>
6 Shopify.csproj.user
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <ProjectView>ProjectFiles</ProjectView>
+ </PropertyGroup>
+</Project>
137 ShopifyObject.cs
@@ -0,0 +1,137 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Net;
+using System.IO;
+using System.Dynamic;
+using System.Web.Script.Serialization;
+
+namespace Shopify {
+ public class ShopifyObject:DynamicObject {
+ string _objectType;
+ string _apiKey;
+ string _password;
+ string _shopUrl;
+
+ /// <summary>
+ /// This is a special, psuedo-wrapper for Shopify objects such as Products, Customers, and so on
+ /// </summary>
+ public ShopifyObject(string objectType, string storeUrl, string apiKey, string password) {
+ _objectType = objectType;
+ _shopUrl = storeUrl;
+ _apiKey = apiKey;
+ _password = password;
+ }
+
+ /// <summary>
+ /// A Dynamic catcher - allows you to invoke Save, Delete, Add, etc on this object
+ /// </summary>
+ public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) {
+
+ //the args should be expandos
+ //loop them and spin up some JSON
+ var sb = new StringBuilder();
+ var name = _objectType;
+ if (name.EndsWith("s"))
+ name = name.TrimEnd('s');
+
+ //what are we doing?
+ if (binder.Name == "Save") {
+
+ var item = args[0];
+ //var dc = (IDictionary<string, object>)item;
+ var serializer = new JavaScriptSerializer();
+ var json = serializer.Serialize(item);
+
+ //wrap this as we need an outer identifier
+ json = "{ " + name + ": " + json + "}";
+
+
+ bool isNew = !item.GetType().GetProperties().Any(x => x.Name == "id");
+
+ //adjust the root to be the name here
+ //var outer = new Dictionary<string, object>();
+ //outer.Add(name, item);
+
+ if (isNew) {
+ Post(json);
+ Console.WriteLine("{0} added...", name);
+ } else {
+ //pull the id
+ var id = item.GetType().GetProperty("id").GetValue(item, null).ToString();
+ Put(id, json);
+ Console.WriteLine("{0} updated...", name);
+ }
+
+ } else if (binder.Name == "Delete" || binder.Name == "Destroy") {
+ Delete(args[0].ToString());
+ Console.WriteLine("Blog {0} deleted ...", args[0].ToString());
+ } else {
+ throw new InvalidDataException("Can't tell what it is you want to do - try using Save or Delete instead");
+ }
+ result = this;
+ return true;
+ }
+
+ /// <summary>
+ /// Executes a PUT to Shopify - used for Updates
+ /// </summary>
+ dynamic Put(string id, string json) {
+ //build the URL
+ var url = _shopUrl + this._objectType + "/" + id + ".json";
+ var result = ExecuteRequest(url, "PUT", json);
+ return JsonHelper.Decode(result);
+ }
+ /// <summary>
+ /// Executes an HTTP DELETE to Shopify... guess what it does!
+ /// </summary>
+ void Delete(string id) {
+ var url = _shopUrl + this._objectType + "/" + id + ".json";
+ ExecuteRequest(url, "DELETE", "");
+ }
+ /// <summary>
+ /// Executes an HTTP POST - which adds an item to the Shopify DB
+ /// </summary>
+ dynamic Post(string json) {
+ //build the URL
+ var url = _shopUrl + this._objectType + ".json";
+ var result = ExecuteRequest(url, "POST", json);
+ //the result will be a pile of JSON
+ //deserialize it and return
+ return JsonHelper.Decode(result);
+
+ }
+
+ /// <summary>
+ /// The core executor for sending off requests
+ /// </summary>
+ string ExecuteRequest(string url, string verb, string data) {
+ HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
+ request.Method = verb;
+ request.ContentType = "application/json";
+ var creds = new NetworkCredential(userName: _apiKey, password: _password);
+ request.Credentials = creds;
+
+ //add the data if needed
+ if (!String.IsNullOrEmpty(data)) {
+ using (var ms = new MemoryStream()) {
+ using (var writer = new StreamWriter(request.GetRequestStream())) {
+ writer.Write(data);
+ writer.Close();
+ }
+ }
+ }
+
+ var response = (HttpWebResponse)request.GetResponse();
+ string result = "";
+
+ using (Stream stream = response.GetResponseStream()) {
+ StreamReader sr = new StreamReader(stream);
+ result = sr.ReadToEnd();
+ sr.Close();
+ }
+ return result;
+ }
+ }
+}
BIN  bin/Debug/Shopify.dll
Binary file not shown
BIN  bin/Debug/Shopify.pdb
Binary file not shown
BIN  obj/Debug/DesignTimeResolveAssemblyReferencesInput.cache
Binary file not shown
5 obj/Debug/Shopify.csproj.FileListAbsolute.txt
@@ -0,0 +1,5 @@
+C:\@projects\Shopify\bin\Debug\Shopify.dll
+C:\@projects\Shopify\bin\Debug\Shopify.pdb
+C:\@projects\Shopify\obj\Debug\ResolveAssemblyReference.cache
+C:\@projects\Shopify\obj\Debug\Shopify.dll
+C:\@projects\Shopify\obj\Debug\Shopify.pdb
BIN  obj/Debug/Shopify.dll
Binary file not shown
BIN  obj/Debug/Shopify.pdb
Binary file not shown
Please sign in to comment.
Something went wrong with that request. Please try again.