main
Paul Schneider 6 months ago
parent 89e1b5a235
commit d6180aa154
24 changed files with 255 additions and 78 deletions

7
.gitignore vendored

@ -6,14 +6,8 @@
/src/isn/.vscode/
/test/isnd.tests/obj/
/test/isnd.tests/bin/
/packages/
bower_components/
/test/isn.tests/bin
/test/isn.tests/obj/
appsettings.Testing.json
appsettings.Development.json
/wwwroot/.sass-cache/
/src/isnd/wwwroot/.sass-cache/
/src/isn.abst/bin
/src/isn.abst/obj
/src/isnd/packages/
@ -23,4 +17,5 @@ appsettings.Development.json
/artifacts/
/.vs/
/.vscode/
appsettings.Development.json

11
.vscode/tasks.json vendored

@ -90,20 +90,13 @@
],
"problemMatcher": "$msCompile"
},
{
"label": "copyTestConfig",
"command": "dotnet",
"type": "process",
"args": [ "build", "/t:CopyTestConfig" ],
"group": "test"
},
{
"label": "test",
"command": "dotnet",
"type": "process",
"options": {
"env": {
"ASPNETCORE_ENVIRONMENT": "Testing"
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"args": [
@ -112,7 +105,7 @@
"--logger:xunit"
],
"problemMatcher": "$msCompile",
"dependsOn": [ "build", "copyTestConfig"],
"dependsOn": [ "build"],
"group": "test"
},
{

@ -1,9 +1,9 @@
{
"dotnet": {
"enabled": false
"enabled": true
},
"msbuild": {
"enabled": true
"enabled": false
},
"Dnx": {
"enabled": false

@ -22,7 +22,7 @@ namespace isnd.Data.Catalog
[JsonProperty("@id")]
public string Id { get => GetId(); }
public string Id { get => id; }
private readonly string id;
public string GetId() { return id; }

@ -106,7 +106,7 @@ namespace isnd.Controllers
ApiKey key = await dbContext.ApiKeys.FirstOrDefaultAsync(k => k.Id == id && k.UserId == userid);
if (key == null)
{
ModelState.AddModelError(null, "Key not found");
ModelState.AddModelError("id", "Key not found");
return View();
}
return View("Details", new DetailModel { ApiKey = key, Name = key.Name, ProtectedValue = protector.Protect(key.Id)});

@ -7,6 +7,7 @@ using isn.abst;
namespace isnd.Controllers
{
// TODO /search GET {@id}?q={QUERY}&skip={SKIP}&take={TAKE}&prerelease={PRERELEASE}&semVerLevel={SEMVERLEVEL}&packageType={PACKAGETYPE}
public partial class PackagesController
{

@ -0,0 +1,36 @@
using Microsoft.AspNetCore.Mvc;
using isnd.Entities;
using isn.abst;
using System.Threading.Tasks;
using isnd.Data.Catalog;
namespace isnd.Controllers
{
// TODO /search GET {@id}?q={QUERY}&skip={SKIP}&take={TAKE}&prerelease={PRERELEASE}&semVerLevel={SEMVERLEVEL}&packageType={PACKAGETYPE}
public partial class PackagesController
{
// Web get the paquet
[HttpGet("~" + Constants.ApiVersionPrefix + ApiConfig.Search)]
[HttpHead("~" + Constants.ApiVersionPrefix + ApiConfig.Search)]
public async Task<IActionResult> Search(
string q=null,
int skip=0,
int take=25,
bool prerelease=false,
string semVerLevel = "2.0.0",
string packageType = null)
{
PackageRegistrationQuery query = new PackageRegistrationQuery
{
Prerelease= prerelease,
Query = q,
Skip = skip,
Take = take
};
var result = await packageManager.SearchPackageAsync(query);
return Ok(result);
}
}
}

@ -23,7 +23,7 @@ namespace isnd.Controllers
return View(new RegistrationPageIndexQueryAndResult
{
Query = model,
Result = pkgs.ToArray()
Result = pkgs.GetResults().Select(p => new PackageRegistration(apiBase, p)).ToArray()
});
}

@ -8,6 +8,7 @@ using isnd.Data;
using isnd.Entities;
using isnd.Interfaces;
using isn.Abstract;
using isn.abst;
namespace isnd.Controllers
{
@ -16,6 +17,7 @@ namespace isnd.Controllers
{
const int maxTake = 100;
private readonly Resource[] resources;
private readonly string apiBase;
private readonly ILogger<PackagesController> logger;
private readonly IDataProtector protector;
@ -36,6 +38,9 @@ namespace isnd.Controllers
this.dbContext = dbContext;
packageManager = pm;
resources = packageManager.GetResources().ToArray();
this.apiBase = isndSettings.ExternalUrl + Constants.ApiVersionPrefix;
}
}
}

@ -23,7 +23,7 @@ namespace isnd.Data.Catalog
this.apiBase = apiBase;
}
public CatalogPage(string bid, string pkgid, string apiBase, List<PackageVersion> versions) : this(pkgid, apiBase)
public CatalogPage(string pkgid, string apiBase, List<PackageVersion> versions) : this(pkgid, apiBase)
{
AddVersionRange(versions);
}

@ -1,4 +1,5 @@
using isnd.Data.Packages;
using isnd.Entities;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
@ -14,11 +15,11 @@ namespace isnd.Data.Catalog
Items = new List<CatalogPage>();
}
public PackageRegistration(string bid, string apiBase, Packages.Package pkg) : base(bid + $"/{pkg.Id}/index.json")
public PackageRegistration(string apiBase, Packages.Package pkg) : base($"{apiBase}{ApiConfig.Registration}/{pkg.Id}/index.json")
{
Items = new List<CatalogPage>
{
new CatalogPage(bid, pkg.Id, apiBase, pkg.Versions)
new CatalogPage(pkg.Id, apiBase, pkg.Versions)
};
}

@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
namespace isnd.Data.Packages
{
public interface IPackage
{
string Id { get; set; }
string OwnerId { get; set; }
string Description { get; set; }
bool Public { get; set; }
ApplicationUser Owner { get; set; }
List<PackageVersion> Versions { get; set; }
long CommitNId { get; set; }
string CommitId { get; }
Commit LatestCommit { get; set; }
DateTimeOffset CommitTimeStamp { get; set; }
}
}

@ -2,24 +2,12 @@ using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using isnd.Interfaces;
using Newtonsoft.Json;
namespace isnd.Data.Packages
{
public interface IPackage
{
string Id { get; set; }
string OwnerId { get; set; }
string Description { get; set; }
bool Public { get; set; }
ApplicationUser Owner { get; set; }
List<PackageVersion> Versions { get; set; }
long CommitNId { get; set; }
string CommitId { get; }
Commit LatestCommit { get; set; }
DateTimeOffset CommitTimeStamp { get; set; }
}
public class Package
{
@ -60,6 +48,9 @@ namespace isnd.Data.Packages
public virtual Commit LatestCommit { get; set; }
internal string GetLatestVersion()
{
return Versions.Max(v => v.NugetVersion).ToFullString();
}
}
}

@ -27,7 +27,7 @@ namespace isnd.Interfaces
Task<PackageRegistration> GetCatalogIndexAsync();
Task<PackageRegistration> GetPackageRegistrationIndexAsync(PackageRegistrationQuery query);
Task<IEnumerable<PackageRegistration>> SearchPackageAsync(PackageRegistrationQuery query);
Task<PackageSearchResult> SearchPackageAsync(PackageRegistrationQuery query);
}
}

@ -62,12 +62,11 @@ namespace isnd.Services
},
new Resource(apiBase + ApiConfig.Search,
"SearchQueryService/" + BASE_API_LEVEL)
"SearchQueryService/3.0.0-rc")
{
Comment = "Search Query service"
Comment = "Query endpoint of NuGet Search service (primary) used by RC clients"
},
new Resource(apiBase + ApiConfig.Registration,
"RegistrationsBaseUrl/Versioned")
{
@ -75,9 +74,6 @@ namespace isnd.Services
"This base URL includes SemVer 2.0.0 packages."
}
};
// TODO ? RegistrationsBaseUrl RegistrationsBaseUrl/v* Catalog
return res;
}
@ -275,31 +271,24 @@ namespace isnd.Services
foreach (var version in scope.Versions)
version.LatestCommit = dbContext.Commits.Single(c=>c.Id == version.CommitNId);
return
new PackageRegistration(bid, apiBase, scope);
new PackageRegistration(apiBase, scope);
}
public async Task<IEnumerable<PackageRegistration>> SearchPackageAsync(PackageRegistrationQuery query)
public async Task<PackageSearchResult> SearchPackageAsync(PackageRegistrationQuery query)
{
string bid = $"{apiBase}{ApiConfig.Registration}";
if (query.Query == null) query.Query = "";
var scope = (await dbContext.Packages
if (string.IsNullOrWhiteSpace(query.Query))
query.Query="";
var scope = dbContext.Packages
.Include(p => p.Owner)
.Include(p => p.Versions)
.Include(p => p.LatestCommit)
.Where(p => p.Id.StartsWith(query.Query)
&& (query.Prerelease || p.Versions.Any(p => !p.IsPrerelease)))
.OrderBy(p => p.CommitNId)
.Skip(query.Skip).Take(query.Take)
.ToListAsync()
);
scope.ForEach(pkg =>
pkg.Versions.ForEach (version =>
version.LatestCommit = dbContext.Commits.Single(c => c.Id == version.CommitNId))
);
.OrderBy(p => p.CommitNId);
return scope.Select(p => new PackageRegistration(bid, apiBase, p));
return new PackageSearchResult(await scope.Skip(query.Skip).Take(query.Take)
.ToListAsync(), apiBase, scope.Count());
}
}
}

@ -16,7 +16,6 @@ using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using System;
using Microsoft.OpenApi.Models;
using System.IO;
using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.AspNetCore.DataProtection;
@ -96,6 +95,7 @@ namespace isnd
s.SerializerSettings.ReferenceResolverProvider = () => new NSJWebApiReferenceResolver();
})
.AddXmlSerializerFormatters();
#if SWAGGER
services.AddSwaggerGen(options =>
{
options.SwaggerDoc("v1", new OpenApiInfo
@ -118,6 +118,8 @@ namespace isnd
var xmlFilename = $"{typeof(Startup).Assembly.GetName().Name}.xml";
options.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, xmlFilename));
});
#endif
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
@ -131,8 +133,10 @@ namespace isnd
{
app.UseDeveloperExceptionPage();
app.UseMigrationsEndPoint();
#if SWAGGER
app.UseSwagger();
app.UseSwaggerUI();
#endif
}
else
{

@ -0,0 +1,60 @@
using System.ComponentModel.DataAnnotations;
using isnd.Data.Catalog;
using Newtonsoft.Json;
using NuGet.Packaging.Core;
using NuGet.Protocol.Core.Types;
namespace isnd.ViewModels
{
public class PackageHit : Permalink
{
public PackageHit(string id) : base(id, "Package")
{
}
/// <summary>
/// The full SemVer 2.0.0 version string of the package (could contain build metadata)
/// </summary>
/// <value></value>
[Required]
public string version { get; set; }
/// <summary>
/// All of the versions of the package matching the prerelease parameter
/// </summary>
/// <value></value>
public SearchVersionInfo[] versions { get; set; }
public string description { get; set; }
public string[] authors { get; set; }
public string iconUrl { get; set; }
public string licenseUrl { get; set; }
public string projectUrl { get; set; }
/// <summary>
/// The absolute URL to the associated registration index
/// </summary>
/// <value></value>
public string registration { get; set; }
public string summary { get; set; }
public string[] tags { get; set; }
public string title { get; set; }
/// <summary>
/// This value can be inferred by the sum of downloads in the versions array
/// </summary>
/// <value></value>
public int totalDownloads { get; set; }
/// <summary>
/// boolean indicating whether the package is verified
/// </summary>
/// <value></value>
public bool verified { get; set; }
/// <summary>
/// The package types defined by the package author (added in SearchQueryService/3.5.0)
/// </summary>
/// <value></value>
[Required]
public PackageType[] packageTypes { get; set; }
}
}

@ -0,0 +1,48 @@
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Reflection.Metadata.Ecma335;
using System.Text.Json.Serialization;
using isnd.Data.Packages;
using isnd.Entities;
namespace isnd.ViewModels
{
public class PackageSearchResult
{
public IEnumerable<Package> GetResults()
{
return result;
}
private IEnumerable<Package> result;
[JsonIgnore]
public string ApiBase{get; private set;}
public PackageSearchResult(IEnumerable<Package> result, string apiBase, int totalHit)
{
this.result = result;
this.ApiBase = apiBase;
data=result.Select(r => NewPackageHit(apiBase, r)).ToArray();
this.totalHits = totalHit;
}
private static PackageHit NewPackageHit(string apiBase, Package package)
{
string regId = $"{apiBase}{ApiConfig.Registration}/{package.Id}/index.json";
return new PackageHit(regId)
{
version = package.GetLatestVersion(),
description = package.Description,
title = package.Id,
versions = package.Versions.Select(v => new SearchVersionInfo(regId, v)).ToArray()
};
}
public PackageHit[] data { get; protected set; }
public int totalHits { get; set; }
}
}

@ -0,0 +1,21 @@
using isnd.Data.Catalog;
using Newtonsoft.Json;
namespace isnd.ViewModels
{
public class SearchVersionInfo: Permalink
{
public SearchVersionInfo(string id, Data.PackageVersion v) : base(id, "VersionInfo")
{
Version = v.FullString;
}
[JsonProperty("version")]
public string Version { get; set; }
[JsonProperty("downloads")]
public long Downloads { get; set; }
}
}

@ -37,7 +37,6 @@
</PackageReference>
<PackageReference Include="System.Security.Cryptography.Pkcs" Version="8.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Antiforgery" Version="2.2.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="7.0.15" />
</ItemGroup>
<ItemGroup>

@ -30,14 +30,13 @@ namespace isn.tests
public void TestPush()
{
Program.LoadConfig();
var report = Program.PushPkg(new string[] { "./src/isn.abst/bin/Debug/isn.abst.1.0.1.nupkg"
+ Constants.PacketFileExtension });
var report = Program.PushPkg(new string[] { "./src/isn.abst/bin/Debug/isn.abst.1.0.1.nupkg" });
}
[Fact]
public void GetServerResourcesUsingHttpClientAsyncTest()
{
var model = SourceHelpers.GetServerResources("Https://isn.pschneider.fr/v3/index");
var model = SourceHelpers.GetServerResources("Https://isn.pschneider.fr/v3/index.json");
Console.WriteLine(JsonConvert.SerializeObject(model));
Assert.NotNull(model.Resources);
var pub = model.Resources.FirstOrDefault((r) => r.Type.StartsWith("PackagePublish/"));

@ -47,11 +47,11 @@ namespace isnd.host.tests
{
var services = serviceScope.ServiceProvider;
var dbContext = services.GetRequiredService<ApplicationDbContext>();
var paul = dbContext.Users.FirstOrDefaultAsync
(u => u.Email == "paul@pschneider.fr").Result;
if (paul != null)
var tester = dbContext.Users.FirstOrDefaultAsync
(u => u.Id == server.TestingUser.Id).Result;
if (tester != null)
{
dbContext.Users.Remove(paul);
dbContext.Users.Remove(tester);
dbContext.SaveChanges();
}
}
@ -64,10 +64,10 @@ namespace isnd.host.tests
using (var serviceScope = server.Host.Services.CreateScope())
{
var isnSettings = serviceScope.ServiceProvider.GetService<IOptions<isnd.Entities.IsndSettings>>().Value;
string pkgSourceUrl = isnSettings.ExternalUrl + apiindex;
string pkgSourceUrl = isnSettings.ExternalUrl + apiindex + ".json";
ProcessStartInfo psi = new ProcessStartInfo("nuget");
psi.ArgumentList.Add("install");
psi.ArgumentList.Add("gitversion");
psi.ArgumentList.Add("isnd");
psi.ArgumentList.Add("-PreRelease");
psi.ArgumentList.Add("-Source");
psi.ArgumentList.Add(pkgSourceUrl);
@ -114,7 +114,7 @@ namespace isnd.host.tests
}
public string SPIIndexURI
{
get => server.Addresses.First() + "/v3/index.json";
get => server.Addresses.First(a => a.StartsWith("https:")) + "/v3/index.json";
}
[Fact]
@ -128,7 +128,7 @@ namespace isnd.host.tests
PackageMetadataResource resource = await repository.GetResourceAsync<PackageMetadataResource>();
IEnumerable<IPackageSearchMetadata> packages = await resource.GetMetadataAsync(
"isn.abst",
"isnd",
includePrerelease: true,
includeUnlisted: true,
cache,
@ -149,10 +149,12 @@ namespace isnd.host.tests
[Fact]
public async Task TestFindPackageAsync()
{
ILogger logger = NullLogger.Instance;
ILogger logger = new TestLogger();
CancellationToken cancellationToken = CancellationToken.None;
SourceRepository repository = Repository.Factory.GetCoreV3(SPIIndexURI);
repository.PackageSource.AllowInsecureConnections=true;
PackageSearchResource resource = await repository.GetResourceAsync<PackageSearchResource>();
SearchFilter searchFilter = new SearchFilter(includePrerelease: true);
@ -198,12 +200,23 @@ namespace isnd.host.tests
{
public override void Log(ILogMessage message)
{
Console.WriteLine(message.Message);
string msg = $"{message.Level}: {message.Message}";
switch (message.Level)
{
case LogLevel.Debug:
case LogLevel.Information:
case LogLevel.Verbose:
case LogLevel.Minimal:
case LogLevel.Warning:
case LogLevel.Error:
Debug.WriteLine(msg);
break;
}
}
public async override Task LogAsync(ILogMessage message)
public override Task LogAsync(ILogMessage message)
{
Log(message);
return Task.Run(()=>Log(message));
}
}
}

@ -14,7 +14,10 @@
"Kestrel": {
"Endpoints": {
"Http": {
"Url": "http://localhost:5005"
"Url": "http://127.0.0.1:5002"
},
"Https": {
"Url": "https://127.0.0.1:5003"
}
}
}

Loading…