Skip to content

Instantly share code, notes, and snippets.

@norritt
Last active February 7, 2023 20:08
Show Gist options
  • Save norritt/add48ae376c4d79d2d253b197bff1ebd to your computer and use it in GitHub Desktop.
Save norritt/add48ae376c4d79d2d253b197bff1ebd to your computer and use it in GitHub Desktop.
// Originally refit is used:
// services
// .AddRefitClient<ISomething>(settingsFactory)) // we need settings factory for custom JsonSerializers
// .ConfigureHttpClient(
// (providers, client) =>
// {
// var endpoints = providers.GetRequiredService<IOptions<TOptions>>();
// client.BaseAddress = new Uri(urlExtractor(endpoints.Value));
// });
// and that stuff you should write for every interface even if they are on same server. boring
// Builder example:
// services
// .BuildExternalCalls()
// .With(jsonConverters)
// .Using<Endpoints>(options => options.TechCards)
// .To<ICheckController>()
// .To<IViolationController>()
// // ....
// .Build();
// perhaps I should add option to omit converters, but you can't omit endpoints (there are no `Build()` method otherwise)
// You can choose order of With()/Using() methods as well
namespace Dis.Common.Refit.ExternalCall.Builder;
public interface IExternalCallBuilder
{
IExternalCallNoUsingBuilder<TOptions> Using<TOptions>(Func<TOptions, string> urlExtractor)
where TOptions : class;
IExternalCallNoWithBuilder With(IReadOnlyCollection<JsonConverter> jsonConverters);
IExternalCallNoWithBuilder With(Func<IServiceProvider, RefitSettings> settingsFactory);
}
public interface IExternalCallNoUsingBuilder<TOptions>
where TOptions : class
{
IExternalCallReadyBuilder With(IEnumerable<JsonConverter> jsonConverters);
IExternalCallReadyBuilder With(Func<IServiceProvider, RefitSettings> settingsFactory);
}
public interface IExternalCallNoWithBuilder
{
IExternalCallReadyBuilder Using<TOptions>(Func<TOptions, string> urlExtractor)
where TOptions : class;
}
public interface IExternalCallReadyBuilder
{
IExternalCallCompletedBuilder To<TRefitInterface>()
where TRefitInterface : class;
}
public interface IExternalCallCompletedBuilder : IExternalCallReadyBuilder
{
IServiceCollection Build();
}
internal class ExternalCallBuilder :
IExternalCallBuilder,
IExternalCallNoWithBuilder
{
protected readonly IServiceCollection _services;
protected Func<IServiceProvider, RefitSettings>? _settingsFactory;
internal ExternalCallBuilder(IServiceCollection services, Func<IServiceProvider, RefitSettings>? settingsFactory = null)
{
_services = services;
_settingsFactory = settingsFactory;
}
IExternalCallNoWithBuilder IExternalCallBuilder.With(IReadOnlyCollection<JsonConverter> jsonConverters)
{
if (_settingsFactory != null)
{
throw new InvalidOperationException("Settings factory already initialized");
}
_settingsFactory = SettingsFactories.GetDefaultTokenFactory(jsonConverters);
return this;
}
IExternalCallNoWithBuilder IExternalCallBuilder.With(Func<IServiceProvider, RefitSettings> settingsFactory)
{
if (_settingsFactory != null)
{
throw new InvalidOperationException("Settings factory already initialized");
}
_settingsFactory = settingsFactory;
return this;
}
IExternalCallNoUsingBuilder<TOptions> IExternalCallBuilder.Using<TOptions>(Func<TOptions, string> urlExtractor)
where TOptions : class
{
return UsingInternal(urlExtractor);
}
IExternalCallReadyBuilder IExternalCallNoWithBuilder.Using<TOptions>(Func<TOptions, string> urlExtractor)
where TOptions : class
{
return UsingInternal(urlExtractor);
}
protected ExternalCallBuilderWithExtractor<TOptions> UsingInternal<TOptions>(Func<TOptions, string> urlExtractor)
where TOptions : class
{
if (GetType() == typeof(ExternalCallBuilderWithExtractor<TOptions>))
{
throw new InvalidOperationException("Url extractor already initialized");
}
return new ExternalCallBuilderWithExtractor<TOptions>(_services, _settingsFactory, urlExtractor);
}
}
internal class ExternalCallBuilderWithExtractor<TOptions> :
ExternalCallBuilder,
IExternalCallNoUsingBuilder<TOptions>,
IExternalCallReadyBuilder,
IExternalCallCompletedBuilder
where TOptions : class
{
private readonly Func<TOptions, string> _urlExtractor;
private readonly HashSet<Type> _registeredTypes = new();
internal ExternalCallBuilderWithExtractor(
IServiceCollection services,
Func<IServiceProvider, RefitSettings>? settingsFactory,
Func<TOptions, string> urlExtractor
) : base(services, settingsFactory)
{
_urlExtractor = urlExtractor;
}
IServiceCollection IExternalCallCompletedBuilder.Build()
{
foreach (var type in _registeredTypes)
{
_services.AddExternalCall(type, _settingsFactory, _urlExtractor);
}
return _services;
}
IExternalCallCompletedBuilder IExternalCallReadyBuilder.To<TRefitInterface>()
{
_registeredTypes.Add(typeof(TRefitInterface));
return this;
}
IExternalCallReadyBuilder IExternalCallNoUsingBuilder<TOptions>.With(IEnumerable<JsonConverter> jsonConverters)
{
if (_settingsFactory != null)
{
throw new InvalidOperationException("Settings factory already initialized");
}
_settingsFactory = SettingsFactories.GetDefaultTokenFactory(jsonConverters);
return this;
}
IExternalCallReadyBuilder IExternalCallNoUsingBuilder<TOptions>.With(Func<IServiceProvider, RefitSettings> settingsFactory)
{
if (_settingsFactory != null)
{
throw new InvalidOperationException("Settings factory already initialized");
}
_settingsFactory = settingsFactory;
return this;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment