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

DoAddCommand - Replacement for NonWorking .Object API #40

Closed
wants to merge 5 commits into from
Closed
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
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.

# Ide

.idea
.env

# User-specific files
*.suo
*.user
Expand Down
31 changes: 31 additions & 0 deletions src/DreadIpfsHttpClient.nuspec
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't need to add a nuspec here, and these values are different from our published package.

<package>
<metadata>
<id>DreadIpfsHttpClient</id>
<version>0.5.0</version>
<title>IpfsHttpClient</title>
<readme>docs\README.md</readme>
<authors>DreadfulBot</authors>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<license type="expression">MIT</license>
<!-- <icon>icon.png</icon> -->
<projectUrl>https://github.com/DreadfulBot/net-ipfs-http-client</projectUrl>
<description>IpfsClient with some improvements</description>
<releaseNotes>Folder upload function fixed</releaseNotes>
<copyright>DreadfulBot</copyright>
<tags>ipfs files nft blockchain liberation</tags>
<dependencies>
<group targetFramework=".NETStandard2.0">
<dependency id="IpfsShipyard.Ipfs.Core" version="0.6.0" />
<dependency id="Microsoft.CSharp" version="4.7.0" />
<dependency id="Newtonsoft.Json" version="13.0.3" />
<dependency id="Multiformats.Base" version="2.0.2" />
<dependency id="PolySharp" version="1.14.1" />
</group>
</dependencies>
</metadata>
<files>
<file src="bin\Release\netstandard2.0\IpfsShipyard.Ipfs.Http.Client.dll" target="lib\netstandard2.0" />
<file src="..\README.md" target="docs\" />
</files>
</package>
133 changes: 109 additions & 24 deletions src/IpfsClient.cs
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
using Ipfs.CoreApi;
using Newtonsoft.Json;
using System;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Ipfs.CoreApi;
using Newtonsoft.Json;

namespace Ipfs.Http
{
Expand Down Expand Up @@ -59,7 +60,8 @@ public IpfsClient()
var assembly = typeof(IpfsClient).GetTypeInfo().Assembly;
var version = assembly.GetName().Version;

UserAgent = string.Format("{0}/{1}.{2}.{3}", assembly.GetName().Name, version.Major, version.Minor, version.Revision);
UserAgent = string.Format("{0}/{1}.{2}.{3}", assembly.GetName().Name, version.Major, version.Minor,
version.Revision);
TrustedPeers = new TrustedPeerCollection(this);

Bootstrap = new BootstrapApi(this);
Expand Down Expand Up @@ -229,7 +231,7 @@ HttpClient Api()
if (HttpMessageHandler is HttpClientHandler handler && handler.SupportsAutomaticDecompression)
{
handler.AutomaticDecompression = DecompressionMethods.GZip
| DecompressionMethods.Deflate;
| DecompressionMethods.Deflate;
}

api = new HttpClient(HttpMessageHandler)
Expand All @@ -241,6 +243,7 @@ HttpClient Api()
}
}
}

return api;
}

Expand Down Expand Up @@ -271,7 +274,8 @@ HttpClient Api()
/// <exception cref="HttpRequestException">
/// When the IPFS server indicates an error.
/// </exception>
public async Task<string> DoCommandAsync(string command, CancellationToken cancel, string arg = null, params string[] options)
public async Task<string> DoCommandAsync(string command, CancellationToken cancel, string arg = null,
params string[] options)
{
var url = BuildCommand(command, arg, options);

Expand All @@ -284,6 +288,74 @@ public async Task<string> DoCommandAsync(string command, CancellationToken cance
}
}

/// <summary>
/// Executes /api/v0/add command
/// See details here - https://docs.ipfs.tech/reference/kubo/rpc/#api-v0-add
/// </summary>
/// <param name="path">FileSystem path</param>
/// <param name="cancel">Cancellation token</param>
/// <param name="arg">Args</param>
/// <param name="options">Options</param>
/// <returns>Response</returns>
public async Task<ICollection<IpfsFile>> DoAddCommand(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The UploadAsync methods handles multipart form data for a given command. We shouldn't need to create a new method for this, especially not one this specific in a class this general.

Instead of this, we should just fix the existing FileSystem.AddDirectoryAsync method.

string path,
CancellationToken cancel,
string arg = null,
params string[] options)
{
var url = BuildCommand("add", arg, options);

var dirInfo = new DirectoryInfo(Path.GetFullPath(path));

if (!dirInfo.Exists)
{
throw new Exception("Directory does not exists");
}

var upperLevelFolder = dirInfo.Parent?.FullName ?? Path.GetFullPath(path);

string GetFileName(string cPath)
{
// var relativePath = Path.GetRelativePath(upperLevelFolder, cPath);
// var relativePath = Path.join(upperLevelFolder, cPath);

var relativePath = cPath.Replace(upperLevelFolder, "");
return $"{Uri.EscapeDataString(relativePath)}";
}

ByteArrayContent addFile(FileInfo cFile)
{
var content = new ByteArrayContent(File.ReadAllBytes(cFile.FullName));
content.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data")
{
Name = "file",
FileName = GetFileName(cFile.FullName)
};
content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
return content;
}

var content = new MultipartFormDataContent();

foreach (var file in dirInfo.EnumerateFiles())
{
content.Add(addFile(file));
}

using var response = await Api().PostAsync(url, content, cancel);
await ThrowOnErrorAsync(response);

var strBody = await response.Content.ReadAsStringAsync();

var result = strBody
.Split('\n')
.Where(x => !string.IsNullOrWhiteSpace(x))
.Select(JsonConvert.DeserializeObject<IpfsFile>)
.ToList();

return result;
}

