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  }