public static async Task HandleAsync( IResourceEventRepository resourceEventRepository, IApiLinkGenerator apiLinkGenerator, ApiOperationContext context, OperationResult result) { if (result is OkResult okResult) { var innerResult = okResult.Content; if (innerResult is ResourceEvent resourceEvent) { var logger = context.ServiceProvider.GetRequiredService <ILogger <ResourceEventHandlerMiddlewareBuilder> >(); void AddMetadata(string k, object v) => resourceEvent.Metadata[k] = v; context.UserAuthorisationContext?.PopulateMetadata(AddMetadata); resourceEvent.CorrelationId = context.Activity?.Id; resourceEvent.Operation = context.Operation; var metadataProviders = context.ServiceProvider.GetServices <IContextMetadataProvider>(); foreach (var p in metadataProviders) { await p.PopulateMetadataAsync(context, AddMetadata); } // If we do not already have Data use the "SelfQuery" to populate using a nested query if possible if (resourceEvent.Data == null && resourceEvent.SelfQuery != null) { await TryPopulateResourceEventDataAsync(context, resourceEvent); } var selfLink = context.DataModel.GetLinkFor(resourceEvent.ResourceType, "self"); if (selfLink == null) { logger.LogWarning( "No self link exists. Href property of the resource event of type {ResourceType} will not be populated", resourceEvent.ResourceType); } else { resourceEvent.Href = apiLinkGenerator.CreateUrl(selfLink, resourceEvent.Data ?? resourceEvent.SelfQuery); } if (resourceEvent.Data != null) { await TryPopulateChangedValuesAsync(resourceEventRepository, resourceEvent); } await resourceEventRepository.AddAsync(resourceEvent); } } }
private static async ValueTask <object> AddResourceLinksAsync( ILogger <LinkGeneratorMiddlewareBuilder> logger, IApiLinkGenerator apiLinkGenerator, IEnumerable <IResourceLinkGenerator> generators, ApiOperationContext context, object resource) { if (resource is ILinkableResource linkableResource) { await AddLinksAsync(logger, apiLinkGenerator, generators, context, linkableResource); } var enumerableResult = resource as IEnumerable <object>; if (resource is IPagedApiResource pagedResult) { enumerableResult = pagedResult.GetEnumerable(); } if (enumerableResult != null) { if (enumerableResult is IQueryable <object> ) { // We need to ensure we are now dealing with a non-deferred result, else the // links will be added, but the deferred result is still returned and so // changes are lost. // // This needs to be what we then actually return from the middleware after the // links have been added enumerableResult = enumerableResult.ToList(); resource = enumerableResult; } foreach (var obj in enumerableResult) { if (obj is ILinkableResource apiResourceItem) { await AddLinksAsync(logger, apiLinkGenerator, generators, context, apiResourceItem); } else { // If we cannot add any links because not a `LinkableResource` then break early // as we assume all entries are the same type if (logger.IsEnabled(LogLevel.Trace)) { logger.LogTrace("Resource type is not a LinkableResource. resource_type={0}", obj.GetType().Name); } break; } } } return(resource); }
public static async Task AddLinksAsync( IApiLinkGenerator apiLinkGenerator, IEnumerable <IResourceLinkGenerator> registeredGenerators, ApiOperationContext context, OperationResult result) { if (result is OkResult okResult) { var logger = context.ServiceProvider.GetRequiredService <ILogger <LinkGeneratorMiddlewareBuilder> >(); okResult.Content = await AddResourceLinksAsync(logger, apiLinkGenerator, registeredGenerators, context, okResult.Content); } }
/// <summary> /// Initializes a new instance of the <see cref="EntityOperationResourceLinkGenerator"/> class. /// </summary> /// <param name="apiAuthoriserAggregator">An authorisor aggregrator to check if links are allowed to be shown to a user.</param> /// <param name="linkGenerator">A link generator to create the URLs of generated links.</param> /// <param name="logger">The logger.</param> public EntityOperationResourceLinkGenerator( IApiAuthoriserAggregator apiAuthoriserAggregator, IApiLinkGenerator linkGenerator, ILogger <EntityOperationResourceLinkGenerator> logger) { Guard.NotNull(nameof(apiAuthoriserAggregator), apiAuthoriserAggregator); Guard.NotNull(nameof(linkGenerator), linkGenerator); Guard.NotNull(nameof(logger), logger); this._apiAuthoriserAggregator = apiAuthoriserAggregator; this._linkGenerator = linkGenerator; this._logger = logger; }
public static async Task HandleAsync( IResourceEventRepository resourceEventRepository, IApiLinkGenerator apiLinkGenerator, ApiOperationContext context, OperationResult result) { if (result is OkResult okResult) { var innerResult = okResult.Content; if (innerResult is ResourceEvent resourceEvent) { var logger = context.ServiceProvider.GetRequiredService <ILogger <ResourceEventHandlerMiddlewareBuilder> >(); logger.LogDebug("ResourceEvent found. Loading resource. resource_type={0}", resourceEvent.ResourceType); void AddMetadata(string k, object v) => resourceEvent.Metadata[k] = v; context.UserAuthorisationContext?.PopulateMetadata(AddMetadata); resourceEvent.CorrelationId = Activity.Current?.Id; resourceEvent.Operation = context.Operation; var metadataProviders = context.ServiceProvider.GetServices <IContextMetadataProvider>(); foreach (var p in metadataProviders) { await p.PopulateMetadataAsync(context, AddMetadata); } var selfLink = context.DataModel.GetLinkFor(resourceEvent.ResourceType, "self"); if (selfLink == null) { logger.LogWarning( "No self link exists. Link and payload will not be populated. resource_type={0}", resourceEvent.ResourceType.Name); return; } resourceEvent.Href = apiLinkGenerator.CreateUrl(selfLink, resourceEvent.SelfQuery); await PopulateResourceEventData(resourceEventRepository, context, resourceEvent); await resourceEventRepository.AddAsync(resourceEvent); } } }
/// <summary> /// Invokes this query, returning a simple model of available routes within this API. /// </summary> /// <param name="linkGenerator">The API link generator.</param> /// <param name="dataModel">The API data model.</param> /// <returns>A <see cref="RootResource" />.</returns> public RootResource Invoke(IApiLinkGenerator linkGenerator, ApiDataModel dataModel) { var systemResource = new RootResource(); foreach (var link in dataModel.GetRootLinks()) { if (link.OperationDescriptor.IsExposed) { systemResource.AddLink(link.Rel, new Link { Href = linkGenerator.CreateUrl(link), }); } } return(systemResource); }
private static async ValueTask AddLinksAsync( ILogger logger, IApiLinkGenerator apiLinkGenerator, IEnumerable <IResourceLinkGenerator> generators, ApiOperationContext context, ILinkableResource result) { foreach (var resourceLinkGenerator in generators) { if (logger.IsEnabled(LogLevel.Trace)) { logger.LogTrace("Generating links. generator={0} result_type={1}", resourceLinkGenerator.GetType().Name, result.GetType().Name); } await resourceLinkGenerator.AddLinksAsync(apiLinkGenerator, context, result); } }
/// <inheritdoc /> public ValueTask AddLinksAsync(IApiLinkGenerator apiLinkGenerator, ApiOperationContext context, ILinkableResource linkableResource) { if (!(linkableResource is T asTypedResource)) { return(default);
/// <inheritdoc/> public async ValueTask AddLinksAsync(IApiLinkGenerator apiLinkGenerator, ApiOperationContext context, ILinkableResource linkableResource) { Guard.NotNull(nameof(context), context); Guard.NotNull(nameof(linkableResource), linkableResource); var traceLogEnabled = this._logger.IsEnabled(LogLevel.Trace); var links = context.DataModel.GetLinksForResource(linkableResource.GetType()); if (links.Count == 0) { if (traceLogEnabled) { this._logger.LogTrace( "No links have been registered for linkable resource {0}", linkableResource.GetType()); } return; } // When trace is enabled, log all links we would be checking, else if debug just the count, otherwise nothing logged here if (traceLogEnabled) { this._logger.LogTrace("Attempting to add {0} links for the linkable resource {1}.", links.Count, linkableResource.GetType()); } foreach (var link in links) { var entityOperation = link.OperationDescriptor; var entityOperationName = entityOperation.OperationType.Name; if (!entityOperation.IsExposed) { if (traceLogEnabled) { this._logger.LogTrace("Operation not exposed, excluding. operation_type={0}", entityOperationName); } continue; } var result = await this._apiAuthoriserAggregator.CanShowLinkAsync(context, entityOperation, linkableResource); if (result.IsAllowed == false) { if (traceLogEnabled) { this._logger.LogTrace("Operation is not allowed to be executed, excluding. operation_type={0}", entityOperationName); } continue; } var url = this._linkGenerator.CreateUrl(link, linkableResource); // We could not generate this URL, a placeholder value could not be injected. We therefore skip it if (url == null) { if (traceLogEnabled) { this._logger.LogTrace("Cannot add link, URL was not generated. operation_type={0}", entityOperationName); } continue; } if (traceLogEnabled) { this._logger.LogTrace("All checks passed. Adding link. operation_type={0}", entityOperationName); } linkableResource.AddLink(link.Rel, new Link { Href = url, }); } }