Browse Source

Source url for GraphQL. Closes #91

pull/95/head
Sebastian Stehle 8 years ago
parent
commit
a1e0f3c979
  1. 2
      src/Squidex.Domain.Apps.Read/Contents/GraphQL/CachingGraphQLService.cs
  2. 18
      src/Squidex.Domain.Apps.Read/Contents/GraphQL/GraphQLModel.cs
  3. 4
      src/Squidex.Domain.Apps.Read/Contents/GraphQL/IGraphQLContext.cs
  4. 4
      src/Squidex.Domain.Apps.Read/Contents/GraphQL/IGraphQLUrlGenerator.cs
  5. 12
      src/Squidex.Domain.Apps.Read/Contents/GraphQL/Types/AssetGraphType.cs
  6. 7
      src/Squidex.Infrastructure.Azure/Assets/AzureBlobAssetStore.cs
  7. 12
      src/Squidex.Infrastructure.GoogleCloud/Assets/GoogleCloudAssetStore.cs
  8. 7
      src/Squidex.Infrastructure/Assets/FolderAssetStore.cs
  9. 2
      src/Squidex.Infrastructure/Assets/IAssetStore.cs
  10. 14
      src/Squidex/Config/Domain/ReadModule.cs
  11. 14
      src/Squidex/Pipeline/GraphQLUrlGenerator.cs
  12. 6
      src/Squidex/appsettings.json
  13. 4
      tests/Squidex.Domain.Apps.Read.Tests/Contents/GraphQLTests.cs
  14. 7
      tests/Squidex.Domain.Apps.Read.Tests/Contents/TestData/FakeUrlGenerator.cs
  15. 13
      tests/Squidex.Infrastructure.Tests/Assets/AzureBlobAssetStoreTests.cs
  16. 10
      tests/Squidex.Infrastructure.Tests/Assets/FolderAssetStoreTests.cs
  17. 13
      tests/Squidex.Infrastructure.Tests/Assets/GoogleCloudAssetStoreTests.cs

2
src/Squidex.Domain.Apps.Read/Contents/GraphQL/CachingGraphQLService.cs

@ -91,7 +91,7 @@ namespace Squidex.Domain.Apps.Read.Contents.GraphQL
{
var schemas = await schemaRepository.QueryAllAsync(app.Id);
modelContext = new GraphQLModel(app, schemas.Where(x => x.IsPublished));
modelContext = new GraphQLModel(app, schemas.Where(x => x.IsPublished), urlGenerator);
Cache.Set(cacheKey, modelContext);
}

18
src/Squidex.Domain.Apps.Read/Contents/GraphQL/GraphQLModel.cs

