Skip to content

Commit cd63903

Browse files
author
gauffininteractive
committed
client api
1 parent 011d08c commit cd63903

File tree

13 files changed

+536
-7
lines changed

13 files changed

+536
-7
lines changed
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3+
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
4+
<PropertyGroup>
5+
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
6+
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
7+
<ProjectGuid>{62989500-31BC-44FA-97CA-84F484C6F1AA}</ProjectGuid>
8+
<OutputType>Library</OutputType>
9+
<AppDesignerFolder>Properties</AppDesignerFolder>
10+
<RootNamespace>OneTrueError.Api.Client.Tests</RootNamespace>
11+
<AssemblyName>OneTrueError.Api.Client.Tests</AssemblyName>
12+
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
13+
<FileAlignment>512</FileAlignment>
14+
</PropertyGroup>
15+
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
16+
<DebugSymbols>true</DebugSymbols>
17+
<DebugType>full</DebugType>
18+
<Optimize>false</Optimize>
19+
<OutputPath>bin\Debug\</OutputPath>
20+
<DefineConstants>DEBUG;TRACE</DefineConstants>
21+
<ErrorReport>prompt</ErrorReport>
22+
<WarningLevel>4</WarningLevel>
23+
</PropertyGroup>
24+
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
25+
<DebugType>pdbonly</DebugType>
26+
<Optimize>true</Optimize>
27+
<OutputPath>bin\Release\</OutputPath>
28+
<DefineConstants>TRACE</DefineConstants>
29+
<ErrorReport>prompt</ErrorReport>
30+
<WarningLevel>4</WarningLevel>
31+
</PropertyGroup>
32+
<ItemGroup>
33+
<Reference Include="DotNetCqs, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
34+
<HintPath>..\packages\DotNetCqs.1.0.0\lib\net45\DotNetCqs.dll</HintPath>
35+
<Private>True</Private>
36+
</Reference>
37+
<Reference Include="FluentAssertions, Version=4.14.0.0, Culture=neutral, PublicKeyToken=33f2691a05b67b6a, processorArchitecture=MSIL">
38+
<HintPath>..\packages\FluentAssertions.4.14.0\lib\net45\FluentAssertions.dll</HintPath>
39+
<Private>True</Private>
40+
</Reference>
41+
<Reference Include="FluentAssertions.Core, Version=4.14.0.0, Culture=neutral, PublicKeyToken=33f2691a05b67b6a, processorArchitecture=MSIL">
42+
<HintPath>..\packages\FluentAssertions.4.14.0\lib\net45\FluentAssertions.Core.dll</HintPath>
43+
<Private>True</Private>
44+
</Reference>
45+
<Reference Include="System" />
46+
<Reference Include="System.Core" />
47+
<Reference Include="System.Xml.Linq" />
48+
<Reference Include="System.Data.DataSetExtensions" />
49+
<Reference Include="Microsoft.CSharp" />
50+
<Reference Include="System.Data" />
51+
<Reference Include="System.Net.Http" />
52+
<Reference Include="System.Xml" />
53+
<Reference Include="xunit.abstractions, Version=2.0.0.0, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL">
54+
<HintPath>..\packages\xunit.abstractions.2.0.0\lib\net35\xunit.abstractions.dll</HintPath>
55+
<Private>True</Private>
56+
</Reference>
57+
<Reference Include="xunit.assert, Version=2.1.0.3179, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL">
58+
<HintPath>..\packages\xunit.assert.2.1.0\lib\dotnet\xunit.assert.dll</HintPath>
59+
<Private>True</Private>
60+
</Reference>
61+
<Reference Include="xunit.core, Version=2.1.0.3179, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL">
62+
<HintPath>..\packages\xunit.extensibility.core.2.1.0\lib\dotnet\xunit.core.dll</HintPath>
63+
<Private>True</Private>
64+
</Reference>
65+
<Reference Include="xunit.execution.desktop, Version=2.1.0.3179, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL">
66+
<HintPath>..\packages\xunit.extensibility.execution.2.1.0\lib\net45\xunit.execution.desktop.dll</HintPath>
67+
<Private>True</Private>
68+
</Reference>
69+
</ItemGroup>
70+
<ItemGroup>
71+
<Compile Include="TryTheClient.cs" />
72+
<Compile Include="Properties\AssemblyInfo.cs" />
73+
</ItemGroup>
74+
<ItemGroup>
75+
<None Include="packages.config">
76+
<SubType>Designer</SubType>
77+
</None>
78+
</ItemGroup>
79+
<ItemGroup>
80+
<ProjectReference Include="..\OneTrueError.Api.Client\OneTrueError.Api.Client.csproj">
81+
<Project>{017F8863-3DE0-4AD2-9ED3-5ACB87BBBCD0}</Project>
82+
<Name>OneTrueError.Api.Client</Name>
83+
</ProjectReference>
84+
<ProjectReference Include="..\OneTrueError.Api\OneTrueError.Api.csproj">
85+
<Project>{fc331a95-fca4-4764-8004-0884665dd01f}</Project>
86+
<Name>OneTrueError.Api</Name>
87+
</ProjectReference>
88+
</ItemGroup>
89+
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
90+
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
91+
Other similar extension points exist, see Microsoft.Common.targets.
92+
<Target Name="BeforeBuild">
93+
</Target>
94+
<Target Name="AfterBuild">
95+
</Target>
96+
-->
97+
</Project>
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
using System.Reflection;
2+
using System.Runtime.CompilerServices;
3+
using System.Runtime.InteropServices;
4+
5+
// General Information about an assembly is controlled through the following
6+
// set of attributes. Change these attribute values to modify the information
7+
// associated with an assembly.
8+
[assembly: AssemblyTitle("OneTrueError.Api.Client.Tests")]
9+
[assembly: AssemblyDescription("")]
10+
[assembly: AssemblyConfiguration("")]
11+
[assembly: AssemblyCompany("")]
12+
[assembly: AssemblyProduct("OneTrueError.Api.Client.Tests")]
13+
[assembly: AssemblyCopyright("Copyright © 2016")]
14+
[assembly: AssemblyTrademark("")]
15+
[assembly: AssemblyCulture("")]
16+
17+
// Setting ComVisible to false makes the types in this assembly not visible
18+
// to COM components. If you need to access a type in this assembly from
19+
// COM, set the ComVisible attribute to true on that type.
20+
[assembly: ComVisible(false)]
21+
22+
// The following GUID is for the ID of the typelib if this project is exposed to COM
23+
[assembly: Guid("62989500-31bc-44fa-97ca-84f484c6f1aa")]
24+
25+
// Version information for an assembly consists of the following four values:
26+
//
27+
// Major Version
28+
// Minor Version
29+
// Build Number
30+
// Revision
31+
//
32+
// You can specify all the values or you can default the Build and Revision Numbers
33+
// by using the '*' as shown below:
34+
// [assembly: AssemblyVersion("1.0.*")]
35+
[assembly: AssemblyVersion("1.0.0.0")]
36+
[assembly: AssemblyFileVersion("1.0.0.0")]
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Net;
5+
using System.Text;
6+
using System.Threading.Tasks;
7+
using FluentAssertions;
8+
using OneTrueError.Api.Core.Accounts.Queries;
9+
using Xunit;
10+
11+
namespace OneTrueError.Api.Client.Tests
12+
{
13+
#if DEBUG
14+
public class TryTheClient
15+
{
16+
[Fact]
17+
public async Task Test()
18+
{
19+
OneTrueClient client = new OneTrueClient();
20+
client.Credentials = new NetworkCredential("jonas", "123456");
21+
client.Open(new Uri("http://localhost/onetrueerror/"));
22+
FindAccountByUserNameResult result = null;
23+
try
24+
{
25+
result = await client.QueryAsync(new FindAccountByUserName("admin"));
26+
}
27+
catch (WebException ex)
28+
{
29+
30+
}
31+
32+
33+
result.Should().NotBeNull();
34+
}
35+
}
36+
#endif
37+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<packages>
3+
<package id="DotNetCqs" version="1.0.0" targetFramework="net452" />
4+
<package id="FluentAssertions" version="4.14.0" targetFramework="net452" />
5+
<package id="xunit" version="2.1.0" targetFramework="net452" />
6+
<package id="xunit.abstractions" version="2.0.0" targetFramework="net452" />
7+
<package id="xunit.assert" version="2.1.0" targetFramework="net452" />
8+
<package id="xunit.core" version="2.1.0" targetFramework="net452" />
9+
<package id="xunit.extensibility.core" version="2.1.0" targetFramework="net452" />
10+
<package id="xunit.extensibility.execution" version="2.1.0" targetFramework="net452" />
11+
</packages>
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
using System.Reflection;
2+
using Newtonsoft.Json;
3+
using Newtonsoft.Json.Serialization;
4+
5+
namespace OneTrueError.Api.Client.Json
6+
{
7+
/// <summary>
8+
/// Allows us to serialize properties with private setters.
9+
/// </summary>
10+
internal class IncludeNonPublicMembersContractResolver : DefaultContractResolver
11+
{
12+
/// <summary>
13+
/// Creates a <see cref="T:Newtonsoft.Json.Serialization.JsonProperty" /> for the given
14+
/// <see cref="T:System.Reflection.MemberInfo" />.
15+
/// </summary>
16+
/// <param name="memberSerialization">The member's parent <see cref="T:Newtonsoft.Json.MemberSerialization" />.</param>
17+
/// <param name="member">The member to create a <see cref="T:Newtonsoft.Json.Serialization.JsonProperty" /> for.</param>
18+
/// <returns>
19+
/// A created <see cref="T:Newtonsoft.Json.Serialization.JsonProperty" /> for the given
20+
/// <see cref="T:System.Reflection.MemberInfo" />.
21+
/// </returns>
22+
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
23+
{
24+
//TODO: Maybe cache
25+
var prop = base.CreateProperty(member, memberSerialization);
26+
27+
if (!prop.Writable)
28+
{
29+
var property = member as PropertyInfo;
30+
if (property != null)
31+
{
32+
var hasPrivateSetter = property.GetSetMethod(true) != null;
33+
prop.Writable = hasPrivateSetter;
34+
}
35+
}
36+
37+
return prop;
38+
}
39+
}
40+
}
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
using System;
2+
using System.Collections.Specialized;
3+
using System.Net;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
using DotNetCqs;
7+
using Newtonsoft.Json;
8+
using OneTrueError.Api.Client.Json;
9+
using OneTrueError.Api.Client.Tests;
10+
11+
namespace OneTrueError.Api.Client
12+
{
13+
/// <summary>
14+
/// Client for the OneTrueError server API
15+
/// </summary>
16+
public class OneTrueClient : IQueryBus, ICommandBus, IEventBus
17+
{
18+
private CookieContainer _cookies;
19+
private Encoding _basicAuthEncoding = Encoding.GetEncoding("ISO-8859-1");
20+
21+
private readonly JsonSerializerSettings _jsonSerializerSettings = new JsonSerializerSettings
22+
{
23+
ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor,
24+
Formatting = Formatting.Indented
25+
};
26+
27+
private Uri _uri;
28+
29+
30+
public OneTrueClient()
31+
{
32+
_jsonSerializerSettings.ContractResolver = new IncludeNonPublicMembersContractResolver();
33+
}
34+
35+
public NetworkCredential Credentials { get; set; }
36+
37+
public async Task ExecuteAsync<T>(T command) where T : Command
38+
{
39+
var response = await RequestAsync("POST", "command", command);
40+
response.Close();
41+
}
42+
43+
public async Task PublishAsync<TApplicationEvent>(TApplicationEvent e)
44+
where TApplicationEvent : ApplicationEvent
45+
{
46+
var response = await RequestAsync("POST", "event", e);
47+
response.Close();
48+
}
49+
50+
public async Task<TResult> QueryAsync<TResult>(Query<TResult> query)
51+
{
52+
//TODO: Unwrap the cqs object to query parameters instead
53+
//to allow caching in the server
54+
var response = await RequestAsync("POST", "query", query);
55+
return await DeserializeResponse<TResult>(response);
56+
}
57+
58+
public void Open(Uri uri)
59+
{
60+
if (uri == null) throw new ArgumentNullException(nameof(uri));
61+
62+
_uri = uri;
63+
_cookies = new CookieContainer();
64+
}
65+
66+
private async Task<TResult> DeserializeResponse<TResult>(HttpWebResponse response)
67+
{
68+
var responseStream = response.GetResponseStream();
69+
var jsonBuf = new byte[responseStream.Length];
70+
await responseStream.ReadAsync(jsonBuf, 0, jsonBuf.Length);
71+
var jsonStr = Encoding.UTF8.GetString(jsonBuf);
72+
var responseObj = JsonConvert.DeserializeObject(jsonStr, typeof(TResult), _jsonSerializerSettings);
73+
return (TResult)responseObj;
74+
}
75+
76+
private async Task<HttpWebResponse> RequestAsync(string httpMethod, string cqsType, object cqsObject)
77+
{
78+
if (_cookies.Count == 0)
79+
{
80+
var sb = new StringBuilder();
81+
sb.AppendUrlEncoded("username", Credentials.UserName);
82+
sb.AppendUrlEncoded("password", Credentials.Password);
83+
var data = Encoding.UTF8.GetBytes(sb.ToString());
84+
85+
var authRequest = WebRequest.CreateHttp(_uri + "/account/login");
86+
authRequest.Method = "POST";
87+
authRequest.CookieContainer = _cookies;
88+
authRequest.ContentType = "application/x-www-form-urlencoded";
89+
authRequest.ContentLength = data.Length;
90+
var authReqStream = await authRequest.GetRequestStreamAsync();
91+
await authReqStream.WriteAsync(data, 0, data.Length);
92+
var resp = authRequest.GetResponse();
93+
94+
}
95+
string authInfo = Credentials.UserName + ":" + Credentials.Password;
96+
authInfo = Convert.ToBase64String(_basicAuthEncoding.GetBytes(authInfo));
97+
98+
var request = WebRequest.CreateHttp(_uri + "api/cqs");
99+
request.Method = httpMethod;
100+
request.Headers.Add("Authorization", "Basic " + authInfo);
101+
request.Headers.Add("X-Cqs-Name", cqsObject.GetType().Name);
102+
//request.PreAuthenticate = true;
103+
request.CookieContainer = _cookies;
104+
105+
var stream = await request.GetRequestStreamAsync();
106+
var json = JsonConvert.SerializeObject(cqsObject, _jsonSerializerSettings);
107+
var buffer = Encoding.UTF8.GetBytes(json);
108+
await stream.WriteAsync(buffer, 0, buffer.Length);
109+
110+
return (HttpWebResponse)await request.GetResponseAsync();
111+
}
112+
}
113+
}

0 commit comments

Comments
 (0)