From a6985a55ad3db24c9b61482a6cf88819a6d57e2d Mon Sep 17 00:00:00 2001 From: Pragyesh Mishra Date: Fri, 28 Mar 2025 14:57:56 +0000 Subject: [PATCH] [feat]: [AH-1033]: nuget implmentation (#3570) * [AH-1033]: [feat]: pr check * [feat]: [AH-1033]: pr check * [feat]: [AH-1033]: pr check * [feat]: [AH-1033]: pr check * [feat]: [AH-1033]: pr check * [feat]: [AH-1033]: pr check * [feat]: [AH-1033]: upload flow * [feat]: [AH-1033]: resolve conflicts * [AH-1033]: [feat]: nuget service endpoint api * [AH-1036]: [feat]: added routes and apis for nuget * [AH-1033]: nuget implmentation * [AH-993]: Updated local file * [AH-993]: Added support for local and created arch to support different package types * Merge branch 'main' of https://git0.harness.io/l7B_kbSEQD2wjrM7PShm5w/PROD/Harness_Commons/gitness into AH-993-upstream-implementation * [AH-993]: temp commit * [AH-993]: Merge commit: * [AH-993]: temp update --- .golangci.yml | 2 + cmd/gitness/wire_gen.go | 7 +- .../api/controller/pkg/nuget/controller.go | 75 +++++ .../controller/pkg/nuget/download_package.go | 76 +++++ .../app/api/controller/pkg/nuget/response.go | 52 ++++ .../controller/pkg/nuget/service_endpoint.go | 74 +++++ .../controller/pkg/nuget/upload_package.go | 75 +++++ registry/app/api/controller/pkg/nuget/wire.go | 40 +++ .../app/api/handler/nuget/download_package.go | 64 ++++ registry/app/api/handler/nuget/handler.go | 72 +++++ .../app/api/handler/nuget/service_endpoint.go | 48 +++ .../app/api/handler/nuget/upload_package.go | 42 +++ registry/app/api/handler/packages/handler.go | 2 + registry/app/api/openapi/api.yaml | 1 + .../contracts/artifact/services.gen.go | 286 ++++++++++-------- .../openapi/contracts/artifact/types.gen.go | 3 +- registry/app/api/router/packages/route.go | 16 + registry/app/api/router/wire.go | 4 +- registry/app/api/wire.go | 13 + registry/app/metadata/nuget/metadata.go | 177 +++++++++++ registry/app/pkg/nuget/helper.go | 61 ++++ registry/app/pkg/nuget/local.go | 209 +++++++++++++ registry/app/pkg/nuget/registry.go | 44 +++ registry/app/pkg/nuget/wire.go | 43 +++ registry/app/pkg/types/nuget/types.go | 60 ++++ 25 files changed, 1417 insertions(+), 129 deletions(-) create mode 100644 registry/app/api/controller/pkg/nuget/controller.go create mode 100644 registry/app/api/controller/pkg/nuget/download_package.go create mode 100644 registry/app/api/controller/pkg/nuget/response.go create mode 100644 registry/app/api/controller/pkg/nuget/service_endpoint.go create mode 100644 registry/app/api/controller/pkg/nuget/upload_package.go create mode 100644 registry/app/api/controller/pkg/nuget/wire.go create mode 100644 registry/app/api/handler/nuget/download_package.go create mode 100644 registry/app/api/handler/nuget/handler.go create mode 100644 registry/app/api/handler/nuget/service_endpoint.go create mode 100644 registry/app/api/handler/nuget/upload_package.go create mode 100644 registry/app/metadata/nuget/metadata.go create mode 100644 registry/app/pkg/nuget/helper.go create mode 100644 registry/app/pkg/nuget/local.go create mode 100644 registry/app/pkg/nuget/registry.go create mode 100644 registry/app/pkg/nuget/wire.go create mode 100644 registry/app/pkg/types/nuget/types.go diff --git a/.golangci.yml b/.golangci.yml index 4a23a6d2d..ea71de76b 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -415,3 +415,5 @@ issues: linters: [ goheader ] - path: "^registry/app/storage/blobStore.go" linters: [ gosec ] + - path: "^registry/app/metadata/nuget/metadata.go" + linters: [ tagliatelle, lll ] diff --git a/cmd/gitness/wire_gen.go b/cmd/gitness/wire_gen.go index d7ab88a20..c7b4578a2 100644 --- a/cmd/gitness/wire_gen.go +++ b/cmd/gitness/wire_gen.go @@ -124,6 +124,7 @@ import ( "github.com/harness/gitness/lock" "github.com/harness/gitness/pubsub" api2 "github.com/harness/gitness/registry/app/api" + nuget2 "github.com/harness/gitness/registry/app/api/controller/pkg/nuget" python2 "github.com/harness/gitness/registry/app/api/controller/pkg/python" "github.com/harness/gitness/registry/app/api/router" events10 "github.com/harness/gitness/registry/app/events" @@ -133,6 +134,7 @@ import ( "github.com/harness/gitness/registry/app/pkg/filemanager" "github.com/harness/gitness/registry/app/pkg/generic" "github.com/harness/gitness/registry/app/pkg/maven" + "github.com/harness/gitness/registry/app/pkg/nuget" "github.com/harness/gitness/registry/app/pkg/python" database2 "github.com/harness/gitness/registry/app/store/database" "github.com/harness/gitness/registry/gc" @@ -523,7 +525,10 @@ func initSystem(ctx context.Context, config *types.Config) (*server.System, erro proxy := python.ProxyProvider(upstreamProxyConfigRepository, registryRepository, imageRepository, artifactRepository, fileManager, transactor, provider, spaceFinder, secretService, localRegistryHelper) pythonController := python2.ControllerProvider(upstreamProxyConfigRepository, registryRepository, imageRepository, artifactRepository, fileManager, transactor, provider, pythonLocalRegistry, proxy) pythonHandler := api2.NewPythonHandlerProvider(pythonController, packagesHandler) - handler4 := router.PackageHandlerProvider(packagesHandler, mavenHandler, genericHandler, pythonHandler) + nugetLocalRegistry := nuget.LocalRegistryProvider(localBase, fileManager, upstreamProxyConfigRepository, transactor, registryRepository, imageRepository, artifactRepository, provider) + nugetController := nuget2.ControllerProvider(upstreamProxyConfigRepository, registryRepository, imageRepository, artifactRepository, fileManager, transactor, provider, nugetLocalRegistry) + nugetHandler := api2.NewNugetHandlerProvider(nugetController, packagesHandler) + handler4 := router.PackageHandlerProvider(packagesHandler, mavenHandler, genericHandler, pythonHandler, nugetHandler) appRouter := router.AppRouterProvider(registryOCIHandler, apiHandler, handler2, handler3, handler4) sender := usage.ProvideMediator(ctx, config, spaceFinder, usageMetricStore) remoteauthService := remoteauth.ProvideRemoteAuth(tokenStore, principalStore) diff --git a/registry/app/api/controller/pkg/nuget/controller.go b/registry/app/api/controller/pkg/nuget/controller.go new file mode 100644 index 000000000..a64fa7ce9 --- /dev/null +++ b/registry/app/api/controller/pkg/nuget/controller.go @@ -0,0 +1,75 @@ +// Copyright 2023 Harness, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package nuget + +import ( + "context" + "io" + + urlprovider "github.com/harness/gitness/app/url" + "github.com/harness/gitness/registry/app/pkg/filemanager" + "github.com/harness/gitness/registry/app/pkg/nuget" + nugettype "github.com/harness/gitness/registry/app/pkg/types/nuget" + "github.com/harness/gitness/registry/app/store" + "github.com/harness/gitness/store/database/dbtx" +) + +type Controller interface { + UploadPackage( + ctx context.Context, + info nugettype.ArtifactInfo, + fileReader io.ReadCloser, + ) *PutArtifactResponse + + DownloadPackage(ctx context.Context, info nugettype.ArtifactInfo) *GetArtifactResponse + + GetServiceEndpoint(ctx context.Context, + info nugettype.ArtifactInfo) *GetServiceEndpointResponse +} + +// Controller handles Python package operations. +type controller struct { + fileManager filemanager.FileManager + proxyStore store.UpstreamProxyConfigRepository + tx dbtx.Transactor + registryDao store.RegistryRepository + imageDao store.ImageRepository + artifactDao store.ArtifactRepository + urlProvider urlprovider.Provider + local nuget.LocalRegistry +} + +// NewController creates a new Python controller. +func NewController( + proxyStore store.UpstreamProxyConfigRepository, + registryDao store.RegistryRepository, + imageDao store.ImageRepository, + artifactDao store.ArtifactRepository, + fileManager filemanager.FileManager, + tx dbtx.Transactor, + urlProvider urlprovider.Provider, + local nuget.LocalRegistry, +) Controller { + return &controller{ + proxyStore: proxyStore, + registryDao: registryDao, + imageDao: imageDao, + artifactDao: artifactDao, + fileManager: fileManager, + tx: tx, + urlProvider: urlProvider, + local: local, + } +} diff --git a/registry/app/api/controller/pkg/nuget/download_package.go b/registry/app/api/controller/pkg/nuget/download_package.go new file mode 100644 index 000000000..88458d25e --- /dev/null +++ b/registry/app/api/controller/pkg/nuget/download_package.go @@ -0,0 +1,76 @@ +// Copyright 2023 Harness, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package nuget + +import ( + "context" + "fmt" + + "github.com/harness/gitness/registry/app/pkg" + "github.com/harness/gitness/registry/app/pkg/base" + "github.com/harness/gitness/registry/app/pkg/nuget" + "github.com/harness/gitness/registry/app/pkg/response" + nugettype "github.com/harness/gitness/registry/app/pkg/types/nuget" + registrytypes "github.com/harness/gitness/registry/types" +) + +func (c *controller) DownloadPackage( + ctx context.Context, + info nugettype.ArtifactInfo, +) *GetArtifactResponse { + f := func(registry registrytypes.Registry, a pkg.Artifact) response.Response { + info.RegIdentifier = registry.Name + info.RegistryID = registry.ID + nugetRegistry, ok := a.(nuget.Registry) + if !ok { + return &GetArtifactResponse{ + BaseResponse{ + fmt.Errorf("invalid registry type: expected nuget.Registry"), + nil, + }, + "", nil, nil, + } + } + headers, fileReader, redirectURL, err := nugetRegistry.DownloadPackage(ctx, info) + return &GetArtifactResponse{ + BaseResponse{ + err, + headers, + }, + redirectURL, fileReader, nil, + } + } + + result, err := base.ProxyWrapper(ctx, c.registryDao, f, info) + if err != nil { + return &GetArtifactResponse{ + BaseResponse{ + err, + nil, + }, + "", nil, nil, + } + } + getResponse, ok := result.(*GetArtifactResponse) + if !ok { + return &GetArtifactResponse{ + BaseResponse{ + fmt.Errorf("invalid registry type: expected nuget.Registry"), + nil, + }, "", nil, nil, + } + } + return getResponse +} diff --git a/registry/app/api/controller/pkg/nuget/response.go b/registry/app/api/controller/pkg/nuget/response.go new file mode 100644 index 000000000..7455d27ee --- /dev/null +++ b/registry/app/api/controller/pkg/nuget/response.go @@ -0,0 +1,52 @@ +// Copyright 2023 Harness, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package nuget + +import ( + "io" + + "github.com/harness/gitness/registry/app/pkg/commons" + "github.com/harness/gitness/registry/app/pkg/response" + "github.com/harness/gitness/registry/app/pkg/types/nuget" + "github.com/harness/gitness/registry/app/storage" +) + +var _ response.Response = (*GetServiceEndpointResponse)(nil) +var _ response.Response = (*GetArtifactResponse)(nil) +var _ response.Response = (*PutArtifactResponse)(nil) + +type BaseResponse struct { + Error error + ResponseHeaders *commons.ResponseHeaders +} + +func (r BaseResponse) GetError() error { + return r.Error +} + +type GetServiceEndpointResponse struct { + BaseResponse + ServiceEndpoint *nuget.ServiceEndpoint +} + +type GetArtifactResponse struct { + BaseResponse + RedirectURL string + Body *storage.FileReader + ReadCloser io.ReadCloser +} +type PutArtifactResponse struct { + BaseResponse +} diff --git a/registry/app/api/controller/pkg/nuget/service_endpoint.go b/registry/app/api/controller/pkg/nuget/service_endpoint.go new file mode 100644 index 000000000..c442b9503 --- /dev/null +++ b/registry/app/api/controller/pkg/nuget/service_endpoint.go @@ -0,0 +1,74 @@ +// Copyright 2023 Harness, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package nuget + +import ( + "context" + "fmt" + + "github.com/harness/gitness/registry/app/pkg" + "github.com/harness/gitness/registry/app/pkg/base" + "github.com/harness/gitness/registry/app/pkg/nuget" + "github.com/harness/gitness/registry/app/pkg/response" + nugettype "github.com/harness/gitness/registry/app/pkg/types/nuget" + registrytypes "github.com/harness/gitness/registry/types" +) + +func (c *controller) GetServiceEndpoint( + ctx context.Context, + info nugettype.ArtifactInfo, +) *GetServiceEndpointResponse { + f := func(registry registrytypes.Registry, a pkg.Artifact) response.Response { + info.RegIdentifier = registry.Name + info.RegistryID = registry.ID + nugetRegistry, ok := a.(nuget.Registry) + if !ok { + return &GetServiceEndpointResponse{ + BaseResponse{ + fmt.Errorf("invalid registry type: expected nuget.Registry"), + nil, + }, nil, + } + } + serviceEndpoint := nugetRegistry.GetServiceEndpoint(ctx, info) + return &GetServiceEndpointResponse{ + BaseResponse{ + nil, + nil, + }, serviceEndpoint, + } + } + + result, err := base.NoProxyWrapper(ctx, c.registryDao, f, info) + + if err != nil { + return &GetServiceEndpointResponse{ + BaseResponse{ + err, + nil, + }, nil, + } + } + serviceEndpointResponse, ok := result.(*GetServiceEndpointResponse) + if !ok { + return &GetServiceEndpointResponse{ + BaseResponse{ + fmt.Errorf("invalid registry type: expected nuget.Registry"), + nil, + }, nil, + } + } + return serviceEndpointResponse +} diff --git a/registry/app/api/controller/pkg/nuget/upload_package.go b/registry/app/api/controller/pkg/nuget/upload_package.go new file mode 100644 index 000000000..784615441 --- /dev/null +++ b/registry/app/api/controller/pkg/nuget/upload_package.go @@ -0,0 +1,75 @@ +// Copyright 2023 Harness, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package nuget + +import ( + "context" + "fmt" + "io" + + "github.com/harness/gitness/registry/app/pkg" + "github.com/harness/gitness/registry/app/pkg/base" + "github.com/harness/gitness/registry/app/pkg/nuget" + "github.com/harness/gitness/registry/app/pkg/response" + nugettype "github.com/harness/gitness/registry/app/pkg/types/nuget" + registrytypes "github.com/harness/gitness/registry/types" +) + +func (c *controller) UploadPackage( + ctx context.Context, + info nugettype.ArtifactInfo, + fileReader io.ReadCloser, +) *PutArtifactResponse { + f := func(registry registrytypes.Registry, a pkg.Artifact) response.Response { + info.RegIdentifier = registry.Name + info.RegistryID = registry.ID + nugetRegistry, ok := a.(nuget.Registry) + if !ok { + return &PutArtifactResponse{ + BaseResponse{ + Error: fmt.Errorf("invalid registry type: expected nuget.Registry"), + ResponseHeaders: nil, + }, + } + } + headers, _, err := nugetRegistry.UploadPackage(ctx, info, fileReader) + return &PutArtifactResponse{ + BaseResponse{ + Error: err, + ResponseHeaders: headers, + }, + } + } + + result, err := base.NoProxyWrapper(ctx, c.registryDao, f, info) + if err != nil { + return &PutArtifactResponse{ + BaseResponse{ + Error: err, + ResponseHeaders: nil, + }, + } + } + uploadResponse, ok := result.(*PutArtifactResponse) + if !ok { + return &PutArtifactResponse{ + BaseResponse{ + Error: err, + ResponseHeaders: nil, + }, + } + } + return uploadResponse +} diff --git a/registry/app/api/controller/pkg/nuget/wire.go b/registry/app/api/controller/pkg/nuget/wire.go new file mode 100644 index 000000000..b4808e8de --- /dev/null +++ b/registry/app/api/controller/pkg/nuget/wire.go @@ -0,0 +1,40 @@ +// Copyright 2023 Harness, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package nuget + +import ( + urlprovider "github.com/harness/gitness/app/url" + "github.com/harness/gitness/registry/app/pkg/filemanager" + "github.com/harness/gitness/registry/app/pkg/nuget" + "github.com/harness/gitness/registry/app/store" + "github.com/harness/gitness/store/database/dbtx" + + "github.com/google/wire" +) + +func ControllerProvider( + proxyStore store.UpstreamProxyConfigRepository, + registryDao store.RegistryRepository, + imageDao store.ImageRepository, + artifactDao store.ArtifactRepository, + fileManager filemanager.FileManager, + tx dbtx.Transactor, + urlProvider urlprovider.Provider, + local nuget.LocalRegistry, +) Controller { + return NewController(proxyStore, registryDao, imageDao, artifactDao, fileManager, tx, urlProvider, local) +} + +var ControllerSet = wire.NewSet(ControllerProvider) diff --git a/registry/app/api/handler/nuget/download_package.go b/registry/app/api/handler/nuget/download_package.go new file mode 100644 index 000000000..aa737078a --- /dev/null +++ b/registry/app/api/handler/nuget/download_package.go @@ -0,0 +1,64 @@ +// Copyright 2023 Harness, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package nuget + +import ( + "fmt" + "net/http" + + "github.com/harness/gitness/registry/app/pkg/commons" + nugettype "github.com/harness/gitness/registry/app/pkg/types/nuget" + "github.com/harness/gitness/registry/request" + + "github.com/rs/zerolog/log" +) + +func (h *handler) DownloadPackage(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + info, ok := request.ArtifactInfoFrom(ctx).(*nugettype.ArtifactInfo) + if !ok { + log.Ctx(ctx).Error().Msg("Failed to get artifact info from context") + h.HandleErrors(r.Context(), []error{fmt.Errorf("failed to fetch info from context")}, w) + return + } + + response := h.controller.DownloadPackage(ctx, *info) + + defer func() { + if response.Body != nil { + err := response.Body.Close() + if err != nil { + log.Ctx(ctx).Error().Msgf("Failed to close body: %v", err) + } + } + if response.ReadCloser != nil { + err := response.ReadCloser.Close() + if err != nil { + log.Ctx(ctx).Error().Msgf("Failed to close readCloser: %v", err) + } + } + }() + + if !commons.IsEmpty(response.GetError()) { + h.HandleError(r.Context(), w, response.GetError()) + } + + if response.RedirectURL != "" { + http.Redirect(w, r, response.RedirectURL, http.StatusTemporaryRedirect) + return + } + h.ServeContent(w, r, response.Body, info.Filename) + response.ResponseHeaders.WriteToResponse(w) +} diff --git a/registry/app/api/handler/nuget/handler.go b/registry/app/api/handler/nuget/handler.go new file mode 100644 index 000000000..2c0d96c7f --- /dev/null +++ b/registry/app/api/handler/nuget/handler.go @@ -0,0 +1,72 @@ +// Copyright 2023 Harness, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package nuget + +import ( + "net/http" + + "github.com/harness/gitness/registry/app/api/controller/pkg/nuget" + "github.com/harness/gitness/registry/app/api/handler/packages" + nugetmetadata "github.com/harness/gitness/registry/app/metadata/nuget" + "github.com/harness/gitness/registry/app/pkg" + nugettype "github.com/harness/gitness/registry/app/pkg/types/nuget" + + "github.com/go-chi/chi/v5" +) + +type Handler interface { + pkg.ArtifactInfoProvider + UploadPackage(writer http.ResponseWriter, request *http.Request) + DownloadPackage(http.ResponseWriter, *http.Request) + GetServiceEndpoint(http.ResponseWriter, *http.Request) +} + +type handler struct { + packages.Handler + controller nuget.Controller +} + +func NewHandler( + controller nuget.Controller, + packageHandler packages.Handler, +) Handler { + return &handler{ + Handler: packageHandler, + controller: controller, + } +} + +var _ Handler = (*handler)(nil) + +func (h *handler) GetPackageArtifactInfo(r *http.Request) (pkg.PackageArtifactInfo, error) { + info, err := h.Handler.GetArtifactInfo(r) + if err != nil { + return nil, err + } + + image := chi.URLParam(r, "id") + filename := chi.URLParam(r, "filename") + version := chi.URLParam(r, "version") + + var md nugetmetadata.Metadata + + info.Image = image + return &nugettype.ArtifactInfo{ + ArtifactInfo: info, + Metadata: md, + Filename: filename, + Version: version, + }, nil +} diff --git a/registry/app/api/handler/nuget/service_endpoint.go b/registry/app/api/handler/nuget/service_endpoint.go new file mode 100644 index 000000000..f838160f0 --- /dev/null +++ b/registry/app/api/handler/nuget/service_endpoint.go @@ -0,0 +1,48 @@ +// Copyright 2023 Harness, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package nuget + +import ( + "encoding/json" + "fmt" + "net/http" + + "github.com/harness/gitness/registry/app/pkg/commons" + nugettype "github.com/harness/gitness/registry/app/pkg/types/nuget" + "github.com/harness/gitness/registry/request" + + "github.com/rs/zerolog/log" +) + +func (h *handler) GetServiceEndpoint(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + info, ok := request.ArtifactInfoFrom(ctx).(*nugettype.ArtifactInfo) + if !ok { + log.Ctx(ctx).Error().Msg("Failed to get artifact info from context") + h.HandleErrors(r.Context(), []error{fmt.Errorf("failed to fetch info from context")}, w) + return + } + response := h.controller.GetServiceEndpoint(r.Context(), *info) + + if !commons.IsEmpty(response.GetError()) { + h.HandleError(r.Context(), w, response.GetError()) + return + } + err := json.NewEncoder(w).Encode(response.ServiceEndpoint) + if err != nil { + h.HandleErrors(r.Context(), []error{err}, w) + return + } +} diff --git a/registry/app/api/handler/nuget/upload_package.go b/registry/app/api/handler/nuget/upload_package.go new file mode 100644 index 000000000..27b090e20 --- /dev/null +++ b/registry/app/api/handler/nuget/upload_package.go @@ -0,0 +1,42 @@ +// Copyright 2023 Harness, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package nuget + +import ( + "fmt" + "net/http" + + "github.com/harness/gitness/registry/app/pkg/commons" + nugettype "github.com/harness/gitness/registry/app/pkg/types/nuget" + "github.com/harness/gitness/registry/request" + + "github.com/rs/zerolog/log" +) + +func (h *handler) UploadPackage(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + info, ok := request.ArtifactInfoFrom(ctx).(*nugettype.ArtifactInfo) + if !ok { + log.Ctx(ctx).Error().Msg("Failed to get artifact info from context") + h.HandleErrors(r.Context(), []error{fmt.Errorf("failed to fetch info from context")}, w) + return + } + response := h.controller.UploadPackage(r.Context(), *info, r.Body) + if !commons.IsEmpty(response.GetError()) { + h.HandleError(ctx, w, response.GetError()) + return + } + response.ResponseHeaders.WriteToResponse(w) +} diff --git a/registry/app/api/handler/packages/handler.go b/registry/app/api/handler/packages/handler.go index c6ec696af..bd8c21f06 100644 --- a/registry/app/api/handler/packages/handler.go +++ b/registry/app/api/handler/packages/handler.go @@ -90,12 +90,14 @@ const ( PathPackageTypeGeneric PathPackageType = "generic" PathPackageTypeMaven PathPackageType = "maven" PathPackageTypePython PathPackageType = "python" + PathPackageTypeNuget PathPackageType = "nuget" ) var packageTypeMap = map[PathPackageType]artifact2.PackageType{ PathPackageTypeGeneric: artifact2.PackageTypeGENERIC, PathPackageTypeMaven: artifact2.PackageTypeMAVEN, PathPackageTypePython: artifact2.PackageTypePYTHON, + PathPackageTypeNuget: artifact2.PackageTypeNUGET, } func (h *handler) GetAuthenticator() authn.Authenticator { diff --git a/registry/app/api/openapi/api.yaml b/registry/app/api/openapi/api.yaml index ed64dd1c0..4334081bf 100644 --- a/registry/app/api/openapi/api.yaml +++ b/registry/app/api/openapi/api.yaml @@ -2207,6 +2207,7 @@ components: - PYTHON - GENERIC - HELM + - NUGET - NPM SectionType: type: string diff --git a/registry/app/api/openapi/contracts/artifact/services.gen.go b/registry/app/api/openapi/contracts/artifact/services.gen.go index c2c972ffe..07e4a3f50 100644 --- a/registry/app/api/openapi/contracts/artifact/services.gen.go +++ b/registry/app/api/openapi/contracts/artifact/services.gen.go @@ -1,6 +1,6 @@ // Package artifact provides primitives to interact with the openapi HTTP API. // -// Code generated by github.com/oapi-codegen/oapi-codegen/v2 version v2.4.1 DO NOT EDIT. +// Code generated by github.com/deepmap/oapi-codegen/v2 version v2.1.0 DO NOT EDIT. package artifact import ( @@ -346,6 +346,7 @@ type MiddlewareFunc func(http.Handler) http.Handler // CreateRegistry operation middleware func (siw *ServerInterfaceWrapper) CreateRegistry(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() var err error @@ -368,11 +369,12 @@ func (siw *ServerInterfaceWrapper) CreateRegistry(w http.ResponseWriter, r *http handler = middleware(handler) } - handler.ServeHTTP(w, r) + handler.ServeHTTP(w, r.WithContext(ctx)) } // DeleteRegistry operation middleware func (siw *ServerInterfaceWrapper) DeleteRegistry(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() var err error @@ -393,11 +395,12 @@ func (siw *ServerInterfaceWrapper) DeleteRegistry(w http.ResponseWriter, r *http handler = middleware(handler) } - handler.ServeHTTP(w, r) + handler.ServeHTTP(w, r.WithContext(ctx)) } // GetRegistry operation middleware func (siw *ServerInterfaceWrapper) GetRegistry(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() var err error @@ -418,11 +421,12 @@ func (siw *ServerInterfaceWrapper) GetRegistry(w http.ResponseWriter, r *http.Re handler = middleware(handler) } - handler.ServeHTTP(w, r) + handler.ServeHTTP(w, r.WithContext(ctx)) } // ModifyRegistry operation middleware func (siw *ServerInterfaceWrapper) ModifyRegistry(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() var err error @@ -443,11 +447,12 @@ func (siw *ServerInterfaceWrapper) ModifyRegistry(w http.ResponseWriter, r *http handler = middleware(handler) } - handler.ServeHTTP(w, r) + handler.ServeHTTP(w, r.WithContext(ctx)) } // ListArtifactLabels operation middleware func (siw *ServerInterfaceWrapper) ListArtifactLabels(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() var err error @@ -495,11 +500,12 @@ func (siw *ServerInterfaceWrapper) ListArtifactLabels(w http.ResponseWriter, r * handler = middleware(handler) } - handler.ServeHTTP(w, r) + handler.ServeHTTP(w, r.WithContext(ctx)) } // GetArtifactStatsForRegistry operation middleware func (siw *ServerInterfaceWrapper) GetArtifactStatsForRegistry(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() var err error @@ -539,11 +545,12 @@ func (siw *ServerInterfaceWrapper) GetArtifactStatsForRegistry(w http.ResponseWr handler = middleware(handler) } - handler.ServeHTTP(w, r) + handler.ServeHTTP(w, r.WithContext(ctx)) } // DeleteArtifact operation middleware func (siw *ServerInterfaceWrapper) DeleteArtifact(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() var err error @@ -573,11 +580,12 @@ func (siw *ServerInterfaceWrapper) DeleteArtifact(w http.ResponseWriter, r *http handler = middleware(handler) } - handler.ServeHTTP(w, r) + handler.ServeHTTP(w, r.WithContext(ctx)) } // UpdateArtifactLabels operation middleware func (siw *ServerInterfaceWrapper) UpdateArtifactLabels(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() var err error @@ -607,11 +615,12 @@ func (siw *ServerInterfaceWrapper) UpdateArtifactLabels(w http.ResponseWriter, r handler = middleware(handler) } - handler.ServeHTTP(w, r) + handler.ServeHTTP(w, r.WithContext(ctx)) } // GetArtifactStats operation middleware func (siw *ServerInterfaceWrapper) GetArtifactStats(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() var err error @@ -660,11 +669,12 @@ func (siw *ServerInterfaceWrapper) GetArtifactStats(w http.ResponseWriter, r *ht handler = middleware(handler) } - handler.ServeHTTP(w, r) + handler.ServeHTTP(w, r.WithContext(ctx)) } // GetArtifactSummary operation middleware func (siw *ServerInterfaceWrapper) GetArtifactSummary(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() var err error @@ -694,11 +704,12 @@ func (siw *ServerInterfaceWrapper) GetArtifactSummary(w http.ResponseWriter, r * handler = middleware(handler) } - handler.ServeHTTP(w, r) + handler.ServeHTTP(w, r.WithContext(ctx)) } // DeleteArtifactVersion operation middleware func (siw *ServerInterfaceWrapper) DeleteArtifactVersion(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() var err error @@ -737,11 +748,12 @@ func (siw *ServerInterfaceWrapper) DeleteArtifactVersion(w http.ResponseWriter, handler = middleware(handler) } - handler.ServeHTTP(w, r) + handler.ServeHTTP(w, r.WithContext(ctx)) } // GetArtifactDetails operation middleware func (siw *ServerInterfaceWrapper) GetArtifactDetails(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() var err error @@ -791,11 +803,12 @@ func (siw *ServerInterfaceWrapper) GetArtifactDetails(w http.ResponseWriter, r * handler = middleware(handler) } - handler.ServeHTTP(w, r) + handler.ServeHTTP(w, r.WithContext(ctx)) } // GetDockerArtifactDetails operation middleware func (siw *ServerInterfaceWrapper) GetDockerArtifactDetails(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() var err error @@ -852,11 +865,12 @@ func (siw *ServerInterfaceWrapper) GetDockerArtifactDetails(w http.ResponseWrite handler = middleware(handler) } - handler.ServeHTTP(w, r) + handler.ServeHTTP(w, r.WithContext(ctx)) } // GetDockerArtifactLayers operation middleware func (siw *ServerInterfaceWrapper) GetDockerArtifactLayers(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() var err error @@ -913,11 +927,12 @@ func (siw *ServerInterfaceWrapper) GetDockerArtifactLayers(w http.ResponseWriter handler = middleware(handler) } - handler.ServeHTTP(w, r) + handler.ServeHTTP(w, r.WithContext(ctx)) } // GetDockerArtifactManifest operation middleware func (siw *ServerInterfaceWrapper) GetDockerArtifactManifest(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() var err error @@ -974,11 +989,12 @@ func (siw *ServerInterfaceWrapper) GetDockerArtifactManifest(w http.ResponseWrit handler = middleware(handler) } - handler.ServeHTTP(w, r) + handler.ServeHTTP(w, r.WithContext(ctx)) } // GetDockerArtifactManifests operation middleware func (siw *ServerInterfaceWrapper) GetDockerArtifactManifests(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() var err error @@ -1017,11 +1033,12 @@ func (siw *ServerInterfaceWrapper) GetDockerArtifactManifests(w http.ResponseWri handler = middleware(handler) } - handler.ServeHTTP(w, r) + handler.ServeHTTP(w, r.WithContext(ctx)) } // GetArtifactFiles operation middleware func (siw *ServerInterfaceWrapper) GetArtifactFiles(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() var err error @@ -1103,11 +1120,12 @@ func (siw *ServerInterfaceWrapper) GetArtifactFiles(w http.ResponseWriter, r *ht handler = middleware(handler) } - handler.ServeHTTP(w, r) + handler.ServeHTTP(w, r.WithContext(ctx)) } // GetHelmArtifactDetails operation middleware func (siw *ServerInterfaceWrapper) GetHelmArtifactDetails(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() var err error @@ -1146,11 +1164,12 @@ func (siw *ServerInterfaceWrapper) GetHelmArtifactDetails(w http.ResponseWriter, handler = middleware(handler) } - handler.ServeHTTP(w, r) + handler.ServeHTTP(w, r.WithContext(ctx)) } // GetHelmArtifactManifest operation middleware func (siw *ServerInterfaceWrapper) GetHelmArtifactManifest(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() var err error @@ -1189,11 +1208,12 @@ func (siw *ServerInterfaceWrapper) GetHelmArtifactManifest(w http.ResponseWriter handler = middleware(handler) } - handler.ServeHTTP(w, r) + handler.ServeHTTP(w, r.WithContext(ctx)) } // GetArtifactVersionSummary operation middleware func (siw *ServerInterfaceWrapper) GetArtifactVersionSummary(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() var err error @@ -1232,11 +1252,12 @@ func (siw *ServerInterfaceWrapper) GetArtifactVersionSummary(w http.ResponseWrit handler = middleware(handler) } - handler.ServeHTTP(w, r) + handler.ServeHTTP(w, r.WithContext(ctx)) } // GetAllArtifactVersions operation middleware func (siw *ServerInterfaceWrapper) GetAllArtifactVersions(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() var err error @@ -1309,11 +1330,12 @@ func (siw *ServerInterfaceWrapper) GetAllArtifactVersions(w http.ResponseWriter, handler = middleware(handler) } - handler.ServeHTTP(w, r) + handler.ServeHTTP(w, r.WithContext(ctx)) } // GetAllArtifactsByRegistry operation middleware func (siw *ServerInterfaceWrapper) GetAllArtifactsByRegistry(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() var err error @@ -1385,11 +1407,12 @@ func (siw *ServerInterfaceWrapper) GetAllArtifactsByRegistry(w http.ResponseWrit handler = middleware(handler) } - handler.ServeHTTP(w, r) + handler.ServeHTTP(w, r.WithContext(ctx)) } // GetClientSetupDetails operation middleware func (siw *ServerInterfaceWrapper) GetClientSetupDetails(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() var err error @@ -1429,11 +1452,12 @@ func (siw *ServerInterfaceWrapper) GetClientSetupDetails(w http.ResponseWriter, handler = middleware(handler) } - handler.ServeHTTP(w, r) + handler.ServeHTTP(w, r.WithContext(ctx)) } // ListWebhooks operation middleware func (siw *ServerInterfaceWrapper) ListWebhooks(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() var err error @@ -1497,11 +1521,12 @@ func (siw *ServerInterfaceWrapper) ListWebhooks(w http.ResponseWriter, r *http.R handler = middleware(handler) } - handler.ServeHTTP(w, r) + handler.ServeHTTP(w, r.WithContext(ctx)) } // CreateWebhook operation middleware func (siw *ServerInterfaceWrapper) CreateWebhook(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() var err error @@ -1522,11 +1547,12 @@ func (siw *ServerInterfaceWrapper) CreateWebhook(w http.ResponseWriter, r *http. handler = middleware(handler) } - handler.ServeHTTP(w, r) + handler.ServeHTTP(w, r.WithContext(ctx)) } // DeleteWebhook operation middleware func (siw *ServerInterfaceWrapper) DeleteWebhook(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() var err error @@ -1556,11 +1582,12 @@ func (siw *ServerInterfaceWrapper) DeleteWebhook(w http.ResponseWriter, r *http. handler = middleware(handler) } - handler.ServeHTTP(w, r) + handler.ServeHTTP(w, r.WithContext(ctx)) } // GetWebhook operation middleware func (siw *ServerInterfaceWrapper) GetWebhook(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() var err error @@ -1590,11 +1617,12 @@ func (siw *ServerInterfaceWrapper) GetWebhook(w http.ResponseWriter, r *http.Req handler = middleware(handler) } - handler.ServeHTTP(w, r) + handler.ServeHTTP(w, r.WithContext(ctx)) } // UpdateWebhook operation middleware func (siw *ServerInterfaceWrapper) UpdateWebhook(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() var err error @@ -1624,11 +1652,12 @@ func (siw *ServerInterfaceWrapper) UpdateWebhook(w http.ResponseWriter, r *http. handler = middleware(handler) } - handler.ServeHTTP(w, r) + handler.ServeHTTP(w, r.WithContext(ctx)) } // ListWebhookExecutions operation middleware func (siw *ServerInterfaceWrapper) ListWebhookExecutions(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() var err error @@ -1677,11 +1706,12 @@ func (siw *ServerInterfaceWrapper) ListWebhookExecutions(w http.ResponseWriter, handler = middleware(handler) } - handler.ServeHTTP(w, r) + handler.ServeHTTP(w, r.WithContext(ctx)) } // GetWebhookExecution operation middleware func (siw *ServerInterfaceWrapper) GetWebhookExecution(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() var err error @@ -1720,11 +1750,12 @@ func (siw *ServerInterfaceWrapper) GetWebhookExecution(w http.ResponseWriter, r handler = middleware(handler) } - handler.ServeHTTP(w, r) + handler.ServeHTTP(w, r.WithContext(ctx)) } // ReTriggerWebhookExecution operation middleware func (siw *ServerInterfaceWrapper) ReTriggerWebhookExecution(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() var err error @@ -1763,11 +1794,12 @@ func (siw *ServerInterfaceWrapper) ReTriggerWebhookExecution(w http.ResponseWrit handler = middleware(handler) } - handler.ServeHTTP(w, r) + handler.ServeHTTP(w, r.WithContext(ctx)) } // GetArtifactStatsForSpace operation middleware func (siw *ServerInterfaceWrapper) GetArtifactStatsForSpace(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() var err error @@ -1807,11 +1839,12 @@ func (siw *ServerInterfaceWrapper) GetArtifactStatsForSpace(w http.ResponseWrite handler = middleware(handler) } - handler.ServeHTTP(w, r) + handler.ServeHTTP(w, r.WithContext(ctx)) } // GetAllArtifacts operation middleware func (siw *ServerInterfaceWrapper) GetAllArtifacts(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() var err error @@ -1899,11 +1932,12 @@ func (siw *ServerInterfaceWrapper) GetAllArtifacts(w http.ResponseWriter, r *htt handler = middleware(handler) } - handler.ServeHTTP(w, r) + handler.ServeHTTP(w, r.WithContext(ctx)) } // GetAllRegistries operation middleware func (siw *ServerInterfaceWrapper) GetAllRegistries(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() var err error @@ -1991,7 +2025,7 @@ func (siw *ServerInterfaceWrapper) GetAllRegistries(w http.ResponseWriter, r *ht handler = middleware(handler) } - handler.ServeHTTP(w, r) + handler.ServeHTTP(w, r.WithContext(ctx)) } type UnescapedCookieParamError struct { @@ -5734,97 +5768,97 @@ func (sh *strictHandler) GetAllRegistries(w http.ResponseWriter, r *http.Request // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "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", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/registry/app/api/openapi/contracts/artifact/types.gen.go b/registry/app/api/openapi/contracts/artifact/types.gen.go index b124bdae5..4fa152d13 100644 --- a/registry/app/api/openapi/contracts/artifact/types.gen.go +++ b/registry/app/api/openapi/contracts/artifact/types.gen.go @@ -1,6 +1,6 @@ // Package artifact provides primitives to interact with the openapi HTTP API. // -// Code generated by github.com/oapi-codegen/oapi-codegen/v2 version v2.4.1 DO NOT EDIT. +// Code generated by github.com/deepmap/oapi-codegen/v2 version v2.1.0 DO NOT EDIT. package artifact import ( @@ -31,6 +31,7 @@ const ( PackageTypeHELM PackageType = "HELM" PackageTypeMAVEN PackageType = "MAVEN" PackageTypeNPM PackageType = "NPM" + PackageTypeNUGET PackageType = "NUGET" PackageTypePYTHON PackageType = "PYTHON" ) diff --git a/registry/app/api/router/packages/route.go b/registry/app/api/router/packages/route.go index e2d46c5e9..687a21e81 100644 --- a/registry/app/api/router/packages/route.go +++ b/registry/app/api/router/packages/route.go @@ -21,6 +21,7 @@ import ( middlewareauthn "github.com/harness/gitness/app/api/middleware/authn" "github.com/harness/gitness/registry/app/api/handler/generic" "github.com/harness/gitness/registry/app/api/handler/maven" + "github.com/harness/gitness/registry/app/api/handler/nuget" "github.com/harness/gitness/registry/app/api/handler/packages" "github.com/harness/gitness/registry/app/api/handler/python" "github.com/harness/gitness/registry/app/api/middleware" @@ -44,6 +45,7 @@ func NewRouter( mavenHandler *maven.Handler, genericHandler *generic.Handler, pythonHandler python.Handler, + nugetHandler nuget.Handler, ) Handler { r := chi.NewRouter() @@ -98,6 +100,20 @@ func NewRouter( http.Error(w, fmt.Sprintf("Package type '%s' is not supported", packageType), http.StatusNotFound) }) }) + + r.Route("/nuget", func(r chi.Router) { + r.Use(middlewareauthn.Attempt(packageHandler.GetAuthenticator())) + + r.With(middleware.StoreArtifactInfo(nugetHandler)). + With(middleware.RequestPackageAccess(packageHandler, enum.PermissionArtifactsUpload)). + Put("/", nugetHandler.UploadPackage) + r.With(middleware.StoreArtifactInfo(nugetHandler)). + With(middleware.RequestPackageAccess(packageHandler, enum.PermissionArtifactsDownload)). + Get("/package/{id}/{version}/{filename}", nugetHandler.DownloadPackage) + r.With(middleware.StoreArtifactInfo(nugetHandler)). + With(middleware.RequestPackageAccess(packageHandler, enum.PermissionArtifactsDownload)). + Get("/index.json", nugetHandler.GetServiceEndpoint) + }) }) return r diff --git a/registry/app/api/router/wire.go b/registry/app/api/router/wire.go index 359aba5ac..9dfa478af 100644 --- a/registry/app/api/router/wire.go +++ b/registry/app/api/router/wire.go @@ -24,6 +24,7 @@ import ( "github.com/harness/gitness/audit" "github.com/harness/gitness/registry/app/api/handler/generic" "github.com/harness/gitness/registry/app/api/handler/maven" + "github.com/harness/gitness/registry/app/api/handler/nuget" hoci "github.com/harness/gitness/registry/app/api/handler/oci" "github.com/harness/gitness/registry/app/api/handler/packages" "github.com/harness/gitness/registry/app/api/handler/python" @@ -116,8 +117,9 @@ func PackageHandlerProvider( mavenHandler *maven.Handler, genericHandler *generic.Handler, pypiHandler python.Handler, + nugetHandler nuget.Handler, ) packagerrouter.Handler { - return packagerrouter.NewRouter(handler, mavenHandler, genericHandler, pypiHandler) + return packagerrouter.NewRouter(handler, mavenHandler, genericHandler, pypiHandler, nugetHandler) } var WireSet = wire.NewSet(APIHandlerProvider, OCIHandlerProvider, AppRouterProvider, diff --git a/registry/app/api/wire.go b/registry/app/api/wire.go index 21abacd1f..a7d9f9e07 100644 --- a/registry/app/api/wire.go +++ b/registry/app/api/wire.go @@ -21,9 +21,11 @@ import ( "github.com/harness/gitness/app/services/refcache" corestore "github.com/harness/gitness/app/store" urlprovider "github.com/harness/gitness/app/url" + nuget2 "github.com/harness/gitness/registry/app/api/controller/pkg/nuget" python2 "github.com/harness/gitness/registry/app/api/controller/pkg/python" "github.com/harness/gitness/registry/app/api/handler/generic" mavenhandler "github.com/harness/gitness/registry/app/api/handler/maven" + nugethandler "github.com/harness/gitness/registry/app/api/handler/nuget" ocihandler "github.com/harness/gitness/registry/app/api/handler/oci" "github.com/harness/gitness/registry/app/api/handler/packages" pypi2 "github.com/harness/gitness/registry/app/api/handler/python" @@ -38,6 +40,7 @@ import ( "github.com/harness/gitness/registry/app/pkg/filemanager" generic2 "github.com/harness/gitness/registry/app/pkg/generic" "github.com/harness/gitness/registry/app/pkg/maven" + "github.com/harness/gitness/registry/app/pkg/nuget" "github.com/harness/gitness/registry/app/pkg/python" "github.com/harness/gitness/registry/app/store" "github.com/harness/gitness/registry/app/store/database" @@ -133,6 +136,13 @@ func NewPythonHandlerProvider( return pypi2.NewHandler(controller, packageHandler) } +func NewNugetHandlerProvider( + controller nuget2.Controller, + packageHandler packages.Handler, +) nugethandler.Handler { + return nugethandler.NewHandler(controller, packageHandler) +} + func NewGenericHandlerProvider( spaceStore corestore.SpaceStore, controller *generic2.Controller, tokenStore corestore.TokenStore, userCtrl *usercontroller.Controller, authenticator authn.Authenticator, urlProvider urlprovider.Provider, @@ -156,16 +166,19 @@ var WireSet = wire.NewSet( NewGenericHandlerProvider, NewPackageHandlerProvider, NewPythonHandlerProvider, + NewNugetHandlerProvider, database.WireSet, pkg.WireSet, docker.WireSet, filemanager.WireSet, maven.WireSet, + nuget.WireSet, python.WireSet, router.WireSet, gc.WireSet, generic2.WireSet, python2.ControllerSet, + nuget2.ControllerSet, base.WireSet, ) diff --git a/registry/app/metadata/nuget/metadata.go b/registry/app/metadata/nuget/metadata.go new file mode 100644 index 000000000..954ad971b --- /dev/null +++ b/registry/app/metadata/nuget/metadata.go @@ -0,0 +1,177 @@ +// Copyright 2023 Harness, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package nuget + +import ( + "github.com/harness/gitness/registry/app/metadata" +) + +var _ metadata.Metadata = (*NugetMetadata)(nil) + +//nolint:revive +type NugetMetadata struct { + Metadata + Files []metadata.File `json:"files"` + FileCount int64 `json:"file_count"` +} + +func (p *NugetMetadata) GetFiles() []metadata.File { + return p.Files +} + +func (p *NugetMetadata) SetFiles(files []metadata.File) { + p.Files = files + p.FileCount = int64(len(files)) +} + +// Package represents the entire NuGet package. +type Metadata struct { + PackageMetadata PackageMetadata `json:"metadata" xml:"metadata"` + Files *FilesWrapper `json:"files,omitempty" xml:"files,omitempty"` +} + +// PackageMetadata represents the metadata of a NuGet package. +type PackageMetadata struct { + ID string `json:"id" xml:"id"` + Version string `json:"version" xml:"version"` + Title string `json:"title,omitempty" xml:"title,omitempty"` + Authors string `json:"authors" xml:"authors"` + Owners string `json:"owners,omitempty" xml:"owners,omitempty"` + LicenseURL string `json:"licenseUrl,omitempty" xml:"licenseUrl,omitempty"` + ProjectURL string `json:"projectUrl,omitempty" xml:"projectUrl,omitempty"` + IconURL string `json:"iconUrl,omitempty" xml:"iconUrl,omitempty"` + RequireLicenseAcceptance bool `json:"requireLicenseAcceptance,omitempty" xml:"requireLicenseAcceptance,omitempty"` + DevelopmentDependency bool `json:"developmentDependency,omitempty" xml:"developmentDependency,omitempty"` + Description string `json:"description" xml:"description"` + Summary string `json:"summary,omitempty" xml:"summary,omitempty"` + ReleaseNotes string `json:"releaseNotes,omitempty" xml:"releaseNotes,omitempty"` + Copyright string `json:"copyright,omitempty" xml:"copyright,omitempty"` + Language string `json:"language,omitempty" xml:"language,omitempty"` + Tags string `json:"tags,omitempty" xml:"tags,omitempty"` + Serviceable bool `json:"serviceable,omitempty" xml:"serviceable,omitempty"` + Icon string `json:"icon,omitempty" xml:"icon,omitempty"` + Readme string `json:"readme,omitempty" xml:"readme,omitempty"` + Repository *Repository `json:"repository,omitempty" xml:"repository,omitempty"` + License *License `json:"license,omitempty" xml:"license,omitempty"` + PackageTypes []PackageType `json:"packageTypes,omitempty" xml:"packageType"` + Dependencies *DependenciesWrapper `json:"dependencies,omitempty" xml:"dependencies,omitempty"` + FrameworkAssemblies []FrameworkAssembly `json:"frameworkAssemblies,omitempty" xml:"frameworkAssembly"` + FrameworkReferences *FrameworkReferencesWrapper `json:"frameworkReferences,omitempty" xml:"frameworkReferences,omitempty"` + References *ReferencesWrapper `json:"references,omitempty" xml:"references,omitempty"` + ContentFiles []ContentFileEntry `json:"contentFiles,omitempty" xml:"files"` + MinClientVersion string `json:"minClientVersion,omitempty" xml:"minClientVersion,attr"` +} + +// Dependency represents a package dependency. +type Dependency struct { + ID string `json:"id,omitempty" xml:"id,attr"` + Version string `json:"version,omitempty" xml:"version,attr"` + Include string `json:"include,omitempty" xml:"include,attr"` + Exclude string `json:"exclude,omitempty" xml:"exclude,attr"` +} + +// DependencyGroup represents a group of dependencies. +type DependencyGroup struct { + TargetFramework string `json:"targetFramework,omitempty" xml:"targetFramework,attr"` + Dependencies []Dependency `json:"dependencies,omitempty" xml:"dependency"` +} + +// Reference represents a package reference. +type Reference struct { + File string `json:"file" xml:"file,attr"` +} + +// ReferenceGroup represents a group of references. +type ReferenceGroup struct { + TargetFramework string `json:"targetFramework,omitempty" xml:"targetFramework,attr"` + References []Reference `json:"references,omitempty" xml:"reference"` +} + +// FrameworkReference represents a single framework reference. +type FrameworkReference struct { + Name string `json:"name" xml:"name,attr"` +} + +// FrameworkReferenceGroup represents a group of framework references. +type FrameworkReferenceGroup struct { + TargetFramework string `json:"targetFramework" xml:"targetFramework,attr"` + FrameworkReferences []FrameworkReference `json:"frameworkReferences,omitempty" xml:"frameworkReference"` +} + +// ContentFileEntry represents a content file entry. +type ContentFileEntry struct { + Include string `json:"include" xml:"include,attr"` + Exclude string `json:"exclude,omitempty" xml:"exclude,attr"` + BuildAction string `json:"buildAction,omitempty" xml:"buildAction,attr"` + CopyToOutput bool `json:"copyToOutput,omitempty" xml:"copyToOutput,attr"` + Flatten bool `json:"flatten,omitempty" xml:"flatten,attr"` +} + +// PackageType represents a package type. +type PackageType struct { + Name string `json:"name" xml:"name,attr"` + Version string `json:"version,omitempty" xml:"version,attr"` +} + +// Repository represents the repository information. +type Repository struct { + Type string `json:"type,omitempty" xml:"type,attr"` + URL string `json:"url,omitempty" xml:"url,attr"` + Branch string `json:"branch,omitempty" xml:"branch,attr"` + Commit string `json:"commit,omitempty" xml:"commit,attr"` +} + +// License represents a package license. +type License struct { + Type string `json:"type" xml:"type,attr"` + Version string `json:"version,omitempty" xml:"version,attr"` + //nolint:staticcheck + Text string `json:",chardata" xml:",chardata"` +} + +// FrameworkAssembly represents a framework assembly reference. +type FrameworkAssembly struct { + AssemblyName string `json:"assemblyName" xml:"assemblyName,attr"` + TargetFramework string `json:"targetFramework,omitempty" xml:"targetFramework,attr"` +} + +// DependenciesWrapper represents the `` section. +type DependenciesWrapper struct { + Dependencies []Dependency `json:"dependencies,omitempty" xml:"dependency"` + Groups []DependencyGroup `json:"groups,omitempty" xml:"group"` +} + +// ReferencesWrapper represents the `` section. +type ReferencesWrapper struct { + References []Reference `json:"references,omitempty" xml:"reference"` + Groups []ReferenceGroup `json:"groups,omitempty" xml:"group"` +} + +// FrameworkReferencesWrapper represents the `` section. +type FrameworkReferencesWrapper struct { + Groups []FrameworkReferenceGroup `json:"groups,omitempty" xml:"group"` +} + +// File represents an individual file in the `` section. +type File struct { + Src string `json:"src" xml:"src,attr"` + Target string `json:"target,omitempty" xml:"target,attr"` + Exclude string `json:"exclude,omitempty" xml:"exclude,attr"` +} + +// FilesWrapper represents the `` section. +type FilesWrapper struct { + Files []File `json:"files,omitempty" xml:"file"` +} diff --git a/registry/app/pkg/nuget/helper.go b/registry/app/pkg/nuget/helper.go new file mode 100644 index 000000000..23951e754 --- /dev/null +++ b/registry/app/pkg/nuget/helper.go @@ -0,0 +1,61 @@ +// Copyright 2023 Harness, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package nuget + +import "github.com/harness/gitness/registry/app/pkg/types/nuget" + +func buildServiceEndpoint(baseURL string) *nuget.ServiceEndpoint { + return &nuget.ServiceEndpoint{ + Version: "3.0.0", + Resources: []nuget.Resource{ + { + ID: baseURL + "/query", + Type: "SearchQueryService", + }, + { + ID: baseURL + "/registration", + Type: "RegistrationsBaseUrl", + }, + { + ID: baseURL + "/package", + Type: "PackageBaseAddress/3.0.0", + }, + { + ID: baseURL, + Type: "PackagePublish/2.0.0", + }, + { + ID: baseURL + "/symbolpackage", + Type: "SymbolPackagePublish/4.9.0", + }, + { + ID: baseURL + "/query", + Type: "SearchQueryService/3.0.0-rc", + }, + { + ID: baseURL + "/registration", + Type: "RegistrationsBaseUrl/3.0.0-rc", + }, + { + ID: baseURL + "/query", + Type: "SearchQueryService/3.0.0-beta", + }, + { + ID: baseURL + "/registration", + Type: "RegistrationsBaseUrl/3.0.0-beta", + }, + }, + } +} diff --git a/registry/app/pkg/nuget/local.go b/registry/app/pkg/nuget/local.go new file mode 100644 index 000000000..0576c4331 --- /dev/null +++ b/registry/app/pkg/nuget/local.go @@ -0,0 +1,209 @@ +// Copyright 2023 Harness, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package nuget + +import ( + "archive/zip" + "context" + "encoding/xml" + "fmt" + "io" + "net/http" + "os" + "path/filepath" + "regexp" + "strings" + + urlprovider "github.com/harness/gitness/app/url" + "github.com/harness/gitness/registry/app/api/openapi/contracts/artifact" + nugetmetadata "github.com/harness/gitness/registry/app/metadata/nuget" + "github.com/harness/gitness/registry/app/pkg" + "github.com/harness/gitness/registry/app/pkg/base" + "github.com/harness/gitness/registry/app/pkg/commons" + "github.com/harness/gitness/registry/app/pkg/filemanager" + nugettype "github.com/harness/gitness/registry/app/pkg/types/nuget" + "github.com/harness/gitness/registry/app/storage" + "github.com/harness/gitness/registry/app/store" + "github.com/harness/gitness/registry/types" + "github.com/harness/gitness/store/database/dbtx" + + "github.com/google/uuid" +) + +var _ pkg.Artifact = (*localRegistry)(nil) +var _ Registry = (*localRegistry)(nil) + +var IDMatch = regexp.MustCompile(`\A\w+(?:[.-]\w+)*\z`) + +type PackageType int + +const ( + // DependencyPackage represents a package (*.nupkg). + DependencyPackage PackageType = iota + 1 + // SymbolsPackage represents a symbol package (*.snupkg). + SymbolsPackage +) + +type localRegistry struct { + localBase base.LocalBase + fileManager filemanager.FileManager + proxyStore store.UpstreamProxyConfigRepository + tx dbtx.Transactor + registryDao store.RegistryRepository + imageDao store.ImageRepository + artifactDao store.ArtifactRepository + urlProvider urlprovider.Provider +} + +func (c *localRegistry) GetServiceEndpoint(ctx context.Context, + info nugettype.ArtifactInfo) *nugettype.ServiceEndpoint { + baseURL := c.urlProvider.RegistryURL(ctx, "pkg", info.RootIdentifier, info.RegIdentifier, "nuget") + serviceEndpoints := buildServiceEndpoint(baseURL) + return serviceEndpoints +} + +func (c *localRegistry) UploadPackage(ctx context.Context, + info nugettype.ArtifactInfo, + fileReader io.ReadCloser, +) (headers *commons.ResponseHeaders, sha256 string, err error) { + metadata, err := c.buildMetadata(info, fileReader) + if err != nil { + return headers, "", err + } + fileName := strings.ToLower(fmt.Sprintf("%s.%s.nupkg", + metadata.PackageMetadata.ID, metadata.PackageMetadata.Version)) + info.Metadata = metadata + info.Filename = fileName + info.Version = metadata.PackageMetadata.Version + info.Image = metadata.PackageMetadata.ID + path := info.Image + "/" + info.Version + "/" + fileName + + return c.localBase.Upload(ctx, info.ArtifactInfo, fileName, info.Version, path, fileReader, + &nugetmetadata.NugetMetadata{ + Metadata: info.Metadata, + }) +} + +func (c *localRegistry) buildMetadata(info nugettype.ArtifactInfo, + fileReader io.Reader) (metadata nugetmetadata.Metadata, err error) { + pathUUID := uuid.NewString() + tmpFile, err2 := os.CreateTemp(os.TempDir(), info.RootIdentifier+"-"+pathUUID+"*") + if err2 != nil { + return metadata, err2 + } + defer os.Remove(tmpFile.Name()) // Cleanup + + _, err2 = io.Copy(tmpFile, fileReader) + if err2 != nil { + return metadata, err2 + } + _, err = tmpFile.Seek(0, 0) + if err != nil { + return nugetmetadata.Metadata{}, err + } + + stat, err2 := tmpFile.Stat() + if err2 != nil { + return metadata, err2 + } + + archive, err2 := zip.NewReader(tmpFile, stat.Size()) + if err2 != nil { + return metadata, err2 + } + + for _, file := range archive.File { + if filepath.Dir(file.Name) != "." { + continue + } + if strings.HasSuffix(strings.ToLower(file.Name), ".nuspec") { + f, err3 := archive.Open(file.Name) + if err3 != nil { + return metadata, err3 + } + defer f.Close() + + metadata, err = c.parseMetadata(f) + return metadata, err + } + } + return metadata, nil +} + +func (c *localRegistry) parseMetadata(f io.Reader) (metadata nugetmetadata.Metadata, err error) { + var p nugetmetadata.Metadata + if err = xml.NewDecoder(f).Decode(&p); err != nil { + return metadata, err + } + + if !IDMatch.MatchString(p.PackageMetadata.ID) { + return metadata, fmt.Errorf("invalid package id: %s", p.PackageMetadata.ID) + } + return p, nil +} + +func (c *localRegistry) DownloadPackage(ctx context.Context, + info nugettype.ArtifactInfo) (*commons.ResponseHeaders, *storage.FileReader, string, error) { + responseHeaders := &commons.ResponseHeaders{ + Headers: make(map[string]string), + Code: 0, + } + + path := "/" + info.Image + "/" + info.Version + "/" + info.Filename + + fileReader, _, redirectURL, err := c.fileManager.DownloadFile(ctx, path, types.Registry{ + ID: info.RegistryID, + Name: info.RegIdentifier, + }, info.RootIdentifier) + if err != nil { + return responseHeaders, nil, "", err + } + responseHeaders.Code = http.StatusOK + return responseHeaders, fileReader, redirectURL, nil +} + +type LocalRegistry interface { + Registry +} + +func NewLocalRegistry( + localBase base.LocalBase, + fileManager filemanager.FileManager, + proxyStore store.UpstreamProxyConfigRepository, + tx dbtx.Transactor, + registryDao store.RegistryRepository, + imageDao store.ImageRepository, + artifactDao store.ArtifactRepository, + urlProvider urlprovider.Provider, +) LocalRegistry { + return &localRegistry{ + localBase: localBase, + fileManager: fileManager, + proxyStore: proxyStore, + tx: tx, + registryDao: registryDao, + imageDao: imageDao, + artifactDao: artifactDao, + urlProvider: urlProvider, + } +} + +func (c *localRegistry) GetArtifactType() artifact.RegistryType { + return artifact.RegistryTypeVIRTUAL +} + +func (c *localRegistry) GetPackageTypes() []artifact.PackageType { + return []artifact.PackageType{artifact.PackageTypeNUGET} +} diff --git a/registry/app/pkg/nuget/registry.go b/registry/app/pkg/nuget/registry.go new file mode 100644 index 000000000..a7ad7789e --- /dev/null +++ b/registry/app/pkg/nuget/registry.go @@ -0,0 +1,44 @@ +// Copyright 2023 Harness, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package nuget + +import ( + "context" + "io" + + "github.com/harness/gitness/registry/app/pkg" + "github.com/harness/gitness/registry/app/pkg/commons" + "github.com/harness/gitness/registry/app/pkg/types/nuget" + "github.com/harness/gitness/registry/app/storage" +) + +type Registry interface { + pkg.Artifact + + UploadPackage( + ctx context.Context, + info nuget.ArtifactInfo, + fileReader io.ReadCloser, + ) (*commons.ResponseHeaders, string, error) + + DownloadPackage(ctx context.Context, info nuget.ArtifactInfo) ( + *commons.ResponseHeaders, + *storage.FileReader, + string, + error, + ) + + GetServiceEndpoint(ctx context.Context, info nuget.ArtifactInfo) *nuget.ServiceEndpoint +} diff --git a/registry/app/pkg/nuget/wire.go b/registry/app/pkg/nuget/wire.go new file mode 100644 index 000000000..d412bc7de --- /dev/null +++ b/registry/app/pkg/nuget/wire.go @@ -0,0 +1,43 @@ +// Copyright 2023 Harness, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package nuget + +import ( + urlprovider "github.com/harness/gitness/app/url" + "github.com/harness/gitness/registry/app/pkg/base" + "github.com/harness/gitness/registry/app/pkg/filemanager" + "github.com/harness/gitness/registry/app/store" + "github.com/harness/gitness/store/database/dbtx" + + "github.com/google/wire" +) + +func LocalRegistryProvider( + localBase base.LocalBase, + fileManager filemanager.FileManager, + proxyStore store.UpstreamProxyConfigRepository, + tx dbtx.Transactor, + registryDao store.RegistryRepository, + imageDao store.ImageRepository, + artifactDao store.ArtifactRepository, + urlProvider urlprovider.Provider, +) LocalRegistry { + registry := NewLocalRegistry(localBase, fileManager, proxyStore, tx, registryDao, imageDao, artifactDao, + urlProvider) + base.Register(registry) + return registry +} + +var WireSet = wire.NewSet(LocalRegistryProvider) diff --git a/registry/app/pkg/types/nuget/types.go b/registry/app/pkg/types/nuget/types.go new file mode 100644 index 000000000..54bfbe373 --- /dev/null +++ b/registry/app/pkg/types/nuget/types.go @@ -0,0 +1,60 @@ +// Copyright 2023 Harness, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package nuget + +import ( + "github.com/harness/gitness/registry/app/metadata/nuget" + "github.com/harness/gitness/registry/app/pkg" +) + +type ArtifactInfo struct { + pkg.ArtifactInfo + Version string + Filename string + Metadata nuget.Metadata +} + +// BaseArtifactInfo implements pkg.PackageArtifactInfo interface. +func (a ArtifactInfo) BaseArtifactInfo() pkg.ArtifactInfo { + return a.ArtifactInfo +} + +func (a ArtifactInfo) GetImageVersion() (exists bool, imageVersion string) { + if a.Image != "" && a.Version != "" { + return true, pkg.JoinWithSeparator(":", a.Image, a.Version) + } + return false, "" +} + +type File struct { + FileURL string + Name string +} + +type PackageMetadata struct { + Name string + Files []File +} + +type ServiceEndpoint struct { + Version string `json:"version"` + Resources []Resource `json:"resources"` +} + +type Resource struct { + //nolint:revive + ID string `json:"@id"` + Type string `json:"@type"` +}