Skip to content

Commit

Permalink
Hoist API clients to top-level client so that docs snippets work (#45)
Browse files Browse the repository at this point in the history
  • Loading branch information
bpapillon authored Mar 3, 2025
1 parent 6536f8f commit 6e60814
Show file tree
Hide file tree
Showing 4 changed files with 232 additions and 2 deletions.
1 change: 1 addition & 0 deletions .fernignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
README.md

src/SchematicHQ.Client.Test/TestCache.cs
src/SchematicHQ.Client.Test/TestCompaniesClient.cs
src/SchematicHQ.Client.Test/TestEventBuffer.cs
src/SchematicHQ.Client.Test/TestClient.cs
src/SchematicHQ.Client.Test/TestLogger.cs
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ Schematic schematic = new Schematic("YOUR_API_KEY");
// Creating and updating companies
async Task UpsertCompanyExample()
{
var response = await schematic.API.Companies.UpsertCompanyAsync(new UpsertCompanyRequestBody
var response = await schematic.Companies.UpsertCompanyAsync(new UpsertCompanyRequestBody
{
Keys = new Dictionary<string, string> { { "id", "your-company-id" } },
Name = "Acme Widgets, Inc.",
Expand Down Expand Up @@ -136,7 +136,7 @@ Schematic schematic = new Schematic("YOUR_API_KEY");
// Creating and updating users
async Task UpsertUserExample()
{
var response = await schematic.API.Companies.UpsertUserAsync(new UpsertUserRequestBody
var response = await schematic.Companies.UpsertUserAsync(new UpsertUserRequestBody
{
Keys = new Dictionary<string, string>
{
Expand Down
202 changes: 202 additions & 0 deletions src/SchematicHQ.Client.Test/TestCompaniesClient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
using Moq;
using NUnit.Framework;
using System.Net;
using Moq.Contrib.HttpClient;
using System.Text.Json;
using System.Text;
using SchematicHQ.Client.Core;

namespace SchematicHQ.Client.Test
{
[TestFixture]
public class CompaniesClientTests
{
private Schematic _schematic;
private ClientOptions _options;
private Mock<ISchematicLogger> _logger;

private HttpResponseMessage CreateUpsertCompanyResponse(HttpStatusCode code)
{
var response = new UpsertCompanyResponse
{
Data = new CompanyDetailResponseData
{
Id = "test-company-id",
Name = "Acme Widgets, Inc.",
CreatedAt = DateTime.UtcNow,
UpdatedAt = DateTime.UtcNow,
EnvironmentId = "test-env-id",
UserCount = 1
}
};
var serializedResponse = JsonSerializer.Serialize(response, new JsonSerializerOptions { WriteIndented = true });
return new HttpResponseMessage(code)
{
Content = new StringContent(serializedResponse, Encoding.UTF8, "application/json")
};
}

private HttpResponseMessage CreateUpsertUserResponse(HttpStatusCode code)
{
var response = new UpsertUserResponse
{
Data = new UserDetailResponseData
{
Id = "test-user-id",
Name = "Wile E. Coyote",
CreatedAt = DateTime.UtcNow,
UpdatedAt = DateTime.UtcNow,
EnvironmentId = "test-env-id"
}
};
var serializedResponse = JsonSerializer.Serialize(response, new JsonSerializerOptions { WriteIndented = true });
return new HttpResponseMessage(code)
{
Content = new StringContent(serializedResponse, Encoding.UTF8, "application/json")
};
}

private void SetupSchematicTestClient(HttpResponseMessage response)
{
_logger = new Mock<ISchematicLogger>();
_options = new ClientOptions
{
Logger = _logger.Object
};

var handler = new Mock<HttpMessageHandler>(MockBehavior.Strict);
var testClient = handler.CreateClient();

handler.SetupAnyRequest()
.ReturnsAsync(response);

_schematic = new Schematic("dummy_api_key", _options.WithHttpClient(testClient));
}

[TearDown]
public async Task TearDown()
{
if (_schematic != null) await _schematic.Shutdown();
}

[Test]
public async Task UpsertCompany_CanAccessThroughHoistedClient()
{
// Arrange
SetupSchematicTestClient(CreateUpsertCompanyResponse(HttpStatusCode.OK));
var request = new UpsertCompanyRequestBody
{
Keys = new Dictionary<string, string> { { "id", "your-company-id" } },
Name = "Acme Widgets, Inc.",
Traits = new Dictionary<string, object>
{
{ "city", "Atlanta" },
{ "high_score", 25 },
{ "is_active", true }
}
};

// Act
// This test verifies we can access Companies directly on Schematic instance
// rather than going through API.Companies
var response = await _schematic.Companies.UpsertCompanyAsync(request);

// Assert
Assert.That(response, Is.Not.Null);
Assert.That(response.Data, Is.Not.Null);
Assert.That(response.Data.Id, Is.EqualTo("test-company-id"));
Assert.That(response.Data.Name, Is.EqualTo("Acme Widgets, Inc."));
}

[Test]
public async Task UpsertUser_CanAccessThroughHoistedClient()
{
// Arrange
SetupSchematicTestClient(CreateUpsertUserResponse(HttpStatusCode.OK));
var request = new UpsertUserRequestBody
{
Keys = new Dictionary<string, string>
{
{ "email", "[email protected]" },
{ "user_id", "your-user-id" }
},
Name = "Wile E. Coyote",
Traits = new Dictionary<string, object>
{
{ "city", "Atlanta" },
{ "high_score", 25 },
{ "is_active", true }
},
Company = new Dictionary<string, string> { { "id", "your-company-id" } }
};

// Act
// This test verifies we can access Companies directly on Schematic instance
// rather than going through API.Companies
var response = await _schematic.Companies.UpsertUserAsync(request);

// Assert
Assert.That(response, Is.Not.Null);
Assert.That(response.Data, Is.Not.Null);
Assert.That(response.Data.Id, Is.EqualTo("test-user-id"));
Assert.That(response.Data.Name, Is.EqualTo("Wile E. Coyote"));
}

[Test]
public async Task UpsertCompany_ExampleFromReadme()
{
// Arrange
SetupSchematicTestClient(CreateUpsertCompanyResponse(HttpStatusCode.OK));

// Act
// This mimics the example code from the README.md
var response = await _schematic.Companies.UpsertCompanyAsync(new UpsertCompanyRequestBody
{
Keys = new Dictionary<string, string> { { "id", "your-company-id" } },
Name = "Acme Widgets, Inc.",
Traits = new Dictionary<string, object>
{
{ "city", "Atlanta" },
{ "high_score", 25 },
{ "is_active", true }
}
});

// Assert
Assert.That(response, Is.Not.Null);
Assert.That(response.Data, Is.Not.Null);
Assert.That(response.Data.Name, Is.EqualTo("Acme Widgets, Inc."));
}

[Test]
public async Task UpsertUser_ExampleFromReadme()
{
// Arrange
SetupSchematicTestClient(CreateUpsertUserResponse(HttpStatusCode.OK));

// Act
// This mimics the example code from the README.md
var response = await _schematic.Companies.UpsertUserAsync(new UpsertUserRequestBody
{
Keys = new Dictionary<string, string>
{
{ "email", "[email protected]" },
{ "user_id", "your-user-id" }
},
Name = "Wile E. Coyote",
Traits = new Dictionary<string, object>
{
{ "city", "Atlanta" },
{ "high_score", 25 },
{ "is_active", true }
},
Company = new Dictionary<string, string> { { "id", "your-company-id" } }
});

// Assert
Assert.That(response, Is.Not.Null);
Assert.That(response.Data, Is.Not.Null);
Assert.That(response.Data.Name, Is.EqualTo("Wile E. Coyote"));
}
}
}
27 changes: 27 additions & 0 deletions src/SchematicHQ.Client/Schematic.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,20 @@ public partial class Schematic
private readonly bool _offline;
public readonly SchematicApi API;

public AccesstokensClient Accesstokens { get; init; }
public AccountsClient Accounts { get; init; }
public BillingClient Billing { get; init; }
public CheckoutClient Checkout { get; init; }
public CompaniesClient Companies { get; init; }
public ComponentsClient Components { get; init; }
public CrmClient Crm { get; init; }
public EntitlementsClient Entitlements { get; init; }
public EventsClient Events { get; init; }
public FeaturesClient Features { get; init; }
public PlangroupsClient Plangroups { get; init; }
public PlansClient Plans { get; init; }
public WebhooksClient Webhooks { get; init; }

public Schematic(string apiKey, ClientOptions? options = null)
{
_options = options ?? new ClientOptions();
Expand All @@ -25,6 +39,19 @@ public Schematic(string apiKey, ClientOptions? options = null)

var httpClient = _offline ? new HttpClient(new OfflineHttpMessageHandler()) : _options.HttpClient;
API = new SchematicApi(apiKey, _options.WithHttpClient(httpClient));
Accesstokens = API.Accesstokens;
Accounts = API.Accounts;
Billing = API.Billing;
Checkout = API.Checkout;
Companies = API.Companies;
Components = API.Components;
Crm = API.Crm;
Entitlements = API.Entitlements;
Events = API.Events;
Features = API.Features;
Plangroups = API.Plangroups;
Plans = API.Plans;
Webhooks = API.Webhooks;

_eventBuffer = _options.EventBuffer ?? new EventBuffer<CreateEventRequestBody>(
async items =>
Expand Down

0 comments on commit 6e60814

Please sign in to comment.