Skip to content

Commit

Permalink
Added methods to get and put data into context (#1938)
Browse files Browse the repository at this point in the history
  • Loading branch information
jimil749 authored Aug 3, 2021
1 parent 0ffa377 commit afe617a
Show file tree
Hide file tree
Showing 3 changed files with 176 additions and 0 deletions.
5 changes: 5 additions & 0 deletions changelog/unreleased/context-methods.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Enhancement: Add methods to get and put context values

Added `GetKeyValues` and `PutKeyValues` methods to fetch/put values from/to context.

https://github.com/cs3org/reva/pull/1938
69 changes: 69 additions & 0 deletions pkg/appctx/ctxmap.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// Copyright 2018-2021 CERN
//
// 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.
//
// In applying this license, CERN does not waive the privileges and immunities
// granted to it by virtue of its status as an Intergovernmental Organization
// or submit itself to any jurisdiction.

package appctx

import (
"context"
"reflect"
"unsafe"
)

// PutKeyValuesToCtx puts all the key-value pairs from the provided map to a background context.
func PutKeyValuesToCtx(m map[interface{}]interface{}) context.Context {
ctx := context.Background()
for key, value := range m {
ctx = context.WithValue(ctx, key, value)
}
return ctx
}

// GetKeyValuesFromCtx retrieves all the key-value pairs from the provided context.
func GetKeyValuesFromCtx(ctx context.Context) map[interface{}]interface{} {
m := make(map[interface{}]interface{})
getKeyValue(ctx, m)
return m
}

func getKeyValue(ctx interface{}, m map[interface{}]interface{}) {
ctxVals := reflect.ValueOf(ctx).Elem()
ctxType := reflect.TypeOf(ctx).Elem()

if ctxType.Kind() == reflect.Struct {
for i := 0; i < ctxVals.NumField(); i++ {
currField, currIf := extractField(ctxVals, ctxType, i)
switch currField {
case "Context":
getKeyValue(currIf, m)
case "key":
nextField, nextIf := extractField(ctxVals, ctxType, i+1)
if nextField == "val" {
m[currIf] = nextIf
i++
}
}
}
}
}

func extractField(vals reflect.Value, fieldType reflect.Type, pos int) (string, interface{}) {
currVal := vals.Field(pos)
currVal = reflect.NewAt(currVal.Type(), unsafe.Pointer(currVal.UnsafeAddr())).Elem()
currField := fieldType.Field(pos)
return currField.Name, currVal.Interface()
}
102 changes: 102 additions & 0 deletions pkg/appctx/ctxmap_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
// Copyright 2018-2021 CERN
//
// 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.
//
// In applying this license, CERN does not waive the privileges and immunities
// granted to it by virtue of its status as an Intergovernmental Organization
// or submit itself to any jurisdiction.

package appctx

import (
"context"
"testing"

userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
"github.com/stretchr/testify/assert"
)

type ctxStringKey string
type ctxIntKey int

func TestGetKeyValues(t *testing.T) {
tests := []struct {
name string
ctx context.Context
m map[interface{}]interface{}
}{
{
"Background context",
context.Background(),
map[interface{}]interface{}{},
},
{
"Context with Values",
context.WithValue(context.Background(), ctxStringKey("key"), "value"),
map[interface{}]interface{}{
ctxStringKey("key"): "value",
},
},
{
"Context with user object",
context.WithValue(context.WithValue(context.Background(), ctxStringKey("key"), "value"), ctxStringKey("user"), &userpb.User{Username: "einstein"}),
map[interface{}]interface{}{
ctxStringKey("key"): "value",
ctxStringKey("user"): &userpb.User{Username: "einstein"},
},
},
{
"Nested Context with Values of different types",
context.WithValue(context.WithValue(context.Background(), ctxStringKey("key"), "value"), ctxIntKey(123), "value2"),
map[interface{}]interface{}{
ctxStringKey("key"): "value",
ctxIntKey(123): "value2",
},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
kvMap := GetKeyValuesFromCtx(tt.ctx)
assert.Equal(t, tt.m, kvMap)
})
}
}

func TestPutKeyValues(t *testing.T) {
tests := []struct {
name string
m map[interface{}]interface{}
ctx context.Context
}{
{
"empty context",
map[interface{}]interface{}{},
context.Background(),
},
{
"single kv pair",
map[interface{}]interface{}{
ctxStringKey("key"): "value",
},
context.WithValue(context.Background(), ctxStringKey("key"), "value"),
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ctx := PutKeyValuesToCtx(tt.m)
assert.Equal(t, tt.ctx, ctx)
})
}
}

0 comments on commit afe617a

Please sign in to comment.