code.gitea.io/gitea@v1.21.7/routers/api/packages/nuget/api_v2.go (about) 1 // Copyright 2022 The Gitea Authors. All rights reserved. 2 // SPDX-License-Identifier: MIT 3 4 package nuget 5 6 import ( 7 "encoding/xml" 8 "strings" 9 "time" 10 11 packages_model "code.gitea.io/gitea/models/packages" 12 nuget_module "code.gitea.io/gitea/modules/packages/nuget" 13 ) 14 15 type AtomTitle struct { 16 Type string `xml:"type,attr"` 17 Text string `xml:",chardata"` 18 } 19 20 type ServiceCollection struct { 21 Href string `xml:"href,attr"` 22 Title AtomTitle `xml:"atom:title"` 23 } 24 25 type ServiceWorkspace struct { 26 Title AtomTitle `xml:"atom:title"` 27 Collection ServiceCollection `xml:"collection"` 28 } 29 30 type ServiceIndexResponseV2 struct { 31 XMLName xml.Name `xml:"service"` 32 Base string `xml:"base,attr"` 33 Xmlns string `xml:"xmlns,attr"` 34 XmlnsAtom string `xml:"xmlns:atom,attr"` 35 Workspace ServiceWorkspace `xml:"workspace"` 36 } 37 38 type EdmxPropertyRef struct { 39 Name string `xml:"Name,attr"` 40 } 41 42 type EdmxProperty struct { 43 Name string `xml:"Name,attr"` 44 Type string `xml:"Type,attr"` 45 Nullable bool `xml:"Nullable,attr"` 46 } 47 48 type EdmxEntityType struct { 49 Name string `xml:"Name,attr"` 50 HasStream bool `xml:"m:HasStream,attr"` 51 Keys []EdmxPropertyRef `xml:"Key>PropertyRef"` 52 Properties []EdmxProperty `xml:"Property"` 53 } 54 55 type EdmxFunctionParameter struct { 56 Name string `xml:"Name,attr"` 57 Type string `xml:"Type,attr"` 58 } 59 60 type EdmxFunctionImport struct { 61 Name string `xml:"Name,attr"` 62 ReturnType string `xml:"ReturnType,attr"` 63 EntitySet string `xml:"EntitySet,attr"` 64 Parameter []EdmxFunctionParameter `xml:"Parameter"` 65 } 66 67 type EdmxEntitySet struct { 68 Name string `xml:"Name,attr"` 69 EntityType string `xml:"EntityType,attr"` 70 } 71 72 type EdmxEntityContainer struct { 73 Name string `xml:"Name,attr"` 74 IsDefaultEntityContainer bool `xml:"m:IsDefaultEntityContainer,attr"` 75 EntitySet EdmxEntitySet `xml:"EntitySet"` 76 FunctionImports []EdmxFunctionImport `xml:"FunctionImport"` 77 } 78 79 type EdmxSchema struct { 80 Xmlns string `xml:"xmlns,attr"` 81 Namespace string `xml:"Namespace,attr"` 82 EntityType *EdmxEntityType `xml:"EntityType,omitempty"` 83 EntityContainer *EdmxEntityContainer `xml:"EntityContainer,omitempty"` 84 } 85 86 type EdmxDataServices struct { 87 XmlnsM string `xml:"xmlns:m,attr"` 88 DataServiceVersion string `xml:"m:DataServiceVersion,attr"` 89 MaxDataServiceVersion string `xml:"m:MaxDataServiceVersion,attr"` 90 Schema []EdmxSchema `xml:"Schema"` 91 } 92 93 type EdmxMetadata struct { 94 XMLName xml.Name `xml:"edmx:Edmx"` 95 XmlnsEdmx string `xml:"xmlns:edmx,attr"` 96 Version string `xml:"Version,attr"` 97 DataServices EdmxDataServices `xml:"edmx:DataServices"` 98 } 99 100 var Metadata = &EdmxMetadata{ 101 XmlnsEdmx: "http://schemas.microsoft.com/ado/2007/06/edmx", 102 Version: "1.0", 103 DataServices: EdmxDataServices{ 104 XmlnsM: "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata", 105 DataServiceVersion: "2.0", 106 MaxDataServiceVersion: "2.0", 107 Schema: []EdmxSchema{ 108 { 109 Xmlns: "http://schemas.microsoft.com/ado/2006/04/edm", 110 Namespace: "NuGetGallery.OData", 111 EntityType: &EdmxEntityType{ 112 Name: "V2FeedPackage", 113 HasStream: true, 114 Keys: []EdmxPropertyRef{ 115 {Name: "Id"}, 116 {Name: "Version"}, 117 }, 118 Properties: []EdmxProperty{ 119 { 120 Name: "Id", 121 Type: "Edm.String", 122 }, 123 { 124 Name: "Version", 125 Type: "Edm.String", 126 }, 127 { 128 Name: "NormalizedVersion", 129 Type: "Edm.String", 130 Nullable: true, 131 }, 132 { 133 Name: "Authors", 134 Type: "Edm.String", 135 Nullable: true, 136 }, 137 { 138 Name: "Created", 139 Type: "Edm.DateTime", 140 }, 141 { 142 Name: "Dependencies", 143 Type: "Edm.String", 144 }, 145 { 146 Name: "Description", 147 Type: "Edm.String", 148 }, 149 { 150 Name: "DownloadCount", 151 Type: "Edm.Int64", 152 }, 153 { 154 Name: "LastUpdated", 155 Type: "Edm.DateTime", 156 }, 157 { 158 Name: "Published", 159 Type: "Edm.DateTime", 160 }, 161 { 162 Name: "PackageSize", 163 Type: "Edm.Int64", 164 }, 165 { 166 Name: "ProjectUrl", 167 Type: "Edm.String", 168 Nullable: true, 169 }, 170 { 171 Name: "ReleaseNotes", 172 Type: "Edm.String", 173 Nullable: true, 174 }, 175 { 176 Name: "RequireLicenseAcceptance", 177 Type: "Edm.Boolean", 178 Nullable: false, 179 }, 180 { 181 Name: "Title", 182 Type: "Edm.String", 183 Nullable: true, 184 }, 185 { 186 Name: "VersionDownloadCount", 187 Type: "Edm.Int64", 188 Nullable: false, 189 }, 190 }, 191 }, 192 }, 193 { 194 Xmlns: "http://schemas.microsoft.com/ado/2006/04/edm", 195 Namespace: "NuGetGallery", 196 EntityContainer: &EdmxEntityContainer{ 197 Name: "V2FeedContext", 198 IsDefaultEntityContainer: true, 199 EntitySet: EdmxEntitySet{ 200 Name: "Packages", 201 EntityType: "NuGetGallery.OData.V2FeedPackage", 202 }, 203 FunctionImports: []EdmxFunctionImport{ 204 { 205 Name: "Search", 206 ReturnType: "Collection(NuGetGallery.OData.V2FeedPackage)", 207 EntitySet: "Packages", 208 Parameter: []EdmxFunctionParameter{ 209 { 210 Name: "searchTerm", 211 Type: "Edm.String", 212 }, 213 }, 214 }, 215 { 216 Name: "FindPackagesById", 217 ReturnType: "Collection(NuGetGallery.OData.V2FeedPackage)", 218 EntitySet: "Packages", 219 Parameter: []EdmxFunctionParameter{ 220 { 221 Name: "id", 222 Type: "Edm.String", 223 }, 224 }, 225 }, 226 }, 227 }, 228 }, 229 }, 230 }, 231 } 232 233 type FeedEntryCategory struct { 234 Term string `xml:"term,attr"` 235 Scheme string `xml:"scheme,attr"` 236 } 237 238 type FeedEntryLink struct { 239 Rel string `xml:"rel,attr"` 240 Href string `xml:"href,attr"` 241 } 242 243 type TypedValue[T any] struct { 244 Type string `xml:"type,attr,omitempty"` 245 Value T `xml:",chardata"` 246 } 247 248 type FeedEntryProperties struct { 249 Version string `xml:"d:Version"` 250 NormalizedVersion string `xml:"d:NormalizedVersion"` 251 Authors string `xml:"d:Authors"` 252 Dependencies string `xml:"d:Dependencies"` 253 Description string `xml:"d:Description"` 254 VersionDownloadCount TypedValue[int64] `xml:"d:VersionDownloadCount"` 255 DownloadCount TypedValue[int64] `xml:"d:DownloadCount"` 256 PackageSize TypedValue[int64] `xml:"d:PackageSize"` 257 Created TypedValue[time.Time] `xml:"d:Created"` 258 LastUpdated TypedValue[time.Time] `xml:"d:LastUpdated"` 259 Published TypedValue[time.Time] `xml:"d:Published"` 260 ProjectURL string `xml:"d:ProjectUrl,omitempty"` 261 ReleaseNotes string `xml:"d:ReleaseNotes,omitempty"` 262 RequireLicenseAcceptance TypedValue[bool] `xml:"d:RequireLicenseAcceptance"` 263 Title string `xml:"d:Title"` 264 } 265 266 type FeedEntry struct { 267 XMLName xml.Name `xml:"entry"` 268 Xmlns string `xml:"xmlns,attr,omitempty"` 269 XmlnsD string `xml:"xmlns:d,attr,omitempty"` 270 XmlnsM string `xml:"xmlns:m,attr,omitempty"` 271 Base string `xml:"xml:base,attr,omitempty"` 272 ID string `xml:"id"` 273 Category FeedEntryCategory `xml:"category"` 274 Links []FeedEntryLink `xml:"link"` 275 Title TypedValue[string] `xml:"title"` 276 Updated time.Time `xml:"updated"` 277 Author string `xml:"author>name"` 278 Summary string `xml:"summary"` 279 Properties *FeedEntryProperties `xml:"m:properties"` 280 Content string `xml:",innerxml"` 281 } 282 283 type FeedResponse struct { 284 XMLName xml.Name `xml:"feed"` 285 Xmlns string `xml:"xmlns,attr,omitempty"` 286 XmlnsD string `xml:"xmlns:d,attr,omitempty"` 287 XmlnsM string `xml:"xmlns:m,attr,omitempty"` 288 Base string `xml:"xml:base,attr,omitempty"` 289 ID string `xml:"id"` 290 Title TypedValue[string] `xml:"title"` 291 Updated time.Time `xml:"updated"` 292 Links []FeedEntryLink `xml:"link"` 293 Entries []*FeedEntry `xml:"entry"` 294 Count int64 `xml:"m:count"` 295 } 296 297 func createFeedResponse(l *linkBuilder, totalEntries int64, pds []*packages_model.PackageDescriptor) *FeedResponse { 298 entries := make([]*FeedEntry, 0, len(pds)) 299 for _, pd := range pds { 300 entries = append(entries, createEntry(l, pd, false)) 301 } 302 303 links := []FeedEntryLink{ 304 {Rel: "self", Href: l.Base}, 305 } 306 if l.Next != nil { 307 links = append(links, FeedEntryLink{ 308 Rel: "next", 309 Href: l.GetNextURL(), 310 }) 311 } 312 313 return &FeedResponse{ 314 Xmlns: "http://www.w3.org/2005/Atom", 315 Base: l.Base, 316 XmlnsD: "http://schemas.microsoft.com/ado/2007/08/dataservices", 317 XmlnsM: "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata", 318 ID: "http://schemas.datacontract.org/2004/07/", 319 Updated: time.Now(), 320 Links: links, 321 Count: totalEntries, 322 Entries: entries, 323 } 324 } 325 326 func createEntryResponse(l *linkBuilder, pd *packages_model.PackageDescriptor) *FeedEntry { 327 return createEntry(l, pd, true) 328 } 329 330 func createEntry(l *linkBuilder, pd *packages_model.PackageDescriptor, withNamespace bool) *FeedEntry { 331 metadata := pd.Metadata.(*nuget_module.Metadata) 332 333 id := l.GetPackageMetadataURL(pd.Package.Name, pd.Version.Version) 334 335 // Workaround to force a self-closing tag to satisfy XmlReader.IsEmptyElement used by the NuGet client. 336 // https://learn.microsoft.com/en-us/dotnet/api/system.xml.xmlreader.isemptyelement 337 content := `<content type="application/zip" src="` + l.GetPackageDownloadURL(pd.Package.Name, pd.Version.Version) + `"/>` 338 339 createdValue := TypedValue[time.Time]{ 340 Type: "Edm.DateTime", 341 Value: pd.Version.CreatedUnix.AsLocalTime(), 342 } 343 344 entry := &FeedEntry{ 345 ID: id, 346 Category: FeedEntryCategory{Term: "NuGetGallery.OData.V2FeedPackage", Scheme: "http://schemas.microsoft.com/ado/2007/08/dataservices/scheme"}, 347 Links: []FeedEntryLink{ 348 {Rel: "self", Href: id}, 349 {Rel: "edit", Href: id}, 350 }, 351 Title: TypedValue[string]{Type: "text", Value: pd.Package.Name}, 352 Updated: pd.Version.CreatedUnix.AsLocalTime(), 353 Author: metadata.Authors, 354 Content: content, 355 Properties: &FeedEntryProperties{ 356 Version: pd.Version.Version, 357 NormalizedVersion: pd.Version.Version, 358 Authors: metadata.Authors, 359 Dependencies: buildDependencyString(metadata), 360 Description: metadata.Description, 361 VersionDownloadCount: TypedValue[int64]{Type: "Edm.Int64", Value: pd.Version.DownloadCount}, 362 DownloadCount: TypedValue[int64]{Type: "Edm.Int64", Value: pd.Version.DownloadCount}, 363 PackageSize: TypedValue[int64]{Type: "Edm.Int64", Value: pd.CalculateBlobSize()}, 364 Created: createdValue, 365 LastUpdated: createdValue, 366 Published: createdValue, 367 ProjectURL: metadata.ProjectURL, 368 ReleaseNotes: metadata.ReleaseNotes, 369 RequireLicenseAcceptance: TypedValue[bool]{Type: "Edm.Boolean", Value: metadata.RequireLicenseAcceptance}, 370 Title: pd.Package.Name, 371 }, 372 } 373 374 if withNamespace { 375 entry.Xmlns = "http://www.w3.org/2005/Atom" 376 entry.Base = l.Base 377 entry.XmlnsD = "http://schemas.microsoft.com/ado/2007/08/dataservices" 378 entry.XmlnsM = "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" 379 } 380 381 return entry 382 } 383 384 func buildDependencyString(metadata *nuget_module.Metadata) string { 385 var b strings.Builder 386 first := true 387 for group, deps := range metadata.Dependencies { 388 for _, dep := range deps { 389 if !first { 390 b.WriteByte('|') 391 } 392 first = false 393 394 b.WriteString(dep.ID) 395 b.WriteByte(':') 396 b.WriteString(dep.Version) 397 b.WriteByte(':') 398 b.WriteString(group) 399 } 400 } 401 return b.String() 402 }