Skip to content

Commit

Permalink
New CLI function to import assets.
Browse files Browse the repository at this point in the history
  • Loading branch information
SebastianStehle committed May 5, 2021
1 parent 53c69bb commit fa9bfac
Show file tree
Hide file tree
Showing 9 changed files with 144 additions and 32 deletions.
90 changes: 90 additions & 0 deletions cli/Squidex.CLI/Squidex.CLI/Commands/App_Assets.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================

using System.IO;
using System.Threading.Tasks;
using CommandDotNet;
using FluentValidation;
using FluentValidation.Attributes;
using Squidex.CLI.Commands.Implementation;
using Squidex.CLI.Commands.Implementation.Sync.Assets;
using Squidex.CLI.Configuration;

namespace Squidex.CLI.Commands
{
public partial class App
{
[Command(Name = "assets", Description = "Manages assets.")]
[SubCommand]
public sealed class Assets
{
private readonly IConfigurationService configuration;
private readonly ILogger log;

public Assets(IConfigurationService configuration, ILogger log)
{
this.configuration = configuration;

this.log = log;
}

[Command(Name = "import", Description = "Import all files from the source folder.")]
public async Task List(ImportArguments arguments)
{
var session = configuration.StartSession();

var assets = session.Assets;

var folder = new DirectoryInfo(arguments.Folder);
var folderTree = new FolderTree(session);

foreach (var file in folder.GetFiles("*.*", SearchOption.AllDirectories))
{
var relativeFolder = Path.GetRelativePath(folder.FullName, file.Directory.FullName);
var relativePath = Path.GetRelativePath(folder.FullName, file.FullName);

var targetFolder = arguments.TargetFolder;

if (!string.IsNullOrWhiteSpace(relativePath) && relativePath != ".")
{
targetFolder = Path.Combine(targetFolder, relativeFolder);
}

var parentId = await folderTree.GetIdAsync(targetFolder);

await log.DoSafeLineAsync($"Uploading {relativePath}", async () =>
{
await assets.PostAssetAsync(session.App, parentId, duplicate: arguments.Duplicate, file: file);
});
}

log.WriteLine("> Import completed");
}

[Validator(typeof(Validator))]
public sealed class ImportArguments : IArgumentModel
{
[Operand(Name = "folder", Description = "The source folder.")]
public string Folder { get; set; }

[Option(ShortName = "t", LongName = "target", Description = "Path to the target folder.")]
public string TargetFolder { get; set; }

[Option(ShortName = "d", LongName = "duplicate", Description = "Duplicate the asset.")]
public bool Duplicate { get; set; }

public sealed class Validator : AbstractValidator<ImportArguments>
{
public Validator()
{
RuleFor(x => x.Folder).NotEmpty();
}
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ namespace Squidex.CLI.Commands.Implementation.Sync.Assets
{
public sealed class FolderTree
{
private static readonly char[] TrimChars = { '/', '\\', ' ' };
private static readonly char[] SplitChars = { ' ', '/', '\\' };
private static readonly char[] TrimChars = { '/', '\\', ' ', '.' };
private static readonly char[] SplitChars = { '/', '\\' };
private static readonly string RootId = Guid.Empty.ToString();
private readonly Dictionary<string, FolderNode> nodes = new Dictionary<string, FolderNode>();
private readonly ISession session;
Expand Down Expand Up @@ -57,7 +57,7 @@ public async Task<string> GetPathAsync(string id)

public async Task<string> GetIdAsync(string path)
{
if (path == null)
if (path == null || path.Equals(".", StringComparison.OrdinalIgnoreCase))
{
return null;
}
Expand Down
1 change: 1 addition & 0 deletions cli/Squidex.CLI/Squidex.CLI/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ public static int Main(string[] args)
.AddSingleton<IConfigurationService, ConfigurationService>()
.AddSingleton<App>()
.AddSingleton<App.Apps>()
.AddSingleton<App.Assets>()
.AddSingleton<App.Backup>()
.AddSingleton<App.Config>()
.AddSingleton<App.Content>()
Expand Down
2 changes: 1 addition & 1 deletion cli/Squidex.CLI/Squidex.CLI/Squidex.CLI.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
<TargetFramework>net5.0</TargetFramework>
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
<ToolCommandName>sq</ToolCommandName>
<Version>7.5</Version>
<Version>7.6</Version>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="CommandDotNet" Version="4.1.6" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ namespace Squidex.ClientLibrary
/// <remarks>
/// This property is not supported anymore. A content item is pending when the <see cref="NewStatus"/> property is not null.
/// </remarks>
[Obsolete]
[Obsolete("Use NewStatus")]
public bool IsPending { get; set; }

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -377,15 +377,15 @@ private static string BuildQuery(object request)
{
if (queryBuilder.Length > 0)
{
queryBuilder.Append("&");
queryBuilder.Append('&');
}
else
{
queryBuilder.Append("?");
queryBuilder.Append('?');
}

queryBuilder.Append(kvp.Key);
queryBuilder.Append("=");
queryBuilder.Append('=');
queryBuilder.Append(Uri.EscapeUriString(kvp.Value.ToString()));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using HeyRed.Mime;
using Squidex.ClientLibrary.Utils;

#pragma warning disable RECS0096 // Type parameter is never used
Expand All @@ -33,10 +36,10 @@ public override string ToString()
!Message.EndsWith(":", StringComparison.OrdinalIgnoreCase) &&
!Message.EndsWith(",", StringComparison.OrdinalIgnoreCase))
{
sb.Append(":");
sb.Append(':');
}

sb.Append(" ");
sb.Append(' ');
sb.Append(string.Join(", ", Details));
}

Expand Down Expand Up @@ -149,47 +152,63 @@ internal string ToIdString()
public partial interface IAssetsClient
#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member
{
/// <summary>
/// Upload a new asset.
/// </summary>
/// <param name="app">The name of the app.</param>
/// <param name="parentId">The optional parent folder id.</param>
/// <param name="id">The optional custom asset id.</param>
/// <param name="duplicate">True to duplicate the asset, event if the file has been uploaded.</param>
/// <param name="file">The file to upload.</param>
/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
/// <returns>
/// Asset created.
/// </returns>
/// <exception cref="SquidexManagementException">A server side error occurred.</exception>
Task<AssetDto> PostAssetAsync(string app, string parentId = null, string id = null, bool? duplicate = null, FileInfo file = null, CancellationToken cancellationToken = default(CancellationToken));

/// <summary>Get assets.</summary>
/// <param name="app">The name of the app.</param>
/// <param name="query">The optional asset query.</param>
/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
/// <returns>Assets returned.</returns>
/// <exception cref="SquidexManagementException">A server side error occurred.</exception>
Task<AssetsDto> GetAssetsAsync(string app, AssetQuery query = null, System.Threading.CancellationToken cancellationToken = default);
Task<AssetsDto> GetAssetsAsync(string app, AssetQuery query = null, CancellationToken cancellationToken = default);

/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
/// <summary>Get assets.</summary>
/// <summary>
/// Get assets.
/// </summary>
/// <param name="app">The name of the app.</param>
/// <param name="callback">The callback that is invoke for each asset.</param>
/// <param name="batchSize">The number of assets per request.</param>
/// <returns>Assets returned.</returns>
/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
/// <returns>
/// Assets returned.
/// </returns>
/// <exception cref="SquidexManagementException">A server side error occurred.</exception>
Task GetAllAsync(string app, Func<AssetDto, Task> callback, int batchSize = 200, System.Threading.CancellationToken cancellationToken = default);
Task GetAllAsync(string app, Func<AssetDto, Task> callback, int batchSize = 200, CancellationToken cancellationToken = default);
}

#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
public partial class AssetsClient
#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member
{
/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
/// <summary>Get assets.</summary>
/// <param name="app">The name of the app.</param>
/// <param name="query">The optional asset query.</param>
/// <returns>Assets returned.</returns>
/// <exception cref="SquidexManagementException">A server side error occurred.</exception>
public Task<AssetsDto> GetAssetsAsync(string app, AssetQuery query = null, System.Threading.CancellationToken cancellationToken = default)
/// <inheritdoc />
public Task<AssetDto> PostAssetAsync(string app, string parentId = null, string id = null, bool? duplicate = null, FileInfo file = null, CancellationToken cancellationToken = default(CancellationToken))
{
Guard.NotNull(file, nameof(file));

return PostAssetAsync(app, parentId, id, duplicate, new FileParameter(file.OpenRead(), file.Name, MimeTypesMap.GetMimeType(file.Name)));
}

/// <inheritdoc />
public Task<AssetsDto> GetAssetsAsync(string app, AssetQuery query = null, CancellationToken cancellationToken = default)
{
return GetAssetsAsync(app, query?.Top, query?.Skip, query?.OrderBy, query?.Filter, query?.ParentId, query?.ToIdString(), query?.ToQueryJson(), cancellationToken);
}

/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
/// <summary>Get assets.</summary>
/// <param name="app">The name of the app.</param>
/// <param name="callback">The callback that is invoke for each asset.</param>
/// <param name="batchSize">The number of assets per request.</param>
/// <returns>Assets returned.</returns>
/// <exception cref="SquidexManagementException">A server side error occurred.</exception>
public async Task GetAllAsync(string app, Func<AssetDto, Task> callback, int batchSize = 200, System.Threading.CancellationToken cancellationToken = default)
/// <inheritdoc />
public async Task GetAllAsync(string app, Func<AssetDto, Task> callback, int batchSize = 200, CancellationToken cancellationToken = default)
{
var query = new AssetQuery { Top = batchSize, Skip = 0 };
var added = new HashSet<string>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ public override object ReadJson(JsonReader reader, Type objectType, object exist
/// <returns>
/// The discriminator value for the specified type.
/// </returns>
public string GetDiscriminatorValue(Type type)
public virtual string GetDiscriminatorValue(Type type)
{
return GetSubtypeDiscriminator(type);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageProjectUrl>https://github.com/Squidex/squidex/</PackageProjectUrl>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<Version>7.2.0</Version>
<Version>7.3.0</Version>
<TargetFrameworks>netstandard2.0;netcoreapp3.1;net5.0</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="MimeMapping" Version="1.0.1.37" />
<PackageReference Include="MimeTypesMap" Version="1.0.8" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
<PackageReference Include="RefactoringEssentials" Version="5.6.0">
Expand Down

0 comments on commit fa9bfac

Please sign in to comment.