Search

Call API v2 using an automatically generated API client

This article explains how to generate a Emply API v2 client in .NET/C#.

This examples uses NSwag with Newtonsoft.JSON as JSON library.

Note

For other languages, you can use the tools listed here: OpenAPI.Tools - an Open Source list of great tools for OpenAPI.

You can also generate an API client using an AI agent. An example of a prompt:

Create a .NET console application that has an API client,
generated using NSwag (NewtonsoftJson as JsonLibrary),
that connects to the following endpoint: 
https://api.emply.com/v2/swagger/swagger.json
  1. Install NSwag.

    dotnet tool install nswag.consolecore
  2. Generate an API client using Nswag-

    nswag openapi2csclient /input:https://api.emply.com/v2/swagger/swagger.json /classname:EmplyApiClient /namespace:Emply /output:Emply/EmplyApiClient.cs /GenerateClientInterfaces:true /GenerateExceptionClasses:true /ExceptionClass:EmplyApiException /UseHttpClientCreationMethod:true /JsonLibrary:NewtonsoftJson /DateTimeType:System.DateTime
  3. Install Newtonsoft.JSON NuGet package.

    dotnet add package Newtonsoft.Json
  4. Currently there’s a bug in NSwag that prevents some of the API endpoints to be invoked correctly. For that reason a workaround should be applied.

    1. Create a C# file “NswagPolymorphicSerializationFix.cs" with the following content.

      using System.Collections.Concurrent;
      using System.Reflection;
      
      using Newtonsoft.Json.Serialization;
      using Newtonsoft.Json;
      
      namespace Emply
      {
              public static class NswagPolymorphicSerializationFix
              {
                      /// <summary>
                      /// Fixes a polymorphic serialization bug in code generated by NSwag: https://github.com/RicoSuter/NSwag/issues/3217
                      /// </summary>
                      public static CustomResolver GetPatchedContractResolver(this Type apiClientType)
                      {
                              Assembly asm = apiClientType.Assembly;
                              string @namespace = apiClientType.Namespace ?? string.Empty;
      
                              IEnumerable<(Type type, string discriminatorField)> polymorphicTypes =
                                      from type in asm.GetExportedTypes()
                                      where type.IsClass && type.Namespace == @namespace
                                      let jsonConverter = type.GetCustomAttribute<JsonConverterAttribute>(false)
                                      where jsonConverter != null && jsonConverter.ConverterType == typeof(JsonInheritanceConverter)
                                      let discriminatorField = jsonConverter.ConverterParameters?.OfType<string>().FirstOrDefault()
                                      where discriminatorField != null
                                      select (type, discriminatorField);
      
                              Dictionary<Type, JsonConverter> converters = polymorphicTypes.ToDictionary(
                                      t => t.type,
                                      t => (JsonConverter) Activator.CreateInstance(typeof(PatchedJsonInheritanceConverter<>).MakeGenericType(t.type),
                                              t.discriminatorField));
      
                              return new CustomResolver(converters);
                      }
      
                      public class CustomResolver : DefaultContractResolver
                      {
                              private readonly Dictionary<Type, JsonConverter> _converters;
      
                              public CustomResolver(Dictionary<Type, JsonConverter> converters)
                              {
                                      _converters = converters;
                              }
      
                              protected override JsonObjectContract CreateObjectContract(Type objectType)
                              {
                                      JsonObjectContract contract = base.CreateObjectContract(objectType);
                                      Type? baseType = objectType.BaseType;
      
                                      if (baseType != null && _converters.TryGetValue(baseType, out JsonConverter converter))
                                      {
                                              contract.Converter = converter;
                                      }
      
                                      return contract;
                              }
                      }
      
                      public class PatchedJsonInheritanceConverter<TBaseType> : JsonInheritanceConverter
                      {
                              [System.ThreadStatic]
                              private static bool _isWritingWrapper;
      
                              private static readonly ConcurrentDictionary<Type, string> _subtypeDiscriminatorCache = new();
      
                              public PatchedJsonInheritanceConverter(string discriminatorName) : base(discriminatorName)
                              {
                              }
      
                              public override bool CanConvert(Type objectType)
                              {
                                      return objectType.IsClass && objectType.IsSubclassOf(typeof(TBaseType));
                              }
      
                              public override bool CanWrite => !_isWritingWrapper && base.CanWrite;
      
                              public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
                              {
                                      try
                                      {
                                              _isWritingWrapper = true;
      
                                              Newtonsoft.Json.Linq.JObject jObject = Newtonsoft.Json.Linq.JObject.FromObject(value, serializer);
      
                                              jObject.Remove(DiscriminatorName); // Remove existing discriminator if present
      
                                              jObject.AddFirst(new Newtonsoft.Json.Linq.JProperty(DiscriminatorName, GetSubtypeDiscriminator(value.GetType())));
                                              writer.WriteToken(jObject.CreateReader());
                                      }
                                      finally
                                      {
                                              _isWritingWrapper = false;
                                      }
                              }
      
                              private string GetSubtypeDiscriminator(Type objectType)
                              {
                                      return _subtypeDiscriminatorCache.GetOrAdd(objectType, type =>
                                      {
                                              IEnumerable<JsonInheritanceAttribute> attributes = type.GetTypeInfo().GetCustomAttributes<JsonInheritanceAttribute>(true);
                                              foreach (JsonInheritanceAttribute attribute in attributes)
                                              {
                                                      if (attribute.Type == type)
                                                      {
                                                              return attribute.Key;
                                                      }
                                              }
      
                                              return type.Name;
                                      });
                              }
                      }
              }
      }
      
    2. Create a C# file EmplyApiClient.fix.cs extending EmplyApiClient.

      using Newtonsoft.Json;
      
      namespace Emply;
      
      public partial class EmplyApiClient
      {
          static partial void UpdateJsonSerializerSettings(JsonSerializerSettings settings)
          {
              settings.ContractResolver = typeof(EmplyApiClient).GetPatchedContractResolver();
          }
      }
  5. Create an instance of EmplyApiClient with an access token provider via the Authorization header to call the API.

    Example 8. Example

    using Emply;
    
    string baseUrl = "https://api.emply.com";
    string bearerToken = "eyJhbGc...."; // Generate a key via Emply WebApp
    
    var httpClient = new HttpClient();
    httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {bearerToken}");
    
    var emplyClient = new EmplyApiClient(baseUrl, httpClient);
    var timeZones = await emplyClient.GetTimeZonesAsync();
    
    Console.WriteLine("TimeZones fetched from Emply API:");
    Console.WriteLine("----------------------------------");
    foreach (var timeZone in timeZones)
    {
        Console.WriteLine($"{timeZone.Title}: {timeZone.Offset}");
    }
    

    Output

    Emply_API_Output.png

Was this article helpful?

Was this article helpful?

Want to get in touch?

We got you. Fill out a request and we'll get back to you as soon as possible.

Submit a request