@ -41,10 +41,14 @@ namespace Squidex.Domain.Apps.Read.Contents.GraphQL
private readonly IGraphType assetListType;
private readonly GraphQLSchema graphQLSchema;
public GraphQLModel(IAppEntity appEntity, IEnumerable<ISchemaEntity> schemas)
public bool CanGenerateAssetSourceUrl { get; }
public GraphQLModel(IAppEntity appEntity, IEnumerable<ISchemaEntity> schemas, IGraphQLUrlGenerator urlGenerator)
{
this.appEntity = appEntity;
CanGenerateAssetSourceUrl = urlGenerator.CanGenerateAssetSourceUrl;
partitionResolver = appEntity.PartitionResolver;
assetType = new AssetGraphType(this);
@ -113,6 +117,18 @@ namespace Squidex.Domain.Apps.Read.Contents.GraphQL
return resolver;
}
public IFieldResolver ResolveAssetSourceUrl()
{
var resolver = new FuncFieldResolver<IAssetEntity, object>(c =>
{
var context = (QueryContext)c.UserContext;
return context.UrlGenerator.GenerateAssetSourceUrl(appEntity, c.Source);
});
return resolver;
}
public IFieldResolver ResolveAssetThumbnailUrl()
{
var resolver = new FuncFieldResolver<IAssetEntity, object>(c =>

4
src/Squidex.Domain.Apps.Read/Contents/GraphQL/IGraphQLContext.cs

@ -17,6 +17,8 @@ namespace Squidex.Domain.Apps.Read.Contents.GraphQL
{
public interface IGraphQLContext
{
bool CanGenerateAssetSourceUrl { get; }
IFieldPartitioning ResolvePartition(Partitioning key);
IGraphType GetAssetType();
@ -25,6 +27,8 @@ namespace Squidex.Domain.Apps.Read.Contents.GraphQL
IFieldResolver ResolveAssetUrl();
IFieldResolver ResolveAssetSourceUrl();
IFieldResolver ResolveAssetThumbnailUrl();
IFieldResolver ResolveContentUrl(ISchemaEntity schemaEntity);

4
src/Squidex.Domain.Apps.Read/Contents/GraphQL/IGraphQLUrlGenerator.cs

@ -14,10 +14,14 @@ namespace Squidex.Domain.Apps.Read.Contents.GraphQL
{
public interface IGraphQLUrlGenerator
{
bool CanGenerateAssetSourceUrl { get; }
string GenerateAssetUrl(IAppEntity appEntity, IAssetEntity assetEntity);
string GenerateAssetThumbnailUrl(IAppEntity appEntity, IAssetEntity assetEntity);
string GenerateAssetSourceUrl(IAppEntity appEntity, IAssetEntity assetEntity);
string GenerateContentUrl(IAppEntity appEntity, ISchemaEntity schemaEntity, IContentEntity contentEntity);
}
}

12
src/Squidex.Domain.Apps.Read/Contents/GraphQL/Types/AssetGraphType.cs

@ -148,6 +148,18 @@ namespace Squidex.Domain.Apps.Read.Contents.GraphQL.Types
Description = "The height of the image in pixels if the asset is an image."
});
if (context.CanGenerateAssetSourceUrl)
{
AddField(new FieldType
{
Name = "sourceUrl",
Resolver = context.ResolveAssetSourceUrl(),
ResolvedType = new StringGraphType(),
Description = "The source url of the asset."
});
}
Description = "An asset";
}

7
src/Squidex.Infrastructure.Azure/Assets/AzureBlobAssetStore.cs

@ -50,6 +50,13 @@ namespace Squidex.Infrastructure.Assets
}
}
public string GenerateSourceUrl(string id, long version, string suffix)
{
var blobName = GetObjectName(id, version, suffix);
return new Uri(blobContainer.StorageUri.PrimaryUri, $"/{containerName}/{blobName}").ToString();
}
public async Task CopyTemporaryAsync(string name, string id, long version, string suffix)
{
var blobName = GetObjectName(id, version, suffix);

12
src/Squidex.Infrastructure.GoogleCloud/Assets/GoogleCloudAssetStore.cs

@ -42,6 +42,13 @@ namespace Squidex.Infrastructure.Assets
}
}
public string GenerateSourceUrl(string id, long version, string suffix)
{
var objectName = GetObjectName(id, version, suffix);
return $"https://storage.cloud.google.com/{bucketName}/{objectName}";
}
public Task UploadTemporaryAsync(string name, Stream stream)
{
return storageClient.UploadObjectAsync(bucketName, name, "application/octet-stream", stream);
@ -88,11 +95,14 @@ namespace Squidex.Infrastructure.Assets
{
await storageClient.DeleteObjectAsync(bucketName, name);
}
catch (GoogleApiException ex) when (ex.HttpStatusCode != HttpStatusCode.NotFound)
catch (GoogleApiException ex)
{
if (ex.HttpStatusCode != HttpStatusCode.NotFound)
{
throw;
}
}
}
private string GetObjectName(string id, long version, string suffix)
{

7
src/Squidex.Infrastructure/Assets/FolderAssetStore.cs

@ -51,6 +51,13 @@ namespace Squidex.Infrastructure.Assets
}
}
public string GenerateSourceUrl(string id, long version, string suffix)
{
var file = GetFile(id, version, suffix);
return file.FullName;
}
public async Task UploadTemporaryAsync(string name, Stream stream)
{
var file = GetFile(name);

2
src/Squidex.Infrastructure/Assets/IAssetStore.cs

@ -13,6 +13,8 @@ namespace Squidex.Infrastructure.Assets
{
public interface IAssetStore
{
string GenerateSourceUrl(string id, long version, string suffix);
Task CopyTemporaryAsync(string name, string id, long version, string suffix);
Task DownloadAsync(string id, long version, string suffix, Stream stream);

14
src/Squidex/Config/Domain/ReadModule.cs

@ -23,6 +23,7 @@ using Squidex.Domain.Apps.Read.Schemas.Services;
using Squidex.Domain.Apps.Read.Schemas.Services.Implementations;
using Squidex.Domain.Users;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Assets;
using Squidex.Infrastructure.CQRS.Events;
using Squidex.Pipeline;
@ -46,6 +47,14 @@ namespace Squidex.Config.Domain
.AsSelf()
.SingleInstance();
builder.Register(c => new GraphQLUrlGenerator(
c.Resolve<IOptions<MyUrlsOptions>>(),
c.Resolve<IAssetStore>(),
Configuration.GetValue<bool>("assetStore:exposeSourceUrl")))
.As<IGraphQLUrlGenerator>()
.AsSelf()
.SingleInstance();
builder.RegisterType<CachingAppProvider>()
.As<IAppProvider>()
.AsSelf()
@ -61,11 +70,6 @@ namespace Squidex.Config.Domain
.AsSelf()
.SingleInstance();
builder.RegisterType<GraphQLUrlGenerator>()
.As<IGraphQLUrlGenerator>()
.AsSelf()
.SingleInstance();
builder.RegisterType<AssetUserPictureStore>()
.As<IUserPictureStore>()
.AsSelf()

14
src/Squidex/Pipeline/GraphQLUrlGenerator.cs

@ -13,6 +13,7 @@ using Squidex.Domain.Apps.Read.Assets;
using Squidex.Domain.Apps.Read.Contents;
using Squidex.Domain.Apps.Read.Contents.GraphQL;
using Squidex.Domain.Apps.Read.Schemas;
using Squidex.Infrastructure.Assets;
// ReSharper disable ConvertIfStatementToReturnStatement
@ -20,11 +21,17 @@ namespace Squidex.Pipeline
{
public sealed class GraphQLUrlGenerator : IGraphQLUrlGenerator
{
private readonly IAssetStore assetStore;
private readonly MyUrlsOptions urlsOptions;
public GraphQLUrlGenerator(IOptions<MyUrlsOptions> urlsOptions)
public bool CanGenerateAssetSourceUrl { get; }
public GraphQLUrlGenerator(IOptions<MyUrlsOptions> urlsOptions, IAssetStore assetStore, bool allowAssetSourceUrl)
{
this.assetStore = assetStore;
this.urlsOptions = urlsOptions.Value;
CanGenerateAssetSourceUrl = allowAssetSourceUrl;
}
public string GenerateAssetThumbnailUrl(IAppEntity appEntity, IAssetEntity assetEntity)
@ -46,5 +53,10 @@ namespace Squidex.Pipeline
{
return urlsOptions.BuildUrl($"api/content/{appEntity.Name}/{schemaEntity.Name}/{contentEntity.Id}");
}
public string GenerateAssetSourceUrl(IAppEntity appEntity, IAssetEntity assetEntity)
{
return assetStore.GenerateSourceUrl(assetEntity.Id.ToString(), assetEntity.FileVersion, null);
}
}
}

6
src/Squidex/appsettings.json

@ -61,7 +61,11 @@
* The connection string to the azure storage service.
*/
"connectionString": "UseDevelopmentStorage=true"
}
},
/*
* Allow to expose the url in graph ql url.
*/
"exposeSourceUrl": false
},
"eventStore": {

4
tests/Squidex.Domain.Apps.Read.Tests/Contents/GraphQLTests.cs

@ -98,6 +98,7 @@ namespace Squidex.Domain.Apps.Read.Contents
lastModifiedBy
url
thumbnailUrl
sourceUrl
mimeType
fileName
fileSize
@ -133,6 +134,7 @@ namespace Squidex.Domain.Apps.Read.Contents
lastModifiedBy = "subject:user2",
url = $"assets/{assetEntity.Id}",
thumbnailUrl = $"assets/{assetEntity.Id}?width=100",
sourceUrl = $"assets/source/{assetEntity.Id}",
mimeType = "image/png",
fileName = "MyFile.png",
fileSize = 1024,
@ -165,6 +167,7 @@ namespace Squidex.Domain.Apps.Read.Contents
lastModifiedBy
url
thumbnailUrl
sourceUrl
mimeType
fileName
fileSize
@ -194,6 +197,7 @@ namespace Squidex.Domain.Apps.Read.Contents
lastModifiedBy = "subject:user2",
url = $"assets/{assetEntity.Id}",
thumbnailUrl = $"assets/{assetEntity.Id}?width=100",
sourceUrl = $"assets/source/{assetEntity.Id}",
mimeType = "image/png",
fileName = "MyFile.png",
fileSize = 1024,

7
tests/Squidex.Domain.Apps.Read.Tests/Contents/TestData/FakeUrlGenerator.cs

@ -15,6 +15,8 @@ namespace Squidex.Domain.Apps.Read.Contents.TestData
{
public sealed class FakeUrlGenerator : IGraphQLUrlGenerator
{
public bool CanGenerateAssetSourceUrl { get; } = true;
public string GenerateAssetUrl(IAppEntity appEntity, IAssetEntity assetEntity)
{
return $"assets/{assetEntity.Id}";
@ -25,6 +27,11 @@ namespace Squidex.Domain.Apps.Read.Contents.TestData
return $"assets/{assetEntity.Id}?width=100";
}
public string GenerateAssetSourceUrl(IAppEntity appEntity, IAssetEntity assetEntity)
{
return $"assets/source/{assetEntity.Id}";
}
public string GenerateContentUrl(IAppEntity appEntity, ISchemaEntity schemaEntity, IContentEntity contentEntity)
{
return $"contents/{schemaEntity.Name}/{contentEntity.Id}";

13
tests/Squidex.Infrastructure.Tests/Assets/AzureBlobAssetStoreTests.cs

@ -6,6 +6,9 @@
// All rights reserved.
// ==========================================================================
using System;
using Xunit;
namespace Squidex.Infrastructure.Assets
{
internal class AzureBlobAssetStoreTests : AssetStoreTests<AzureBlobAssetStore>
@ -18,5 +21,15 @@ namespace Squidex.Infrastructure.Assets
public override void Dispose()
{
}
[Fact]
public void Should_calculate_source_url()
{
Sut.Connect();
var id = Guid.NewGuid().ToString();
Assert.Equal($"http://127.0.0.1:10000/squidex-test-container/{id}_1", Sut.GenerateSourceUrl(id, 1, null));
}
}
}

10
tests/Squidex.Infrastructure.Tests/Assets/FolderAssetStoreTests.cs

@ -45,6 +45,16 @@ namespace Squidex.Infrastructure.Assets
Assert.True(Directory.Exists(testFolder));
}
[Fact]
public void Should_calculate_source_url()
{
Sut.Connect();
var id = Guid.NewGuid().ToString();
Assert.Equal(Path.Combine(testFolder, $"{id}_1"), Sut.GenerateSourceUrl(id, 1, null));
}
private static string CreateInvalidPath()
{
var windir = Environment.GetEnvironmentVariable("windir");

13
tests/Squidex.Infrastructure.Tests/Assets/GoogleCloudAssetStoreTests.cs

@ -6,6 +6,9 @@
// All rights reserved.
// ==========================================================================
using System;
using Xunit;
namespace Squidex.Infrastructure.Assets
{
internal class GoogleCloudAssetStoreTests : AssetStoreTests<GoogleCloudAssetStore>
@ -18,5 +21,15 @@ namespace Squidex.Infrastructure.Assets
public override void Dispose()
{
}
[Fact]
public void Should_calculate_source_url()
{
Sut.Connect();
var id = Guid.NewGuid().ToString();
Assert.Equal($"https://storage.cloud.google.com/squidex-test/{id}_1", Sut.GenerateSourceUrl(id, 1, null));
}
}
}

Loading…
Cancel
Save