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

For #55. Add GraphQL support #57

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions src/Examples/Definitions/API_GraphQL.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
version: 0.0.1 # Required
apimServiceName: $(apimServiceName) # Required, must match name of an apim service deployed in the specified resource group

apis:
- name: ApiG1
displayName: API G v1
graphQlSpec: https://swapi-graphql.azure-api.net/graphql
policy: $(apimBasePath)\Policies\ApiPolicy.xml
path: $(prefix)/g/api
serviceUrl: https://swapi-graphql.azure-api.net/graphql
protocols: https
subscriptionRequired: true
isCurrent: true
subscriptionKeyParameterNames:
header: ProviderKey
query: ProviderKey
13 changes: 13 additions & 0 deletions src/apimtemplate/ApimEntities/Api/Schema/Document.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Apim.DevOps.Toolkit.ApimEntities.Api.Schema
{
public class Document
{
public string Value { get; set; }
}
}
14 changes: 14 additions & 0 deletions src/apimtemplate/ApimEntities/Api/Schema/SchemaApiProperties.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Apim.DevOps.Toolkit.ApimEntities.Api.Schema
{
public class SchemaApiProperties
{
public string ContentType { get; set; }
public Document Document { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using Apim.DevOps.Toolkit.ApimEntities.Api.Operation.Policy;
using Apim.DevOps.Toolkit.ApimEntities.Api.Policy;
using Apim.DevOps.Toolkit.ApimEntities.Api.Product;
using Apim.DevOps.Toolkit.ApimEntities.Api.Schema;
using Apim.DevOps.Toolkit.ApimEntities.Api.Tag;
using Apim.DevOps.Toolkit.Core.DeploymentDefinitions;
using Apim.DevOps.Toolkit.Core.DeploymentDefinitions.Entities;
Expand Down Expand Up @@ -42,6 +43,7 @@ public IEnumerable<ArmTemplateResource> Create(DeploymentDefinition deploymentDe
resources.AddRange(CreateProductApis(deploymentDefinition));
resources.AddRange(CreateTagApis(deploymentDefinition));
resources.AddRange(CreateApiDiagnostics(deploymentDefinition));
resources.AddRange(CreateGraphQlApiSchema(deploymentDefinition));

return resources;
}
Expand Down Expand Up @@ -176,6 +178,36 @@ private IEnumerable<ArmTemplateResource<ApiDiagnosticsProperties>> CreateApiDiag
.CreateResourcesIf(d => d.HasDiagnostics());
}

/// <summary>
/// Api schemas currently only added if GraphQL schema file is specified in deployment definition.
/// </summary>
/// <param name="deploymentDefinition"></param>
/// <returns></returns>
private IEnumerable<ArmTemplateResource<SchemaApiProperties>> CreateGraphQlApiSchema(DeploymentDefinition deploymentDefinition)
{
return new ArmTemplateResourceCreator<ApiDeploymentDefinition, SchemaApiProperties>(_mapper)
.ForDeploymentDefinitions(deploymentDefinition.Apis)
.UseResourceCreator(apiDeploymentDefinition =>
{
var templateResource = new ArmTemplateResource<SchemaApiProperties>(
$"{apiDeploymentDefinition.Name}/graphql",
$"[concat(parameters('ApimServiceName'), '/{apiDeploymentDefinition.Name}/graphql')]",
ResourceType.ApiSchema,
new SchemaApiProperties
{
ContentType = "application/vnd.ms-azure-apim.graphql.schema",
Document = new Document
{
Value = new GraphQlSpecReader(apiDeploymentDefinition.GraphQlSpec).GetFileContent().Result
}
},
new string[] { $"[resourceId('{ResourceType.Api}', parameters('ApimServiceName'), '{apiDeploymentDefinition.Name}')]" });

return new List<ArmTemplateResource<SchemaApiProperties>> { templateResource };
})
.CreateResourcesIf(d => !d.GraphQlSpec.IsUri(out _), true); // create only if graphQL spec is a file
}

private IEnumerable<ArmTemplateResource<ApiProperties>> CreateApis(DeploymentDefinition deploymentDefinition)
{
return new ArmTemplateResourceCreator<ApiDeploymentDefinition, ApiProperties>(_mapper)
Expand Down
10 changes: 10 additions & 0 deletions src/apimtemplate/Core/Constants/GraphQlFormat.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace Apim.DevOps.Toolkit.Core.Infrastructure.Constants
{
public static class GraphQlFormat
{
/// <summary>
/// The GraphQL API endpoint hosted on a publicly accessible internet address.
/// </summary>
public static readonly string GraphQlLink = "graphql-link";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,15 @@ public class ApiDeploymentDefinition: EntityDeploymentDefinition
public string Name { get; set; }

/// <summary>
/// local path or url to policy
/// local path or url to OpenAPI specification
/// </summary>
public string OpenApiSpec { get; set; }

/// <summary>
/// local path or url to GraphQL API specification
/// </summary>
public string GraphQlSpec { get; set; }

/// <summary>
/// local path or url to policy
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,9 +110,13 @@ private bool ValidateApis(DeploymentDefinition deploymentDefinition)
{
throw new ArgumentException("API name is required");
}
if (api.OpenApiSpec == null)
if (api.OpenApiSpec == null && api.GraphQlSpec == null)
{
throw new ArgumentException("Open API Spec is required");
throw new ArgumentException("Open API Spec or Graph QL Endpoint is required");
}
if (api.OpenApiSpec != null && api.GraphQlSpec != null)
{
throw new ArgumentException("Specifying both Open API Spec and Graph QL Endpoint is not allowed");
}
if (api.Path == null)
{
Expand Down
41 changes: 41 additions & 0 deletions src/apimtemplate/Core/Infrastructure/GraphQlSpecReader.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using Apim.DevOps.Toolkit.Core.Infrastructure.Constants;
using Apim.DevOps.Toolkit.Extensions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Apim.DevOps.Toolkit.Core.Infrastructure
{
public class GraphQlSpecReader
{
private readonly FileReader _fileReader = new();
private string _graphQlFilePath;
public GraphQlSpecReader(string graphQlFilePath)
{
_graphQlFilePath = graphQlFilePath;
}

/// <summary>
/// If the GraphQL spec is a file, return null, we'll import it as a 'apis/schemas' resource type.
/// </summary>
public string GetGraphQlFormat()
{
return _graphQlFilePath.IsUri(out _) ? GraphQlFormat.GraphQlLink : null;
}

/// <summary>
/// If the GraphQL spec is a file, return null, we'll import it as a 'apis/schemas' resource type.
/// </summary>
public string GetValue()
{
return _graphQlFilePath.IsUri(out _) ? _graphQlFilePath : null;
}

public async Task<string> GetFileContent()
{
return await _fileReader.RetrieveFileContentsAsync(_graphQlFilePath);
}
}
}
6 changes: 4 additions & 2 deletions src/apimtemplate/Core/Mapping/ApiMapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@ public void Map(IMapperConfigurationExpression cfg)
{
cfg.CreateMap<ApiDeploymentDefinition, ApiProperties>()
.ForMember(dst => dst.Path, opt => opt.MapFrom(src => src.Path.Replace("//", "/").TrimStart(new[] { '/' })))
.ForMember(dst => dst.Format, opt => opt.MapFrom(src => new OpenApiSpecReader(src.OpenApiSpec).GetOpenApiFormat().Result))
.ForMember(dst => dst.Value, opt => opt.MapFrom(src => new OpenApiSpecReader(src.OpenApiSpec).GetValue().Result))
.ForMember(dst => dst.Format, opt => opt.MapFrom(
src => src.GraphQlSpec != null ? new GraphQlSpecReader(src.GraphQlSpec).GetGraphQlFormat() : new OpenApiSpecReader(src.OpenApiSpec).GetOpenApiFormat().Result))
.ForMember(dst => dst.Value, opt => opt.MapFrom(
src => src.GraphQlSpec != null ? new GraphQlSpecReader(src.GraphQlSpec).GetValue() : new OpenApiSpecReader(src.OpenApiSpec).GetValue().Result))
.ForMember(dst => dst.Protocols, opt => opt.MapFrom(src => src.Protocols.GetItems(new[] { "https" })))
.ForMember(dst => dst.ApiVersionSetId, opt => opt.MapFrom(src => string.IsNullOrEmpty(src.ApiVersionSetId) ? null : $"[resourceId('{ResourceType.ApiVersionSet}', parameters('ApimServiceName'), '{src.ApiVersionSetId}')]"));

Expand Down