A valid package index page

fixed-page-index 1.0.1
Paul Schneider 2 years ago
parent 88c8480911
commit 58f77a06ea
15 changed files with 210 additions and 153 deletions

@ -20,11 +20,11 @@ namespace isn
currentSource = settings.DefaultSource;
}
}
static OptionSet storeoptions = new OptionSet {
static readonly OptionSet storeoptions = new OptionSet {
{ "s|source=", "use source", val => currentSource = currentSource ?? val },
{ "h|help", "show this message and exit", h => shouldShowPushHelp = h != null },
};
private static string _configFileName =
private static readonly string _configFileName =
Path.Combine(
Path.Combine(Environment.GetFolderPath(
Environment.SpecialFolder.UserProfile), ".isn"),
@ -32,21 +32,21 @@ namespace isn
;
public const string push = "push";
static OptionSet options = new OptionSet {
static readonly OptionSet options = new OptionSet {
{ "h|help", "show this message and exit", h => shouldShowHelp = h != null },
};
static OptionSet pushoptions = new OptionSet {
static readonly OptionSet pushoptions = new OptionSet {
{ "k|api-key=", "use api key", val => apiKey = apiKey ?? val },
{ "p|store-api-key", "store used api key (=<true|false>)", val => storApiKey = val != null },
{ "s|source=", "use source", val => currentSource = currentSource ?? val },
{ "h|help", "show this message and exit", h => shouldShowPushHelp = h != null },
};
static OptionSet sourceoptions = new OptionSet {
static readonly OptionSet sourceoptions = new OptionSet {
{ "h|help", "show this message and exit", h => shouldShowSourceHelp = h != null },
};
static OptionSet showOptions = new OptionSet {
static readonly OptionSet showOptions = new OptionSet {
{ "h|help", "show this message and exit", h => shouldShowSourceHelp = h != null },
};
@ -154,7 +154,7 @@ namespace isn
var pushCmd = new Command(push)
{
Run = async sargs =>
Run = sargs =>
{
var pargs = pushoptions.Parse(sargs);
if (shouldShowPushHelp)

@ -24,28 +24,32 @@ namespace isnd.Controllers
return Ok(PackageManager.CurrentCatalogPages[int.Parse(id)]);
}
[HttpGet(_pkgRootPrefix + "{apiVersion}/" + ApiConfig.Registration + "/{id}/index.json")]
public async Task<IActionResult> CatalogRegistrationAsync(string apiVersion, string id)
[HttpGet(_pkgRootPrefix + "{apiVersion}/" + ApiConfig.Registration + "/{id}/{lower}.json")]
public IActionResult CatalogRegistration(string apiVersion, string id, string lower)
{
var pkgs = packageManager.SearchById(id, null, null);
if (pkgs == null) return NotFound();
return Ok(pkgs);
if (lower.Equals("index", System.StringComparison.OrdinalIgnoreCase))
{
var query = new Data.Catalog.RegistrationPageIndexQuery
{
Query = id,
Prerelease = true
};
var index = packageManager.GetPackageRegistrationIndex(query);
if (index == null) return NotFound();
// query.TotalHits = result.Items.Select(i=>i.Items.Length).Aggregate((a,b)=>a+b);
return Ok(index);
}
var leaf = packageManager.SearchById(id,lower,null);
if (leaf.Count()==0) return NotFound(new { id, lower });
return Ok(leaf.First());
}
[HttpGet(_pkgRootPrefix + ApiConfig.CatalogLeaf + "/{id}/{version}/{lower}/index.json")]
public async Task<IActionResult> CatalogLeafAsync(string id, string pversion, string lower)
public IActionResult CatalogLeaf(string id, string pversion, string lower)
{
bool askForindex = lower == null;
if (false)
{
if (!NuGetVersion.TryParse(lower, out NuGetVersion version))
return BadRequest(lower);
var pkgFromname = packageManager.GetVersions(id, version, true);
if (pkgFromname == null) return NotFound();
return Ok(pkgFromname);
}
var pkgvs = this.packageManager.GetCatalogLeaf(id, pversion, lower).ToArray();
if (pkgvs.Count() == 0) return NotFound();
List<string> types = pkgvs.Select(

@ -0,0 +1,10 @@
using isnd.Data.Catalog;
namespace isnd
{
public class RegistrationPageIndexQueryAndResult
{
public RegistrationPageIndexQuery Query { get; set; }
public RegistrationPageIndex Result { get; set; }
}
}

@ -11,7 +11,7 @@ namespace isnd.Controllers
{
// GET {@id}?q={QUERY}&skip={SKIP}&take={TAKE}&prerelease={PRERELEASE}&semVerLevel={SEMVERLEVEL}&packageType={PACKAGETYPE}
[HttpGet(_pkgRootPrefix + ApiConfig.Search)]
public async Task<IActionResult> Search(
public IActionResult Search(
string q,
int skip = 0,
int take = 25,

@ -17,7 +17,8 @@ namespace isnd.Controllers
// Web search
public async Task<IActionResult> Index(RegistrationPageIndexQuery model)
{
return View(packageManager.GetPackageRegistrationIndex(model));
return View(new RegistrationPageIndexQueryAndResult{Query = model,
Result = packageManager.GetPackageRegistrationIndex(model)});
}
public async Task<IActionResult> Details(string pkgid)

@ -1,27 +0,0 @@
using System.Collections.Generic;
using Newtonsoft.Json;
namespace isnd.Data.Catalog
{
public class RegistrationPageIndexQuery : RegistrationPageIndex
{
public RegistrationPageIndexQuery() : base("")
{
}
public RegistrationPageIndexQuery(string bid, IEnumerable<RegistrationLeaf> leaves) : base(bid, leaves)
{
}
[JsonProperty("query")]
public string Query { get; set; }
[JsonProperty("prerelease")]
public bool Prerelease { get; set; }
public int Skip { get; set; }
public int Take { get; set; } = 25;
public int TotalHits { get; set; }
}
}

@ -1,18 +1,62 @@
using System;
using System.Collections.Generic;
using System.Linq;
using isnd.Data.Packages;
using Newtonsoft.Json;
using NuGet.Versioning;
namespace isnd.Data.Catalog
{
public class RegistrationPage
public class RegistrationPage
{
[JsonProperty("@id")]
[JsonRequired]
public string Id { get; }
public RegistrationPage (string id)
public string Id { get; protected set;}
private List<PackageVersion> items;
protected string Bid { get ; private set; }
public string DlBase { get; }
public RegistrationPage (string bid, string dlBase)
{
Bid = bid;
DlBase = dlBase;
this.items = new List<PackageVersion>();
}
public RegistrationPage (string bid, string pkgid, string dlBase, IEnumerable<PackageVersion> items)
{
Bid = bid;
Parent = Bid + "/index.json";
DlBase = dlBase;
this.items = new List<PackageVersion>(items);
SetPackageId(pkgid);
UpdateCompact();
}
protected void SetPackageId(string pkgid)
{
this.Id = Bid + "/" + pkgid + "/index.json";
}
private void UpdateCompact()
{
Id = id;
Items = new List<RegistrationLeaf>();
NuGetVersion upper = new NuGetVersion(0,0,0);
// Assert.True(items.All(p=>p.Id == id));
foreach (var p in items)
{
if (upper < p.NugetVersion) upper = p.NugetVersion;
}
Upper = upper.ToFullString();
NuGetVersion lower = upper;
foreach (var p in items)
{
if (lower > p.NugetVersion) lower = p.NugetVersion;
}
Lower = lower.ToFullString();
}
/// <summary>
@ -21,19 +65,26 @@ namespace isnd.Data.Catalog
/// <value></value>
[JsonProperty("items")]
public List<RegistrationLeaf> Items { get; set; }
public RegistrationLeaf[] Items { get => items.Select((p) => p.ToLeave(Bid, DlBase)).ToArray(); }
public void AddVersionRange(IEnumerable<PackageVersion> vitems)
{
if (vitems.Count() == 0) return;
items.AddRange(vitems);
UpdateCompact();
}
/// <summary>
/// The highest SemVer 2.0.0 version in the page (inclusive)
/// </summary>
/// <value></value>
[JsonProperty("upper"), JsonRequired]
public Version Upper { get; set; }
public string Upper { get; private set; }
/// <summary>
/// The lowest SemVer 2.0.0 version in the page (inclusive)
/// </summary>
/// <value></value>
[JsonProperty("lower"), JsonRequired]
public Version Lower { get; set; }
public string Lower { get; private set; }
/// <summary>
/// The URL to the registration index

@ -1,5 +1,7 @@
using isnd.Data.Packages;
using Newtonsoft.Json;
using System.Collections.Generic;
using System.Linq;
namespace isnd.Data.Catalog
{
@ -10,30 +12,24 @@ namespace isnd.Data.Catalog
/// </summary>
/// <value></value>
[JsonProperty("@id")]
public string Id { get; set; }
public RegistrationPageIndex(string id)
public string Id { get; protected set; }
public RegistrationPageIndex(string bid, string id)
{
Id = id;
Id = bid + "/" + id + "/index.json";
Items = new List<RegistrationPage>();
}
public RegistrationPageIndex(IEnumerable<RegistrationPage> pages)
{
Items = new List<RegistrationPage>(pages);
}
public RegistrationPageIndex(string id, IEnumerable<RegistrationLeaf> leaves) : this(id)
public RegistrationPageIndex(string bid, string id, string dlBase, IEnumerable<Package> pkgs) : this(bid, id)
{
// leaves;
this.Items = new List<RegistrationPage>
(
)
{
};
(pkgs.GroupBy(l => l.Id)
.Select(lg => new RegistrationPage
(bid, lg.Key, dlBase, lg.ToArray()
.Select(p => p.Versions).Aggregate
((l, m) => { l.AddRange(m); return l.ToList(); })
)));
}
[JsonProperty("count")]

@ -0,0 +1,28 @@
using System.Collections.Generic;
using isnd.Data.Packages;
using Newtonsoft.Json;
namespace isnd.Data.Catalog
{
public class RegistrationPageIndexQuery
{
public RegistrationPageIndexQuery()
{
}
[JsonProperty("query")]
public string Query { get; set; }
[JsonProperty("prerelease")]
public bool Prerelease { get; set; } = true;
[JsonProperty("skip")]
public int Skip { get; set; } = 0;
[JsonProperty("take")]
public int Take { get; set; } = 25;
[JsonProperty("totalHits")]
public int TotalHits { get; set; }
}
}

@ -2,6 +2,7 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using isn.abst;
using isnd.Data.Catalog;
using isnd.Data.Packages;
using isnd.Data.Packages.Catalog;
using Newtonsoft.Json;
@ -28,6 +29,11 @@ namespace isnd.Data
public int Revision { get; set; }
/// <summary>
/// Full version string
/// </summary>
/// <value></value>
[StringLength(256)]
[Required][Key]
public string FullString { get; set; }
@ -48,13 +54,30 @@ namespace isnd.Data
public string CommitId { get => CommitNId.ToString(); }
public virtual Commit LatestCommit {get; set; }
public string NugetLink => $"/{Constants.PaquetFileEstension}/{PackageId}/{FullString}/{PackageId}-{FullString}."
public string NugetLink => $"{Constants.PaquetFileEstension}/{PackageId}/{FullString}/{PackageId}-{FullString}."
+ Constants.PaquetFileEstension;
public string NuspecLink => $"/{Constants.SpecFileEstension}/{PackageId}/{FullString}/{PackageId}-{FullString}."
public string NuspecLink => $"{Constants.SpecFileEstension}/{PackageId}/{FullString}/{PackageId}-{FullString}."
+ Constants.SpecFileEstension;
public string SementicVersionString { get => $"{Major}.{Minor}.{Patch}"; }
public NuGetVersion NugetVersion { get => new NuGetVersion(FullString); }
public RegistrationLeaf ToLeave(string bid, string dlbase)
{
string leaveid = bid + "/" + this.PackageId + "/" + FullString + ".json";
return new RegistrationLeaf
{
Id = leaveid,
PackageContent = dlbase + NugetLink,
Entry = new CatalogEntry
{
Id = leaveid,
idp = PackageId,
version = FullString,
authors = $"{this.Package.Owner.FullName} <${Package.Owner.Email}>"
}
};
}
}
}

@ -2,12 +2,11 @@ namespace isnd.Helpers
{
public static class PackageIdHelpers
{
internal static bool SeparatedByMinusMatch(string id, string q)
{
foreach (var part in id.Split('-'))
{
if (part.StartsWith(q, System.StringComparison.OrdinalIgnoreCase)) return true;
if (part.Equals(q, System.StringComparison.OrdinalIgnoreCase)) return true;
}
return false;
}
@ -24,7 +23,7 @@ namespace isnd.Helpers
while (id.Length > i && char.IsLower(id[i])) i++;
if (i == 0) break;
id = id.Substring(i);
if (id.StartsWith(query, System.StringComparison.OrdinalIgnoreCase)) return true;
if (id.Equals(query, System.StringComparison.OrdinalIgnoreCase)) return true;
}
return false;
}

@ -11,35 +11,10 @@ namespace isnd.Helpers
{
public static class PackageVersionHelpers
{
public static bool IsOwner(this ClaimsPrincipal user, PackageVersion v)
{
var userId = user.FindFirstValue(ClaimTypes.NameIdentifier);
return v.Package.OwnerId == userId;
}
public static RegistrationPageIndex CreateRegistrationPages(this IEnumerable<RegistrationLeaf> leaves,
string bid)
{
List<RegistrationPage> pages = new List<RegistrationPage>();
var ids = leaves.Select(l => l.Entry.Id).Distinct().ToArray();
foreach (var id in ids)
{
var lbi = leaves.Where(l=>l.Entry.Id == id).OrderBy(l=>
new Version(l.Entry.version));
var latest = new Version(lbi.Last().Entry.version);
pages.Add(new RegistrationPage(bid + id + "/" + latest.Major + "."
+ latest.Minor + "."
+ latest.Build)
{
Count = lbi.Count(),
Lower = new Version(lbi.First().Entry.version),
Upper = latest,
Items = lbi.ToList(),
Parent = bid + id + "/" + ApiConfig.IndexDotJson,
});
}
return new RegistrationPageIndex(pages);
}
}
}

@ -28,7 +28,7 @@ namespace isnd.Interfaces
IEnumerable<RegistrationLeaf> SearchById(string pkgId, string semver, string pkgType);
RegistrationPageIndex GetCatalogIndex();
RegistrationPageIndexQuery GetPackageRegistrationIndex(RegistrationPageIndexQuery query);
RegistrationPageIndex GetPackageRegistrationIndex(RegistrationPageIndexQuery query);
}
}

@ -118,7 +118,7 @@ namespace isnd.Services
Type = "RegistrationsBaseUrl/3.6.0",
Comment = "Base URL of storage where isn package registration info is stored in GZIP format. This base URL includes SemVer 2.0.0 packages."
});
return res;
}
@ -184,9 +184,9 @@ namespace isnd.Services
var oldIndex = CurrentCatalogIndex;
var oldPages = CurrentCatalogPages;
string baseid = extUrl + ApiConfig.Catalog;
string bidreg = $"{extUrl}v3.4.0/{ApiConfig.Registration}/";
string bidreg = $"{extUrl}v3.4.0/{ApiConfig.Registration}";
string basepageid = extUrl + ApiConfig.CatalogPage;
CurrentCatalogIndex = new RegistrationPageIndex(baseid);
CurrentCatalogIndex = new RegistrationPageIndex(baseid,"index");
CurrentCatalogPages = new List<RegistrationPage>();
var scope = dbContext.Commits.OrderBy(c => c.TimeStamp);
@ -197,14 +197,14 @@ namespace isnd.Services
{
if (i >= this.isndSettings.CatalogPageLen)
{
page = new RegistrationPage(basepageid + "-" + p++)
page = new RegistrationPage(basepageid, extUrl)
{
Parent = baseid,
CommitId = commit.CommitId,
CommitTimeStamp = commit.CommitTimeStamp
};
CurrentCatalogPages.Add(page);
var pageRef = new RegistrationPage(page.Id)
var pageRef = new RegistrationPage(page.Id, extUrl)
{
CommitId = commit.CommitId,
CommitTimeStamp = commit.CommitTimeStamp
@ -218,27 +218,27 @@ namespace isnd.Services
.Include(pkg => pkg.LatestVersion)
.Where(
pkg => pkg.Versions.Count(v => v.CommitId == commit.CommitId) > 0
).GroupBy((q)=> q.Id);
).GroupBy((q) => q.Id);
// pkg.Versions.OrderByDescending(vi => vi.CommitNId).First().FullString
foreach (var pkgid in validPkgs)
{
StringBuilder refid = new StringBuilder(bidreg);
refid.AppendFormat("{0}/",
pkgid.Key);
/* var pkgref = new PackageRef
{
Version = v.FullString,
LastCommit = v.LatestCommit,
CommitId = v.LatestCommit.CommitId,
CommitTimeStamp = v.LatestCommit.CommitTimeStamp,
RefId = refid.ToString(),
Id = v.PackageId,
RefType = v.LatestCommit.Action == PackageAction.PublishPackage
? "nuget:PackageDetails" :
"nuget:PackageDelete"
}; */
foreach (var pkgv in pkgid)
page.Items.Add(pkgv.ToLeave(bidreg));
/* var pkgref = new PackageRef
{
Version = v.FullString,
LastCommit = v.LatestCommit,
CommitId = v.LatestCommit.CommitId,
CommitTimeStamp = v.LatestCommit.CommitTimeStamp,
RefId = refid.ToString(),
Id = v.PackageId,
RefType = v.LatestCommit.Action == PackageAction.PublishPackage
? "nuget:PackageDetails" :
"nuget:PackageDelete"
}; */
foreach (var pkgv in pkgid)
page.AddVersionRange(pkgv.Versions);
}
reason = commit;
i++;
@ -293,8 +293,8 @@ namespace isnd.Services
return dbContext.PackageVersions
.Include(v => v.Package)
.Include(v => v.LatestCommit)
.Where(v => v.PackageId == pkgId
&& (semver == null ||
.Where(v => v.PackageId == pkgId
&& (semver == null ||
semver.StartsWith(v.SementicVersionString))
&& (pkgType == null || pkgType == v.Type));
}
@ -312,13 +312,16 @@ namespace isnd.Services
public IEnumerable<Data.Catalog.RegistrationLeaf> SearchById(string pkgId, string semver, string pkgType)
{
string bid = $"{extUrl}v3.4.0/{ApiConfig.Registration}/";
string bid = $"{extUrl}v3.4.0/{ApiConfig.Registration}";
return dbContext.PackageVersions
.Include(v => v.Package)
.Include(v => v.Package.Owner)
.Include(v => v.LatestCommit)
.Where(v => v.PackageId == pkgId && semver.StartsWith(v.SementicVersionString)
&& (pkgType == null || pkgType == v.Type)).Select(p => p.Package.ToLeave(bid));
.Where(v => v.PackageId == pkgId && semver.Equals(v.FullString, StringComparison.OrdinalIgnoreCase)
&& (pkgType == null || pkgType == v.Type))
.OrderByDescending(p=> p.CommitId)
.Select(p => p.Package.ToLeave(bid))
;
}
public PackageVersion GetPackage(string pkgId, string semver, string pkgType)
{
@ -329,24 +332,18 @@ namespace isnd.Services
&& (pkgType == null || pkgType == v.Type));
}
public RegistrationPageIndexQuery GetPackageRegistrationIndex(RegistrationPageIndexQuery query)
public RegistrationPageIndex GetPackageRegistrationIndex(RegistrationPageIndexQuery query)
{
// RegistrationPageIndexAndQuery
var scope = dbContext.Packages
.Include(p => p.Versions)
.Include(p => p.Owner)
.Where(
p => (PackageIdHelpers.CamelCaseMatch(p.Id, query.Query)
|| PackageIdHelpers.SeparatedByMinusMatch(p.Id, query.Query))
&& (query.Prerelease || p.Versions.Any(v => !v.IsPrerelease))
);
var scope = dbContext.Packages.Include(p => p.Versions).Include(p => p.Owner)
.Where(p => (PackageIdHelpers.CamelCaseMatch(p.Id, query.Query)
|| PackageIdHelpers.SeparatedByMinusMatch(p.Id, query.Query))
&& (query.Prerelease || p.Versions.Any(v => !v.IsPrerelease)));
var total = scope.Count();
var pkgs = scope.Skip(query.Skip).Take(query.Take).ToArray();
string bid = $"{extUrl}v3.4.0/{ApiConfig.Registration}/";
var leaves = pkgs.Select(p => p.ToLeave(bid));
return
new RegistrationPageIndexQuery(bid, leaves);
string bid = $"{extUrl}v3.4.0/{ApiConfig.Registration}";
return
new RegistrationPageIndex(bid, query.Query, extUrl, pkgs);
}
}
}

@ -1,4 +1,4 @@
@model RegistrationPageIndexQuery
@model isnd.RegistrationPageIndexQueryAndResult
@{
ViewData["Title"] = "Index";
@ -11,9 +11,9 @@
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Query" class="control-label"></label>
<input asp-for="Query" class="form-control" />
<label asp-for="TotalHits" class="control-label"></label>
<label asp-for="Query.Query" class="control-label"></label>
<input asp-for="Query.Query" class="form-control" />
<label asp-for="Query.TotalHits" class="control-label"></label>
</div>
<div class="form-group">
<input type="submit" value="Find" class="btn btn-default" />
@ -25,16 +25,16 @@
<thead>
<tr>
<th>
@Html.DisplayNameFor(model => model.Items[0].Id)
@Html.DisplayNameFor(model => model.Result.Items[0].Id)
</th>
<th>
@Html.DisplayNameFor(model => model.Items[0].CommitId)
@Html.DisplayNameFor(model => model.Result.Items[0].CommitId)
</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var page in Model.Items) {
@foreach (var page in Model.Result.Items) {
@foreach (var item in page.Items) {
<tr>
<td>

Loading…