Commits

Michal Kutil committed f7d2c53

Variable data client add

Comments (0)

Files changed (10)

Client/Client.csproj

+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProjectGuid>{7F8A3F48-7746-4C65-A022-B81DEE622FBD}</ProjectGuid>
+    <OutputType>Library</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>Plantyst.Client</RootNamespace>
+    <AssemblyName>Plantyst.Client</AssemblyName>
+    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+    <FileAlignment>512</FileAlignment>
+    <TargetFrameworkProfile />
+  </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>
+    <Prefer32Bit>false</Prefer32Bit>
+  </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>
+    <Prefer32Bit>false</Prefer32Bit>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="System" />
+    <Reference Include="System.Core" />
+    <Reference Include="System.IdentityModel" />
+    <Reference Include="System.IdentityModel.Tokens.Jwt">
+      <HintPath>..\packages\System.IdentityModel.Tokens.Jwt.1.0.0\lib\net45\System.IdentityModel.Tokens.Jwt.dll</HintPath>
+    </Reference>
+    <Reference Include="System.Web" />
+    <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="Security\AuthorizationTokenProvider.cs" />
+    <Compile Include="RequestFactory.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="Security\IUserAccessToken.cs" />
+    <Compile Include="Security\UserAccessToken.cs" />
+    <Compile Include="VariableDataClient.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="packages.config" />
+  </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>

Client/Properties/AssemblyInfo.cs

+using System.Reflection;
+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("Plantyst C Sharp Api Client")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("Plantyst")]
+[assembly: AssemblyCopyright("Copyright © Plantyst 2013")]
+[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("58c01d9c-62b8-4546-be9b-add41b0f17e2")]
+
+// 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.*")]

Client/RequestFactory.cs

+namespace Plantyst.Client
+{
+    using System;
+    using System.Collections.Generic;
+    using System.Collections.Specialized;
+    using System.Net;
+    using System.Security.Claims;
+    using System.Web;
+    using Plantyst.Client.Security;
+
+    /// <summary>
+    /// The http web request factory.
+    /// </summary>
+    public class RequestFactory
+    {
+        /// <summary>
+        /// The base URI.
+        /// </summary>
+        public const string BaseUri = @"http://my.plantyst.com/";
+
+        /// <summary>
+        /// Creates the get request.
+        /// </summary>
+        /// <param name="relativeUri">The relative URI.</param>
+        /// <param name="userAccessToken">The user access token.</param>
+        /// <param name="parameters">The query string parameters.</param>
+        /// <param name="claims">The claims add to the Authorization token header.</param>
+        /// <returns>Http web request.</returns>
+        /// <exception cref="System.ArgumentException">Throw if argument <see cref="relativeUri"/> is null or empty.</exception>
+        public virtual HttpWebRequest CreateGetRequest(string relativeUri, IUserAccessToken userAccessToken, NameValueCollection parameters, IEnumerable<Claim> claims)
+        {
+            if (string.IsNullOrWhiteSpace(relativeUri))
+            {
+                throw new ArgumentException("Argument relativeUri can not be null or empty.", "relativeUri");
+            }
+
+            var uriBuilder = new UriBuilder(RequestFactory.BaseUri);
+            uriBuilder.Path = relativeUri;
+
+            // Add parameters to the request.
+            if (parameters != null && parameters.Count > 0)
+            {
+                var queryString = HttpUtility.ParseQueryString(uriBuilder.Query);
+                queryString.Add(parameters);
+                uriBuilder.Query = queryString.ToString();
+            }
+
+            // Create HttpWebRequest.
+            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uriBuilder.Uri);
+            request.Method = "GET";
+
+            // Add authorization header
+            if (userAccessToken != null)
+            {
+                var authorizationTokenProvider = new AuthorizationTokenProvider(RequestFactory.BaseUri, userAccessToken);
+                var token = authorizationTokenProvider.CreateAuthorizationToken(TimeSpan.FromSeconds(60), claims);
+                request.Headers.Add("Authorization", "Token " + token);
+            }
+
+            return request;
+        }
+    }
+}

Client/Security/AuthorizationTokenProvider.cs

