Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Enhancement] Improved public API for IDownstreamWebApi #537

Closed
jmprieur opened this issue Sep 2, 2020 · 2 comments
Closed

[Enhancement] Improved public API for IDownstreamWebApi #537

jmprieur opened this issue Sep 2, 2020 · 2 comments
Assignees
Labels
enhancement New feature or request fixed
Milestone

Comments

@jmprieur
Copy link
Collaborator

jmprieur commented Sep 2, 2020

Thanks for your suggestion, @NikolaosWakem, and for being an early adopter.
Would you see value in having other generic methods (for instance CallWebApiForUserAsync?)

Thanks for getting back to me... I just use the following...

public static class MicrosoftIdentityWebExtensions
    {
        public static async Task<TOutput> GetWebApiForUserAsync<TOutput>(this IDownstreamWebApi downstreamWebApi, string serviceName, string relativePath)
        {
            return await downstreamWebApi.CallWebApiForUserAsync<TOutput>(serviceName, options =>
            {
                options.HttpMethod = HttpMethod.Get;
                options.RelativePath = relativePath;
            });
        }

        public static async Task PostWebApiForUserAsync(this IDownstreamWebApi downstreamWebApi, string serviceName, string relativePath)
        {
            await downstreamWebApi.CallWebApiForUserAsync(serviceName, options =>
            {
                options.HttpMethod = HttpMethod.Post;
                options.RelativePath = relativePath;
            });
        }

        public static async Task<TOutput> PostWebApiForUserAsync<TOutput, TInput>(this IDownstreamWebApi downstreamWebApi, string serviceName, string relativePath, TInput data)
        {
            return await downstreamWebApi.CallWebApiForUserAsync<TInput, TOutput>(serviceName, data, options =>
            {
                options.HttpMethod = HttpMethod.Post;
                options.RelativePath = relativePath;
            });
        }

        public static async Task PutWebApiForUserAsync<TInput>(this IDownstreamWebApi downstreamWebApi, string serviceName, string relativePath, TInput data)
        {
            await downstreamWebApi.CallWebApiForUserAsync(serviceName, data, options =>
            {
                options.HttpMethod = HttpMethod.Put;
                options.RelativePath = relativePath;
            });
        }

        public static async Task<TOutput> PutWebApiForUserAsync<TOutput, TInput>(this IDownstreamWebApi downstreamWebApi, string serviceName, string relativePath, TInput data)
        {
            return await downstreamWebApi.CallWebApiForUserAsync<TInput, TOutput>(serviceName, data, options =>
            {
                options.HttpMethod = HttpMethod.Put;
                options.RelativePath = relativePath;
            });
        }

        private static async Task<TOutput> CallWebApiForUserAsync<TOutput>(this IDownstreamWebApi downstreamWebApi, string serviceName, Action<DownstreamWebApiOptions> downstreamWebApiOptionsOverride = null, ClaimsPrincipal user = null)
        {
            var response = await downstreamWebApi.CallWebApiForUserAsync(
                serviceName,
                downstreamWebApiOptionsOverride,
                user,
                null
                ).ConfigureAwait(false);

            var content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);

            if (!response.IsSuccessStatusCode)
                throw new HttpRequestException($"{(int)response.StatusCode} {response.StatusCode} {content}");

            return !string.IsNullOrWhiteSpace(content) ? JsonSerializer.Deserialize<TOutput>(content, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }) : default;
        }

        private static async Task CallWebApiForUserAsync<TInput>(this IDownstreamWebApi downstreamWebApi, string serviceName, TInput input, Action<DownstreamWebApiOptions> downstreamWebApiOptionsOverride = null, ClaimsPrincipal user = null)
        {
            var response = await downstreamWebApi.CallWebApiForUserAsync(
                serviceName,
                downstreamWebApiOptionsOverride,
                user,
                new StringContent(JsonSerializer.Serialize(input), Encoding.UTF8, "application/json")
                ).ConfigureAwait(false);

            if (!response.IsSuccessStatusCode)
                throw new HttpRequestException($"{(int)response.StatusCode} {response.StatusCode} {await response.Content.ReadAsStringAsync().ConfigureAwait(false)}");
        }

        private static async Task<TOutput> CallWebApiForUserAsync<TInput, TOutput>(this IDownstreamWebApi downstreamWebApi, string serviceName, TInput input, Action<DownstreamWebApiOptions> downstreamWebApiOptionsOverride = null, ClaimsPrincipal user = null)
        {
            var response = await downstreamWebApi.CallWebApiForUserAsync(
                serviceName,
                downstreamWebApiOptionsOverride,
                user,
                new StringContent(JsonSerializer.Serialize(input), Encoding.UTF8, "application/json")
                ).ConfigureAwait(false);

            var content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);

            if (!response.IsSuccessStatusCode)
                throw new HttpRequestException($"{(int)response.StatusCode} {response.StatusCode} {content}");

            return !string.IsNullOrWhiteSpace(content) ? JsonSerializer.Deserialize<TOutput>(content, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }) : default;
        }

        public static bool IsMsalUiRequiredException(this Exception exception)
        {
            if (exception is Microsoft.Identity.Client.MsalUiRequiredException)
                return true;
            else if (exception.InnerException != null)
                return IsMsalUiRequiredException(exception.InnerException);
            else
                return false;
        }
    }

Originally posted by @NikolaosWakem in #503 (comment)

@bgavrilMS
Copy link
Member

Just as a thought, C# 8 introduced "default implementations in interfaces" which allows you to add new methods to interfaces without breaking backwards compat. So if these methods should be part of the interface, consider using this mechanism. If however these methods are more of a convenience API, or you want to hide them under a different namespace, you can use extensions.

https://devblogs.microsoft.com/dotnet/default-implementations-in-interfaces/

@jennyf19
Copy link
Collaborator

Included in 1.2.0 release.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request fixed
Projects
None yet
Development

No branches or pull requests

3 participants