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.

Anmerkung

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.

    Beispiel 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

War dieser Beitrag hilfreich?

War dieser Beitrag hilfreich?

Möchten Sie Kontakt aufnehmen?

Wir helfen Ihnen. Füllen Sie eine Anfrage aus und wir melden uns so schnell wie möglich bei Ihnen.

Eine Anfrage einreichen