+namespace Plantyst.Client.Security
+{
+    using System;
+    using System.Collections.Generic;
+    using System.IdentityModel.Protocols.WSTrust;
+    using System.IdentityModel.Tokens;
+    using System.Security.Claims;
+
+    /// <summary>
+    /// Authorization token provider.
+    /// </summary>
+    public class AuthorizationTokenProvider
+    {
+        /// <summary>
+        /// The access token.
+        /// </summary>
+        private IUserAccessToken userAccessToken;
+
+        /// <summary>
+        /// The base URI.
+        /// </summary>
+        private string baseUri;
+
+        /// <summary>
+        /// The date time function.
+        /// </summary>
+        private Func<DateTime> dateTimeFunc;
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="AuthorizationTokenProvider"/> class.
+        /// </summary>
+        /// <param name="baseUri">The base URI.</param>
+        /// <param name="userAccessToken">The user access token.</param>
+        public AuthorizationTokenProvider(string baseUri, IUserAccessToken userAccessToken) :
+            this(baseUri, userAccessToken, () => DateTime.UtcNow)
+        {
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="AuthorizationTokenProvider" /> class.
+        /// </summary>
+        /// <param name="baseUri">The base URI.</param>
+        /// <param name="userAccessToken">The access token.</param>
+        /// <param name="dateTimeFunc">The date time function.</param>
+        public AuthorizationTokenProvider(string baseUri, IUserAccessToken userAccessToken, Func<DateTime> dateTimeFunc)
+        {
+            this.baseUri = baseUri;
+            this.userAccessToken = userAccessToken;
+            this.dateTimeFunc = dateTimeFunc;
+        }
+
+        /// <summary>
+        /// Creates the authorization token.
+        /// </summary>
+        /// <param name="lifetime">The token lifetime.</param>
+        /// <param name="claims">The token claims.</param>
+        /// <returns>The authorization token.</returns>
+        public string CreateAuthorizationToken(TimeSpan lifetime, IEnumerable<Claim> claims)
+        {
+            var tokenHandler = new JwtSecurityTokenHandler();
+            var now = this.dateTimeFunc();
+            var subject = new ClaimsIdentity(claims);
+            subject.AddClaim(new Claim("AccessKey", this.userAccessToken.Key));
+
+            var tokenDescriptor = new SecurityTokenDescriptor
+            {
+                Subject = subject,
+                TokenIssuerName = this.userAccessToken.Application,
+                AppliesToAddress = this.baseUri,
+                Lifetime = new Lifetime(now, now.Add(lifetime)),
+                SigningCredentials = new SigningCredentials(
+                    new InMemorySymmetricSecurityKey(Convert.FromBase64String(this.userAccessToken.Secret)),
+                    "http://www.w3.org/2001/04/xmldsig-more#hmac-sha256",
+                    "http://www.w3.org/2001/04/xmlenc#sha256")
+            };
+
+            var token = tokenHandler.CreateToken(tokenDescriptor);
+            var tokenString = tokenHandler.WriteToken(token);
+            return tokenString;
+        }
+    }
+}

Client/Security/IUserAccessToken.cs

+namespace Plantyst.Client.Security
+{
+    /// <summary>
+    /// User access token.
+    /// </summary>
+    public interface IUserAccessToken
+    {
+        /// <summary>
+        /// Gets the application id.
+        /// </summary>
+         string Application { get; }
+
+         /// <summary>
+         /// Gets the access token key.
+         /// </summary>
+         string Key { get; }
+
+         /// <summary>
+         /// Gets the access token secret.
+         /// </summary>
+         /// <remarks>The secret is between byte array and the string coded/decoded by the base64.</remarks>
+         string Secret { get; }
+    }
+}

Client/Security/UserAccessToken.cs

+namespace Plantyst.Client.Security
+{
+    /// <summary>
+    /// User access token.
+    /// </summary>
+    public struct UserAccessToken : IUserAccessToken
+    {
+        /// <summary>
+        /// Gets or sets the application.
+        /// </summary>
+        public string Application { get; set; }
+
+        /// <summary>
+        /// Gets or sets access token key.
+        /// </summary>
+        public string Key { get; set; }
+
+        /// <summary>
+        /// Gets or sets the access token secret.
+        /// </summary>
+        /// <remarks>The secret is between byte array and the string coded/decoded by the base64.</remarks>
+        public string Secret { get; set; }
+    }
+}

Client/Settings.StyleCop

+<StyleCopSettings Version="105">
+  <Analyzers>
+    <Analyzer AnalyzerId="Community.StyleCop.CSharp.LayoutRules">
+      <Rules>
+        <Rule Name="LinesMustNotBeLongerThanNumCharacters">
+          <RuleSettings>
+            <BooleanProperty Name="Enabled">False</BooleanProperty>
+          </RuleSettings>
+        </Rule>
+      </Rules>
+      <AnalyzerSettings />
+    </Analyzer>
+    <Analyzer AnalyzerId="StyleCop.CSharp.DocumentationRules">
+      <Rules>
+        <Rule Name="FileMustHaveHeader">
+          <RuleSettings>
+            <BooleanProperty Name="Enabled">False</BooleanProperty>
+          </RuleSettings>
+        </Rule>
+      </Rules>
+      <AnalyzerSettings>
+        <StringProperty Name="CompanyName">Plantyst</StringProperty>
+        <StringProperty Name="Copyright">Copyright Plantyst</StringProperty>
+      </AnalyzerSettings>
+    </Analyzer>
+  </Analyzers>
+</StyleCopSettings>

Client/VariableDataClient.cs

+namespace Plantyst.Client
+{
+    using System;
+    using System.Collections.Generic;
+    using System.Collections.Specialized;
+    using System.IO;
+    using System.Linq;
+    using System.Net;
+    using System.Security.Claims;
+    using System.Threading.Tasks;
+    using Plantyst.Client.Security;
+
+    /// <summary>
+    /// Variable data client
+    /// </summary>
+    public class VariableDataClient
+    {
+        /// <summary>
+        /// The path to the <c>Api</c> access point.
+        /// </summary>
+        private const string Path = @"/api/VariableData/";
+
+        /// <summary>
+        /// The date time format.
+        /// </summary>
+        private const string DateTimeFormat = @"yyyy-MM-dd HH:mm";
+
+        /// <summary>
+        /// The user access token
+        /// </summary>
+        private IUserAccessToken userAccessToken;
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="VariableDataClient"/> class.
+        /// </summary>
+        /// <param name="userAccessToken">The user access token.</param>
+        public VariableDataClient(IUserAccessToken userAccessToken)
+        {
+            this.userAccessToken = userAccessToken;
+        }
+
+        /// <summary>
+        /// Gets the request factory.
+        /// </summary>
+        protected virtual RequestFactory RequestFactory
+        {
+            get
+            {
+                return new RequestFactory();
+            }
+        }
+
+        /// <summary>
+        /// Gets the variable data for requested variable id set.
+        /// </summary>
+        /// <param name="variableIdSet">The id array of requested variables.</param>
+        /// <param name="from">From time in variable time zone.</param>
+        /// <param name="to">To time in variable time zone.</param>
+        /// <param name="timeMode">The time mode.</param>
+        /// <returns>Task with serialized variable data.</returns>
+        /// <exception cref="System.ArgumentException">Throw exception if <see cref="variableIdSet" /> is empty or null.</exception>
+        public Task<Stream> Get(IEnumerable<int> variableIdSet, DateTime from, DateTime to, string timeMode)
+        {
+            if (variableIdSet == null || variableIdSet.Count() == 0)
+            {
+                throw new ArgumentException();
+            }
+
+            // prepare parameters
+            var parameters = new NameValueCollection();
+            foreach (var variableId in variableIdSet)
+            {
+                parameters.Add("variableIdSet", variableId.ToString());
+            }
+
+            parameters.Add("from", from.ToString(VariableDataClient.DateTimeFormat));
+            parameters.Add("to", to.ToString(VariableDataClient.DateTimeFormat));
+            parameters.Add("mode", timeMode);
+
+            var claims = new List<Claim>(1);
+            claims.Add(new Claim("variableIdSet", "[" + string.Join(",", variableIdSet.Select(v => v.ToString())) + "]", ClaimTypes.UserData));
+
+            HttpWebRequest request = this.RequestFactory.CreateGetRequest(VariableDataClient.Path, this.userAccessToken, parameters, claims);
+
+            var responseTask = request.GetResponseAsync();
+            return responseTask.ContinueWith(webResponseTask => webResponseTask.Result.GetResponseStream()); // todo: handle error states
+        }
+    }
+}

Client/packages.config

+<?xml version="1.0" encoding="utf-8"?>
+<packages>
+  <package id="System.IdentityModel.Tokens.Jwt" version="1.0.0" targetFramework="net45" />
+</packages>

Plantyst SDK for Net.sln

+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 2012
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Client", "Client\Client.csproj", "{7F8A3F48-7746-4C65-A022-B81DEE622FBD}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
+		Release|Any CPU = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{7F8A3F48-7746-4C65-A022-B81DEE622FBD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{7F8A3F48-7746-4C65-A022-B81DEE622FBD}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{7F8A3F48-7746-4C65-A022-B81DEE622FBD}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{7F8A3F48-7746-4C65-A022-B81DEE622FBD}.Release|Any CPU.Build.0 = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+EndGlobal