Skip to content

Commit ea5323d

Browse files
author
ducksoop
committed
2 parents cf80f0d + a9a6fe8 commit ea5323d

File tree

13 files changed

+367
-20
lines changed

13 files changed

+367
-20
lines changed
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
name: .NET CI/CD
2+
3+
on:
4+
push:
5+
branches: [ master ]
6+
pull_request:
7+
branches: [ master ]
8+
9+
jobs:
10+
build-and-deploy:
11+
runs-on: ubuntu-latest
12+
13+
steps:
14+
- uses: actions/checkout@v2
15+
16+
- name: Setup .NET
17+
uses: actions/setup-dotnet@v1
18+
with:
19+
dotnet-version: '8.0.x'
20+
21+
- name: Restore dependencies
22+
run: dotnet restore
23+
24+
- name: Build
25+
run: dotnet build --no-restore
26+
27+
- name: Test
28+
run: dotnet test --no-build --verbosity normal
29+
30+
- name: Pack
31+
run: dotnet pack -c Release -o out
32+
33+
- name: Push to NuGet
34+
run: dotnet nuget push out/*.nupkg -k ${{secrets.NUGET_API_KEY}} -s https://api.nuget.org/v3/index.json
35+
if: github.ref == 'refs/heads/master' && github.event_name == 'push'

QuickbaseNet.Examples/Program.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ static async Task Main(string[] args)
1313
var query = new QuickbaseQueryBuilder()
1414
.From("bmycek2xq")
1515
.Select(3, 7, 14, 75, 150, 157, 354, 355, 367, 538, 539, 540, 541, 542, 543)
16-
.Where("{'7'.'CT'.'10136'}")
16+
.Where("{'7'.'EX'.'10136'}")
1717
.Build();
1818

1919
string jsonRequest = JsonConvert.SerializeObject(query);
@@ -22,12 +22,12 @@ static async Task Main(string[] args)
2222
if (result.IsSuccess)
2323
{
2424
Console.WriteLine("Success!");
25-
Console.WriteLine(JsonConvert.SerializeObject(result.Response, Formatting.Indented));
25+
Console.WriteLine(JsonConvert.SerializeObject(result.Value, Formatting.Indented));
2626
}
2727
else
2828
{
2929
Console.WriteLine("Error!");
30-
Console.WriteLine(JsonConvert.SerializeObject(result.Error, Formatting.Indented));
30+
Console.WriteLine(JsonConvert.SerializeObject(result.QuickbaseError, Formatting.Indented));
3131
}
3232

3333
// var recordBuilder = new QuickbaseCommandBuilder()
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
global using Xunit;
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
using System.Net;
2+
3+
namespace QuickbaseNet.UnitTests.Mocks;
4+
5+
public class MockHttpMessageHandler : HttpMessageHandler
6+
{
7+
public string ResponseContent { get; set; }
8+
public HttpStatusCode ResponseStatusCode { get; set; }
9+
10+
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
11+
{
12+
return await Task.FromResult(new HttpResponseMessage
13+
{
14+
StatusCode = ResponseStatusCode,
15+
Content = new StringContent(ResponseContent)
16+
});
17+
}
18+
}
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
using System.Net;
2+
using System.Text.Json;
3+
using Bogus;
4+
using QuickbaseNet.Requests;
5+
using QuickbaseNet.Responses;
6+
using QuickbaseNet.Services;
7+
using QuickbaseNet.UnitTests.Mocks;
8+
9+
namespace QuickbaseNet.UnitTests.QuickbaseClientTests;
10+
11+
public class QuickbaseClientTests
12+
{
13+
private readonly MockHttpMessageHandler _mockHandler;
14+
private readonly QuickbaseClient _client;
15+
private const string TestRealm = "testRealm";
16+
private const string TestToken = "testToken";
17+
private const string BaseUrl = "http://localhost/";
18+
19+
public QuickbaseClientTests()
20+
{
21+
_mockHandler = SetupMockHandlerWithSuccessResponse();
22+
_client = CreateConfiguredQuickbaseClient();
23+
}
24+
25+
[Fact]
26+
public async Task QueryRecords_ReturnsSuccessResponse_WhenCalled()
27+
{
28+
// Arrange
29+
var request = new QuickbaseQueryRequest();
30+
31+
// Act
32+
var response = await _client.QueryRecords(request);
33+
34+
// Assert
35+
Assert.True(response.IsSuccess);
36+
Assert.NotNull(response.Value);
37+
Assert.NotEmpty(response.Value.Data);
38+
Assert.NotEmpty(response.Value.Fields);
39+
Assert.NotNull(response.Value.Metadata);
40+
}
41+
42+
[Fact]
43+
public async Task QueryRecords_ReturnsErrorResponse_WhenBadRequestOccurs()
44+
{
45+
// Arrange
46+
SetupMockHandlerWithErrorResponse();
47+
var request = new QuickbaseQueryRequest
48+
{
49+
From = "tableId",
50+
Where = "{1.CT.'query'}",
51+
Select = [1, 2, 3]
52+
};
53+
54+
// Act
55+
var actualResponse = await _client.QueryRecords(request);
56+
57+
// Assert
58+
Assert.False(actualResponse.IsSuccess);
59+
Assert.NotNull(actualResponse.QuickbaseError);
60+
}
61+
62+
#region Helpers
63+
private void SetupMockHandlerWithErrorResponse()
64+
{
65+
var faker = new Faker();
66+
var errorResponse = new QuickbaseErrorResponse
67+
{
68+
Message = faker.Random.Words(),
69+
Description = faker.Lorem.Paragraph()
70+
};
71+
72+
_mockHandler.ResponseContent = JsonSerializer.Serialize(errorResponse);
73+
_mockHandler.ResponseStatusCode = HttpStatusCode.BadRequest;
74+
}
75+
76+
private MockHttpMessageHandler SetupMockHandlerWithSuccessResponse()
77+
{
78+
var mockJsonResponse = GetMockJsonResponse();
79+
return new MockHttpMessageHandler
80+
{
81+
ResponseContent = mockJsonResponse,
82+
ResponseStatusCode = HttpStatusCode.OK
83+
};
84+
}
85+
86+
private QuickbaseClient CreateConfiguredQuickbaseClient()
87+
{
88+
var httpClient = new HttpClient(_mockHandler)
89+
{
90+
BaseAddress = new Uri(BaseUrl)
91+
};
92+
return new QuickbaseClient(TestRealm, TestToken)
93+
{
94+
Client = httpClient
95+
};
96+
}
97+
98+
private static string GetMockJsonResponse()
99+
{
100+
// JSON response string...
101+
return @"{
102+
""data"": [
103+
{
104+
""6"": { ""value"": ""Andre Harris"" },
105+
""7"": { ""value"": 10 },
106+
""8"": { ""value"": ""2019-12-18T08:00:00Z"" }
107+
}
108+
],
109+
""fields"": [
110+
{ ""id"": 6, ""label"": ""Full Name"", ""type"": ""text"" },
111+
{ ""id"": 7, ""label"": ""Amount"", ""type"": ""numeric"" },
112+
{ ""id"": 8, ""label"": ""Date time"", ""type"": ""date time"" }
113+
],
114+
""metadata"": {
115+
""totalRecords"": 10,
116+
""numRecords"": 1,
117+
""numFields"": 3,
118+
""skip"": 0
119+
}
120+
}";
121+
}
122+
123+
#endregion
124+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net8.0</TargetFramework>
5+
<ImplicitUsings>enable</ImplicitUsings>
6+
<Nullable>enable</Nullable>
7+
8+
<IsPackable>false</IsPackable>
9+
<IsTestProject>true</IsTestProject>
10+
</PropertyGroup>
11+
12+
<ItemGroup>
13+
<PackageReference Include="Bogus" Version="35.4.0" />
14+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.0" />
15+
<PackageReference Include="Moq" Version="4.20.70" />
16+
<PackageReference Include="xunit" Version="2.4.2" />
17+
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
18+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
19+
<PrivateAssets>all</PrivateAssets>
20+
</PackageReference>
21+
<PackageReference Include="coverlet.collector" Version="6.0.0">
22+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
23+
<PrivateAssets>all</PrivateAssets>
24+
</PackageReference>
25+
</ItemGroup>
26+
27+
<ItemGroup>
28+
<ProjectReference Include="..\QuickbaseNet\QuickbaseNet.csproj" />
29+
</ItemGroup>
30+
31+
<ItemGroup>
32+
<Folder Include="Mocks\" />
33+
</ItemGroup>
34+
35+
</Project>

QuickbaseNet.sln

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@ Microsoft Visual Studio Solution File, Format Version 12.00
33
# Visual Studio Version 17
44
VisualStudioVersion = 17.8.34322.80
55
MinimumVisualStudioVersion = 10.0.40219.1
6-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QuickbaseNet", "QuickbaseNet\QuickbaseNet.csproj", "{375B33E5-C837-4915-844C-52057055E84C}"
6+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "QuickbaseNet", "QuickbaseNet\QuickbaseNet.csproj", "{375B33E5-C837-4915-844C-52057055E84C}"
77
EndProject
8-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QuickbaseNet.Examples", "QuickbaseNet.Examples\QuickbaseNet.Examples.csproj", "{F92A2FF7-450E-4672-8781-BC648ACE2ACF}"
8+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "QuickbaseNet.Examples", "QuickbaseNet.Examples\QuickbaseNet.Examples.csproj", "{F92A2FF7-450E-4672-8781-BC648ACE2ACF}"
9+
EndProject
10+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QuickbaseNet.UnitTests", "QuickbaseNet.UnitTests\QuickbaseNet.UnitTests.csproj", "{E2654CA5-971C-43D0-912E-D4445F1EB4B0}"
911
EndProject
1012
Global
1113
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -21,6 +23,10 @@ Global
2123
{F92A2FF7-450E-4672-8781-BC648ACE2ACF}.Debug|Any CPU.Build.0 = Debug|Any CPU
2224
{F92A2FF7-450E-4672-8781-BC648ACE2ACF}.Release|Any CPU.ActiveCfg = Release|Any CPU
2325
{F92A2FF7-450E-4672-8781-BC648ACE2ACF}.Release|Any CPU.Build.0 = Release|Any CPU
26+
{E2654CA5-971C-43D0-912E-D4445F1EB4B0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
27+
{E2654CA5-971C-43D0-912E-D4445F1EB4B0}.Debug|Any CPU.Build.0 = Debug|Any CPU
28+
{E2654CA5-971C-43D0-912E-D4445F1EB4B0}.Release|Any CPU.ActiveCfg = Release|Any CPU
29+
{E2654CA5-971C-43D0-912E-D4445F1EB4B0}.Release|Any CPU.Build.0 = Release|Any CPU
2430
EndGlobalSection
2531
GlobalSection(SolutionProperties) = preSolution
2632
HideSolutionNode = FALSE
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
namespace QuickbaseNet.Errors
2+
{
3+
public class QuickbaseError
4+
{
5+
public static readonly QuickbaseError None = new QuickbaseError(string.Empty, string.Empty, string.Empty, QuickbaseErrorType.Failure);
6+
public static readonly QuickbaseError NullValue = new QuickbaseError("Error.NullValue", "Null value was provided", string.Empty, QuickbaseErrorType.Failure);
7+
8+
public QuickbaseError(string code, string message, string description, QuickbaseErrorType quickbaseErrorType)
9+
{
10+
Code = code;
11+
Message = message;
12+
Description = description;
13+
Type = quickbaseErrorType;
14+
}
15+
16+
public string Code { get; private set; }
17+
18+
public string Message { get; private set; }
19+
20+
public string Description { get; private set; }
21+
22+
public QuickbaseErrorType Type { get; private set; }
23+
24+
public static QuickbaseError NotFound(string code, string message, string description) =>
25+
new QuickbaseError(code, message, description, QuickbaseErrorType.NotFound);
26+
27+
public static QuickbaseError Failure(string code, string message, string description) =>
28+
new QuickbaseError(code, message, description, QuickbaseErrorType.Failure);
29+
30+
public static QuickbaseError ClientError(string code, string message, string description) =>
31+
new QuickbaseError(code, message, description, QuickbaseErrorType.ClientError);
32+
33+
public static QuickbaseError ServerError(string code, string message, string description) =>
34+
new QuickbaseError(code, message, description, QuickbaseErrorType.ServerError);
35+
}
36+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
namespace QuickbaseNet.Errors
2+
{
3+
public enum QuickbaseErrorType
4+
{
5+
Failure = 0,
6+
NotFound = 1,
7+
ClientError = 3,
8+
ServerError = 4
9+
}
10+
}

QuickbaseNet/Models/FieldValue.cs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
11
using Newtonsoft.Json;
2+
using System;
23

34
namespace QuickbaseNet.Models
45
{
56
public class FieldValue
67
{
7-
[JsonProperty("value")]
8-
public dynamic Value { get; set; }
8+
[JsonProperty("value", NullValueHandling = NullValueHandling.Ignore)]
9+
public object Value { get; set; }
10+
11+
public T GetValue<T>()
12+
{
13+
return Value == null ? default : (T)Convert.ChangeType(Value, typeof(T));
14+
}
915
}
1016
}

0 commit comments

Comments
 (0)