internal Task DoCommandAsync(Uri url, byte[] bytes, CancellationToken cancel)
{
return DoCommandAsync(url, new ByteArrayContent(bytes), cancel);
Expand All @@ -299,13 +371,14 @@ internal Task DoCommandAsync(Uri url, string str, CancellationToken cancel)
return DoCommandAsync(url, new StringContent(str), cancel);
}

internal async Task DoCommandAsync(Uri url, HttpContent content, CancellationToken cancel)
internal async Task<string> DoCommandAsync(Uri url, HttpContent content, CancellationToken cancel)
{
using (var response = await Api().PostAsync(url, new MultipartFormDataContent { { content, "\"file\"" } }, cancel))
{
await ThrowOnErrorAsync(response);
var body = await response.Content.ReadAsStringAsync();
}
using var response =
await Api().PostAsync(url, new MultipartFormDataContent {{content, "\"file\""}}, cancel);

await ThrowOnErrorAsync(response);
var body = await response.Content.ReadAsStringAsync();
return body;
}


Expand Down Expand Up @@ -339,7 +412,8 @@ internal async Task DoCommandAsync(Uri url, HttpContent content, CancellationTok
/// <exception cref="HttpRequestException">
/// When the IPFS server indicates an error.
/// </exception>
public async Task<T> DoCommandAsync<T>(string command, CancellationToken cancel, string arg = null, params string[] options)
public async Task<T> DoCommandAsync<T>(string command, CancellationToken cancel, string arg = null,
params string[] options)
{
var json = await DoCommandAsync(command, cancel, arg, options);
return JsonConvert.DeserializeObject<T>(json);
Expand Down Expand Up @@ -367,7 +441,8 @@ public async Task<T> DoCommandAsync<T>(string command, CancellationToken cancel,
/// <exception cref="HttpRequestException">
/// When the IPFS server indicates an error.
/// </exception>
public async Task<Stream> PostDownloadAsync(string command, CancellationToken cancel, string arg = null, params string[] options)
public async Task<Stream> PostDownloadAsync(string command, CancellationToken cancel, string arg = null,
params string[] options)
{
var url = BuildCommand(command, arg, options);

Expand Down Expand Up @@ -402,7 +477,8 @@ public async Task<Stream> PostDownloadAsync(string command, CancellationToken ca
/// <exception cref="HttpRequestException">
/// When the IPFS server indicates an error.
/// </exception>
public async Task<Stream> DownloadAsync(string command, CancellationToken cancel, string arg = null, params string[] options)
public async Task<Stream> DownloadAsync(string command, CancellationToken cancel, string arg = null,
params string[] options)
{
var url = BuildCommand(command, arg, options);

Expand Down Expand Up @@ -435,7 +511,8 @@ public async Task<Stream> DownloadAsync(string command, CancellationToken cancel
/// <exception cref="HttpRequestException">
/// When the IPFS server indicates an error.
/// </exception>
public async Task<byte[]> DownloadBytesAsync(string command, CancellationToken cancel, string arg = null, params string[] options)
public async Task<byte[]> DownloadBytesAsync(string command, CancellationToken cancel, string arg = null,
params string[] options)
{
var url = BuildCommand(command, arg, options);

Expand Down Expand Up @@ -473,7 +550,8 @@ public async Task<byte[]> DownloadBytesAsync(string command, CancellationToken c
/// <exception cref="HttpRequestException">
/// When the IPFS server indicates an error.
/// </exception>
public async Task<String> UploadAsync(string command, CancellationToken cancel, Stream data, string name, params string[] options)
public async Task<String> UploadAsync(string command, CancellationToken cancel, Stream data, string name,
params string[] options)
{
var content = new MultipartFormDataContent();
var streamContent = new StreamContent(data);
Expand All @@ -495,6 +573,7 @@ public async Task<String> UploadAsync(string command, CancellationToken cancel,
return json;
}
}

/// <summary>
/// Perform an <see href="https://ipfs.io/docs/api/">IPFS API command</see> that
/// requires uploading of a "file".
Expand Down Expand Up @@ -523,7 +602,8 @@ public async Task<String> UploadAsync(string command, CancellationToken cancel,
/// <exception cref="HttpRequestException">
/// When the IPFS server indicates an error.
/// </exception>
public async Task<Stream> Upload2Async(string command, CancellationToken cancel, Stream data, string name, params string[] options)
public async Task<Stream> Upload2Async(string command, CancellationToken cancel, Stream data, string name,
params string[] options)
{
var content = new MultipartFormDataContent();
var streamContent = new StreamContent(data);
Expand All @@ -543,7 +623,8 @@ public async Task<Stream> Upload2Async(string command, CancellationToken cancel,
/// <summary>
/// TODO
/// </summary>
public async Task<String> UploadAsync(string command, CancellationToken cancel, byte[] data, params string[] options)
public async Task<String> UploadAsync(string command, CancellationToken cancel, byte[] data,
params string[] options)
{
var content = new MultipartFormDataContent();
var streamContent = new ByteArrayContent(data);
Expand Down Expand Up @@ -593,17 +674,21 @@ async Task<bool> ThrowOnErrorAsync(HttpResponseMessage response)
try
{
var res = JsonConvert.DeserializeObject<dynamic>(body);
message = (string)res.Message;
message = (string) res.Message;
}
catch
{
}
catch { }

throw new HttpRequestException(message);
}

/// <inheritdoc />
public IAsyncEnumerable<PingResult> Ping(MultiHash peer, int count = 10, CancellationToken cancel = new CancellationToken()) => Generic.Ping(peer, count, cancel);
public IAsyncEnumerable<PingResult> Ping(MultiHash peer, int count = 10,
CancellationToken cancel = new CancellationToken()) => Generic.Ping(peer, count, cancel);

/// <inheritdoc />
public IAsyncEnumerable<PingResult> Ping(MultiAddress address, int count = 10, CancellationToken cancel = new CancellationToken()) => Generic.Ping(address, count, cancel);
public IAsyncEnumerable<PingResult> Ping(MultiAddress address, int count = 10,
CancellationToken cancel = new CancellationToken()) => Generic.Ping(address, count, cancel);
}
}
}
28 changes: 28 additions & 0 deletions src/IpfsFIle.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using System.Runtime.Serialization;

namespace Ipfs.Http;

/// <summary>
/// IpfsFile
/// </summary>
[DataContract]
public class IpfsFile
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like this type was created for the DoAddCommand method created above, but we already have this in ipfs-net-core as IFileSystemLink and IMerkleLink, with a FileSystemLink implementation in this repo.

{
/// <summary>
/// Name
/// </summary>
[DataMember(Name = "Name")]
public string Name { get; set; }

/// <summary>
/// Hash
/// </summary>
[DataMember(Name = "Hash")]
public string Hash { get; set; }

/// <summary>
/// Size
/// </summary>
[DataMember(Name = "Size")]
public string Size { get; set; }
}
7 changes: 6 additions & 1 deletion src/IpfsHttpClient.csproj
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>netstandard2.0</TargetFrameworks>
<AssemblyName>IpfsShipyard.Ipfs.Http.Client</AssemblyName>
<RootNamespace>Ipfs.Http</RootNamespace>
<DocumentationFile>bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml</DocumentationFile>
<DebugType>full</DebugType>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<PackageReadmeFile>README.md</PackageReadmeFile>

<!-- https://semver.org/spec/v2.0.0.html -->
<Version>0.5.0</Version>
Expand All @@ -27,6 +27,7 @@
<IncludeSymbols>True</IncludeSymbols>
<PackageProjectUrl>https://github.com/ipfs-shipyard/net-ipfs-http-client</PackageProjectUrl>
<PackageIcon>icon.png</PackageIcon>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>

<PropertyGroup>
Expand Down Expand Up @@ -71,6 +72,10 @@ The obsolete IFileSystemApi.ListFileAsync was removed due to prior deprecation a
Added missing IFileSystemApi.ListAsync. Doesn't fully replace the removed IFileSystemApi.ListFileAsync, but is a step in the right direction. See https://github.com/ipfs/kubo/issues/7493#issuecomment-2016563729.
</PackageReleaseNotes>
</PropertyGroup>
<ItemGroup>
<None Include="../README.md" Pack="true" PackagePath="\" />
</ItemGroup>


<ItemGroup>
<None Include="icon.png" Pack="true" PackagePath="\" />
Expand Down
5 changes: 5 additions & 0 deletions src/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
include .env
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't need to include a makefile. The underlying commands are already as simple as can be, and our CI handles publishing updates to nuget.

nuget-push:
nuget push bin/Release/$(PACKAGE_NAME).$(VERSION).nupkg -Source https://api.nuget.org/v3/index.json -ApiKey $(NUGET_API_KEY)
nuget-pack:
nuget pack $(PACKAGE_NAME).nuspec -Version $(VERSION) -Properties Configuration=Release -OutputDirectory bin/Release
Loading