Skip to content

Commit 6767834

Browse files
authored
chore: add get local resource response (#208)
Signed-off-by: Gergely Brautigam <182850+Skarlso@users.noreply.github.com> On-behalf-of: @SAP gergely.brautigam@sap.com
1 parent 35270a7 commit 6767834

11 files changed

Lines changed: 107 additions & 70 deletions

File tree

bindings/go/plugin/internal/testplugin/main.go

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,9 +70,24 @@ func (m *TestPlugin) ListComponentVersions(ctx context.Context, request repov1.L
7070
return []string{"v0.0.1", "v0.0.2"}, nil
7171
}
7272

73-
func (m *TestPlugin) GetLocalResource(ctx context.Context, request repov1.GetLocalResourceRequest[*dummyv1.Repository], credentials map[string]string) error {
74-
_, _ = fmt.Fprintf(os.Stdout, "Writing my local resource here to target: %+v\n", request.TargetLocation)
75-
return nil
73+
func (m *TestPlugin) GetLocalResource(ctx context.Context, request repov1.GetLocalResourceRequest[*dummyv1.Repository], credentials map[string]string) (repov1.GetLocalResourceResponse, error) {
74+
// the plugin decides where things will live.
75+
f, err := os.CreateTemp("", "test-resource-file")
76+
if err != nil {
77+
return repov1.GetLocalResourceResponse{}, fmt.Errorf("error creating temp file: %w", err)
78+
}
79+
80+
if err := os.WriteFile(f.Name(), []byte("test-resource"), 0o600); err != nil {
81+
return repov1.GetLocalResourceResponse{}, fmt.Errorf("error write to temp file: %w", err)
82+
}
83+
84+
_, _ = fmt.Fprintf(os.Stdout, "Writing my local resource here to target: %+v\n", f.Name())
85+
return repov1.GetLocalResourceResponse{
86+
Location: types.Location{
87+
Value: f.Name(),
88+
LocationType: types.LocationTypeLocalFile,
89+
},
90+
}, nil
7691
}
7792

7893
func (m *TestPlugin) AddLocalResource(ctx context.Context, request repov1.PostLocalResourceRequest[*dummyv1.Repository], credentials map[string]string) (*descriptor.Resource, error) {

bindings/go/plugin/manager/contracts/ocmrepository/v1/contracts.go

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,19 @@ import (
88
"ocm.software/open-component-model/bindings/go/runtime"
99
)
1010

11+
type IdentityProvider[T runtime.Typed] interface {
12+
contracts.PluginBase
13+
GetIdentity(ctx context.Context, typ GetIdentityRequest[T]) (runtime.Identity, error)
14+
}
15+
1116
// ReadOCMRepositoryPluginContract is a plugin type that can deal with repositories
1217
// These provide type safety for all implementations. The Type defines the repository on which these requests work on.
1318
type ReadOCMRepositoryPluginContract[T runtime.Typed] interface {
1419
contracts.PluginBase
1520
IdentityProvider[T]
1621
GetComponentVersion(ctx context.Context, request GetComponentVersionRequest[T], credentials map[string]string) (*descriptor.Descriptor, error)
1722
ListComponentVersions(ctx context.Context, request ListComponentVersionsRequest[T], credentials map[string]string) ([]string, error)
18-
GetLocalResource(ctx context.Context, request GetLocalResourceRequest[T], credentials map[string]string) error
23+
GetLocalResource(ctx context.Context, request GetLocalResourceRequest[T], credentials map[string]string) (GetLocalResourceResponse, error)
1924
}
2025

2126
// WriteOCMRepositoryPluginContract defines the ability to upload ComponentVersions to a repository with a given Type.
@@ -32,24 +37,20 @@ type ReadWriteOCMRepositoryPluginContract[T runtime.Typed] interface {
3237
WriteOCMRepositoryPluginContract[T]
3338
}
3439

35-
// ResourcePluginContract is the contract defining Add and Get global resources.
36-
type ResourcePluginContract interface {
37-
contracts.PluginBase
38-
AddGlobalResource(ctx context.Context, request PostResourceRequest, credentials map[string]string) (*descriptor.Resource, error)
39-
GetGlobalResource(ctx context.Context, request GetResourceRequest, credentials map[string]string) error
40-
}
41-
4240
type ReadResourcePluginContract interface {
4341
contracts.PluginBase
44-
GetGlobalResource(ctx context.Context, request GetResourceRequest, credentials map[string]string) error
42+
IdentityProvider[runtime.Typed]
43+
GetGlobalResource(ctx context.Context, request GetResourceRequest, credentials map[string]string) (GetResourceResponse, error)
4544
}
4645

4746
type WriteResourcePluginContract interface {
4847
contracts.PluginBase
48+
IdentityProvider[runtime.Typed]
4949
AddGlobalResource(ctx context.Context, request PostResourceRequest, credentials map[string]string) (*descriptor.Resource, error)
5050
}
5151

52-
type IdentityProvider[T runtime.Typed] interface {
53-
contracts.PluginBase
54-
GetIdentity(ctx context.Context, typ GetIdentityRequest[T]) (runtime.Identity, error)
52+
// ReadWriteResourcePluginContract is the contract defining Add and Get global resources.
53+
type ReadWriteResourcePluginContract interface {
54+
ReadResourcePluginContract
55+
WriteResourcePluginContract
5556
}

bindings/go/plugin/manager/contracts/ocmrepository/v1/types.go

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,11 @@ type GetLocalResourceRequest[T runtime.Typed] struct {
3737

3838
// Identity of the local resource
3939
Identity map[string]string `json:"identity,omitempty"`
40+
}
4041

41-
// The Location of the Local Resource to download to
42-
TargetLocation types.Location `json:"targetLocation"`
42+
type GetLocalResourceResponse struct {
43+
// Location where the local resource will be downloaded to and can be accessed.
44+
Location types.Location `json:"location"`
4345
}
4446

4547
type PostLocalResourceRequest[T runtime.Typed] struct {
@@ -59,9 +61,11 @@ type GetResourceRequest struct {
5961
types.Location
6062
// The resource specification to download
6163
*v2.Resource `json:"resource"`
64+
}
6265

63-
// The Location of the Local Resource to download to
64-
TargetLocation types.Location `json:"targetLocation"`
66+
type GetResourceResponse struct {
67+
// Location where the resource will be downloaded to and can be accessed.
68+
Location types.Location `json:"location"`
6569
}
6670

6771
type PostResourceRequest struct {

bindings/go/plugin/manager/manager_test.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,20 @@ func TestPluginManager(t *testing.T) {
6666
}, map[string]string{})
6767
require.NoError(t, err)
6868
require.Equal(t, "test-component:1.0.0", desc.String())
69+
70+
response, err := plugin.GetLocalResource(ctx, repov1.GetLocalResourceRequest[runtime.Typed]{
71+
Repository: &dummyv1.Repository{
72+
Type: typ,
73+
BaseUrl: "https://ocm.software/test",
74+
},
75+
Name: "test-resource",
76+
Version: "v0.0.1",
77+
}, map[string]string{})
78+
require.NoError(t, err)
79+
require.Equal(t, types.LocationTypeLocalFile, response.Location.LocationType)
80+
content, err := os.ReadFile(response.Location.Value)
81+
require.NoError(t, err)
82+
require.Equal(t, "test-resource", string(content))
6983
}
7084

7185
func TestConfigurationPassedToPlugin(t *testing.T) {

bindings/go/plugin/manager/registries/componentversionrepository/endpoints_function_test.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"testing"
77

88
"github.com/stretchr/testify/require"
9+
"ocm.software/open-component-model/bindings/go/plugin/manager/types"
910

1011
descriptor "ocm.software/open-component-model/bindings/go/descriptor/runtime"
1112
"ocm.software/open-component-model/bindings/go/plugin/internal/dummytype"
@@ -48,8 +49,13 @@ func (m *mockPlugin) ListComponentVersions(_ context.Context, _ repov1.ListCompo
4849
return []string{"v0.0.1", "v0.0.2"}, nil
4950
}
5051

51-
func (m *mockPlugin) GetLocalResource(_ context.Context, _ repov1.GetLocalResourceRequest[*dummyv1.Repository], _ map[string]string) error {
52-
return nil
52+
func (m *mockPlugin) GetLocalResource(_ context.Context, _ repov1.GetLocalResourceRequest[*dummyv1.Repository], _ map[string]string) (repov1.GetLocalResourceResponse, error) {
53+
return repov1.GetLocalResourceResponse{
54+
Location: types.Location{
55+
LocationType: types.LocationTypeLocalFile,
56+
Value: "/dummy/local-file",
57+
},
58+
}, nil
5359
}
5460

5561
func (m *mockPlugin) GetIdentity(ctx context.Context, typ repov1.GetIdentityRequest[*dummyv1.Repository]) (runtime.Identity, error) {

bindings/go/plugin/manager/registries/componentversionrepository/handlers.go

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import (
1212
descriptor "ocm.software/open-component-model/bindings/go/descriptor/runtime"
1313
"ocm.software/open-component-model/bindings/go/plugin/manager/contracts/ocmrepository/v1"
1414
"ocm.software/open-component-model/bindings/go/plugin/manager/registries/plugins"
15-
"ocm.software/open-component-model/bindings/go/plugin/manager/types"
1615
"ocm.software/open-component-model/bindings/go/runtime"
1716
)
1817

@@ -106,7 +105,7 @@ func AddComponentVersionHandlerFunc[T runtime.Typed](f func(ctx context.Context,
106105
}
107106
}
108107

109-
func GetLocalResourceHandlerFunc[T runtime.Typed](f func(ctx context.Context, request v1.GetLocalResourceRequest[T], credentials map[string]string) error, scheme *runtime.Scheme, proto T) http.HandlerFunc {
108+
func GetLocalResourceHandlerFunc[T runtime.Typed](f func(ctx context.Context, request v1.GetLocalResourceRequest[T], credentials map[string]string) (v1.GetLocalResourceResponse, error), scheme *runtime.Scheme, proto T) http.HandlerFunc {
110109
return func(writer http.ResponseWriter, request *http.Request) {
111110
rawCredentials := []byte(request.Header.Get("Authorization"))
112111
credentials := map[string]string{}
@@ -118,10 +117,6 @@ func GetLocalResourceHandlerFunc[T runtime.Typed](f func(ctx context.Context, re
118117
query := request.URL.Query()
119118
name := query.Get("name")
120119
version := query.Get("version")
121-
targetLocation := types.Location{
122-
LocationType: types.LocationType(query.Get("target_location_type")),
123-
Value: query.Get("target_location_value"),
124-
}
125120
identityQuery := query.Get("identity")
126121
decodedIdentity, err := base64.StdEncoding.DecodeString(identityQuery)
127122
if err != nil {
@@ -137,13 +132,18 @@ func GetLocalResourceHandlerFunc[T runtime.Typed](f func(ctx context.Context, re
137132
}
138133
}
139134

140-
if err := f(request.Context(), v1.GetLocalResourceRequest[T]{
141-
Repository: proto,
142-
Name: name,
143-
Version: version,
144-
Identity: identity,
145-
TargetLocation: targetLocation,
146-
}, credentials); err != nil {
135+
response, err := f(request.Context(), v1.GetLocalResourceRequest[T]{
136+
Repository: proto,
137+
Name: name,
138+
Version: version,
139+
Identity: identity,
140+
}, credentials)
141+
if err != nil {
142+
plugins.NewError(err, http.StatusInternalServerError).Write(writer)
143+
return
144+
}
145+
146+
if err := json.NewEncoder(writer).Encode(response); err != nil {
147147
plugins.NewError(err, http.StatusInternalServerError).Write(writer)
148148
return
149149
}

bindings/go/plugin/manager/registries/componentversionrepository/handlers_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -130,8 +130,8 @@ func TestGetLocalResourceHandlerFunc(t *testing.T) {
130130
{
131131
name: "GetLocalResourceHandlerFunc unauthorized error",
132132
handlerFunc: func(t *testing.T) http.HandlerFunc {
133-
handler := GetLocalResourceHandlerFunc(func(ctx context.Context, request repov1.GetLocalResourceRequest[*dummyv1.Repository], credentials map[string]string) error {
134-
return nil
133+
handler := GetLocalResourceHandlerFunc(func(ctx context.Context, request repov1.GetLocalResourceRequest[*dummyv1.Repository], credentials map[string]string) (repov1.GetLocalResourceResponse, error) {
134+
return repov1.GetLocalResourceResponse{}, nil
135135
}, scheme, &dummyv1.Repository{})
136136

137137
return handler
@@ -153,10 +153,10 @@ func TestGetLocalResourceHandlerFunc(t *testing.T) {
153153
{
154154
name: "GetLocalResourceHandlerFunc success",
155155
handlerFunc: func(t *testing.T) http.HandlerFunc {
156-
handler := GetLocalResourceHandlerFunc(func(ctx context.Context, request repov1.GetLocalResourceRequest[*dummyv1.Repository], credentials map[string]string) error {
156+
handler := GetLocalResourceHandlerFunc(func(ctx context.Context, request repov1.GetLocalResourceRequest[*dummyv1.Repository], credentials map[string]string) (repov1.GetLocalResourceResponse, error) {
157157
require.Equal(t, "component", request.Name)
158158
require.Equal(t, "1.0.0", request.Version)
159-
return nil
159+
return repov1.GetLocalResourceResponse{}, nil
160160
}, scheme, &dummyv1.Repository{})
161161

162162
return handler

bindings/go/plugin/manager/registries/componentversionrepository/implementations.go

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import (
88
"fmt"
99
"log/slog"
1010
"net/http"
11-
"os"
1211

1312
descriptor "ocm.software/open-component-model/bindings/go/descriptor/runtime"
1413
v2 "ocm.software/open-component-model/bindings/go/descriptor/v2"
@@ -171,42 +170,36 @@ func (r *RepositoryPlugin) AddLocalResource(ctx context.Context, request v1.Post
171170
return &resources[0], nil
172171
}
173172

174-
func (r *RepositoryPlugin) GetLocalResource(ctx context.Context, request v1.GetLocalResourceRequest[runtime.Typed], credentials map[string]string) error {
173+
func (r *RepositoryPlugin) GetLocalResource(ctx context.Context, request v1.GetLocalResourceRequest[runtime.Typed], credentials map[string]string) (v1.GetLocalResourceResponse, error) {
175174
var params []plugins.KV
176175
addParam := func(k, v string) {
177176
params = append(params, plugins.KV{Key: k, Value: v})
178177
}
179178
addParam("name", request.Name)
180179
addParam("version", request.Version)
181-
addParam("target_location_type", string(request.TargetLocation.LocationType))
182-
addParam("target_location_value", request.TargetLocation.Value)
183180
identityEncoded, err := json.Marshal(request.Identity)
181+
var response v1.GetLocalResourceResponse
184182
if err != nil {
185-
return err
183+
return response, err
186184
}
187185
identityBase64 := base64.StdEncoding.EncodeToString(identityEncoded)
188186
addParam("identity", identityBase64)
189187

190188
credHeader, err := toCredentials(credentials)
191189
if err != nil {
192-
return err
190+
return response, err
193191
}
194192

195193
// We know we only have this single schema for all endpoints which require validation.
196194
if err := r.validateEndpoint(request.Repository, r.jsonSchema); err != nil {
197-
return err
195+
return response, err
198196
}
199197

200-
if err := plugins.Call(ctx, r.client, r.config.Type, r.location, DownloadLocalResource, http.MethodGet, plugins.WithQueryParams(params), plugins.WithHeader(credHeader)); err != nil {
201-
return fmt.Errorf("failed to get local resource %s:%s from %s: %w", request.Name, request.Version, r.ID, err)
198+
if err := plugins.Call(ctx, r.client, r.config.Type, r.location, DownloadLocalResource, http.MethodGet, plugins.WithQueryParams(params), plugins.WithHeader(credHeader), plugins.WithResult(&response)); err != nil {
199+
return response, fmt.Errorf("failed to get local resource %s:%s from %s: %w", request.Name, request.Version, r.ID, err)
202200
}
203201

204-
_, err = os.Stat(request.TargetLocation.Value)
205-
if err != nil {
206-
return fmt.Errorf("failed to stat target file: %w", err)
207-
}
208-
209-
return nil
202+
return response, nil
210203
}
211204

212205
func (r *RepositoryPlugin) GetIdentity(ctx context.Context, typ v1.GetIdentityRequest[runtime.Typed]) (runtime.Identity, error) {

bindings/go/plugin/manager/registries/componentversionrepository/implementations_test.go

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -208,12 +208,18 @@ func TestAddLocalResource(t *testing.T) {
208208
}
209209

210210
func TestGetLocalResource(t *testing.T) {
211+
f, err := os.CreateTemp("", "temp_file")
212+
require.NoError(t, err)
213+
response := &repov1.GetLocalResourceResponse{
214+
Location: types.Location{
215+
LocationType: types.LocationTypeLocalFile,
216+
Value: f.Name(),
217+
},
218+
}
211219
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
212220
if r.URL.Path == "/"+DownloadLocalResource && r.Method == http.MethodGet {
213-
location := r.URL.Query().Get("target_location_value")
214-
require.NoError(t, os.WriteFile(location, []byte(`test`), os.ModePerm))
215-
216-
w.WriteHeader(http.StatusOK)
221+
require.NoError(t, os.WriteFile(f.Name(), []byte(`test`), os.ModePerm))
222+
require.NoError(t, json.NewEncoder(w).Encode(response))
217223

218224
return
219225
}
@@ -229,28 +235,24 @@ func TestGetLocalResource(t *testing.T) {
229235
PluginType: types.ComponentVersionRepositoryPluginType,
230236
}, server.URL, []byte(`{}`))
231237

232-
f, err := os.CreateTemp("", "temp_file")
233-
require.NoError(t, err)
234238
t.Cleanup(func() {
235239
require.NoError(t, f.Close())
236240
require.NoError(t, os.Remove(f.Name()))
237241
})
238242

239243
ctx := context.Background()
240-
err = plugin.GetLocalResource(ctx, repov1.GetLocalResourceRequest[runtime.Typed]{
244+
resp, err := plugin.GetLocalResource(ctx, repov1.GetLocalResourceRequest[runtime.Typed]{
241245
Repository: &dummyv1.Repository{},
242246
Name: "test-plugin",
243247
Version: "v1.0.0",
244-
TargetLocation: types.Location{
245-
LocationType: types.LocationTypeLocalFile,
246-
Value: f.Name(),
247-
},
248248
}, map[string]string{})
249249
require.NoError(t, err)
250250

251251
content, err := os.ReadFile(f.Name())
252252
require.NoError(t, err)
253253
require.Equal(t, "test", string(content))
254+
require.Equal(t, types.LocationTypeLocalFile, resp.Location.LocationType)
255+
require.Equal(t, f.Name(), resp.Location.Value)
254256
}
255257

256258
func defaultDescriptor() *v2.Descriptor {

bindings/go/plugin/manager/registries/componentversionrepository/registry.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,9 @@ loop:
131131
}
132132

133133
func (r *RepositoryRegistry) GetPlugin(ctx context.Context, spec runtime.Typed) (v1.ReadWriteOCMRepositoryPluginContract[runtime.Typed], error) {
134+
r.mu.Lock()
135+
defer r.mu.Unlock()
136+
134137
if _, err := r.internalComponentVersionRepositoryScheme.DefaultType(spec); err != nil {
135138
return nil, fmt.Errorf("failed to default type for prototype %T: %w", spec, err)
136139
}

0 commit comments

Comments
 (0)