Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Concurrent/Parallel user sharing #15

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions features/concurrent-user-sharing.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
Feature: concurrent user sharing
As a user
I want to share their resource concurrently to different multiple users


Scenario: users make concurrent sharing to each other
Given user "admin" has logged in with password "admin"
And user "admin" has created a personal space with the alias "Admin Home"
And user "admin" has uploaded a file "testfile.txt" with content "concurrent sharing" in the home directory with the alias "testfile1"
When user "admin" shares a file "testfile.txt" with the following users concurrently
| users |
| marie |
| moss |
| richard |
| katherine |
| einstein |
Then the concurrent user sharing should have been successfull
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module github.com/owncloud/cs3api-validator
go 1.18

require (
github.com/cs3org/go-cs3apis v0.0.0-20221012090518-ef2996678965
github.com/cs3org/go-cs3apis v0.0.0-20230727093620-0f4399be4543
github.com/cs3org/reva/v2 v2.10.1-0.20221019091055-df0a189e218d
github.com/cucumber/godog v0.12.2
github.com/cucumber/messages-go/v16 v16.0.1
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsr
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/cs3org/go-cs3apis v0.0.0-20221012090518-ef2996678965 h1:y4n2j68LLnvac+zw/al8MfPgO5aQiIwLmHM/JzYN8AM=
github.com/cs3org/go-cs3apis v0.0.0-20221012090518-ef2996678965/go.mod h1:UXha4TguuB52H14EMoSsCqDj7k8a/t7g4gVP+bgY5LY=
github.com/cs3org/go-cs3apis v0.0.0-20230727093620-0f4399be4543 h1:IFo6dj0XEOIA6i2baRWMC3vd+fAmuIUAVfSf77ZhoQg=
github.com/cs3org/go-cs3apis v0.0.0-20230727093620-0f4399be4543/go.mod h1:UXha4TguuB52H14EMoSsCqDj7k8a/t7g4gVP+bgY5LY=
github.com/cs3org/reva/v2 v2.10.1-0.20221019091055-df0a189e218d h1:pYCrKLmcSF15jwWpougleGIeng+FehQf0LePxhR04kQ=
github.com/cs3org/reva/v2 v2.10.1-0.20221019091055-df0a189e218d/go.mod h1:lq+LRpBDYU1vHUmJDeK7sGquREciO8GDj5/SYIibMPY=
github.com/cucumber/gherkin-go/v19 v19.0.3 h1:mMSKu1077ffLbTJULUfM5HPokgeBcIGboyeNUof1MdE=
Expand Down
3 changes: 3 additions & 0 deletions scenario/featurecontext.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"github.com/owncloud/cs3api-validator/steps/login"
"github.com/owncloud/cs3api-validator/steps/publicshare"
"github.com/owncloud/cs3api-validator/steps/resources"
"github.com/owncloud/cs3api-validator/steps/share"
"github.com/owncloud/cs3api-validator/steps/spaces"
)

Expand All @@ -17,6 +18,7 @@ type featureContext struct {
*publicshare.PublicShareFeatureContext
*resources.ResourcesFeatureContext
*spaces.SpacesFeatureContext
*share.ShareTestFeatureContext
}

// newFeatureContext returns a new feature context for the scenario initialization
Expand All @@ -32,6 +34,7 @@ func newFeatureContext(sc *godog.ScenarioContext) *featureContext {
PublicShareFeatureContext: publicshare.NewPublicShareFeatureContext(fc, sc),
ResourcesFeatureContext: resources.NewResourcesFeatureContext(fc, sc),
SpacesFeatureContext: spaces.NewSpacesFeatureContext(fc, sc),
ShareTestFeatureContext: share.NewShareTestFeatureContext(fc, sc),
}
return uc
}
23 changes: 23 additions & 0 deletions steps/share/context.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package share

import (
"github.com/cucumber/godog"
"github.com/owncloud/cs3api-validator/featurecontext"
)

// ShareTestFeatureContext holds values which are used across test steps
type ShareTestFeatureContext struct {
*featurecontext.FeatureContext
}

func NewShareTestFeatureContext(fc *featurecontext.FeatureContext, sc *godog.ScenarioContext) *ShareTestFeatureContext {
nsc := &ShareTestFeatureContext{FeatureContext: fc}
nsc.Register(sc)
return nsc
}

func (f *ShareTestFeatureContext) Register(ctx *godog.ScenarioContext) {
// steps
ctx.Step(`^user "([^"]*)" shares a file "([^"]*)" with the following users concurrently$`, f.UserSharesAFileWithTheFollowingUsers)
ctx.Step(`^the concurrent user sharing should have been successfull$`, f.TheConcurrentUserSharingShouldHaveBeenSuccessfull)
}
170 changes: 170 additions & 0 deletions steps/share/steps.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
package share

import (
"fmt"
identityv1beta1 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
collaborationv1beta1 "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/v1beta1"
providerv1beta1 "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
"github.com/cucumber/godog"
"github.com/cucumber/messages-go/v16"
"github.com/owncloud/cs3api-validator/featurecontext"
"github.com/owncloud/cs3api-validator/helpers"
"sync"
)

type CreateShareResult struct {
ResourceInformation *providerv1beta1.ResourceInfo
InformationOfSharee *identityv1beta1.UserId
Status *rpc.Status
Error error
}

var concurentResults []*CreateShareResult

func (f *ShareTestFeatureContext) UserSharesAFileWithTheFollowingUsers(shareer string, resourceName string, sharees *godog.Table) error {
ctx, err := f.GetAuthContext(shareer)
if err != nil {
return err
}
rows := sharees.Rows
if len(rows) == 0 {
return fmt.Errorf("empty gherkin table")
}

if rows[0].Cells[0].Value != "users" {
return fmt.Errorf("the first line of the tables needs to be in the form | <users> |")
}

var rowsValues []*messages.PickleTableRow
rowsValues = append(rowsValues, rows[+1:]...)

// to create a share we need information of each sharee
var collectedShareesInfos []* identityv1beta1.UserId

for _, row := range rowsValues {
var sharee = row.Cells[0].Value
shareeInformations, err := f.Client.FindUsers(
ctx,
&identityv1beta1.FindUsersRequest{
Filter: sharee,
},
)
if err != nil {
return err
}
if len(shareeInformations.GetUsers()) == 0 {
return fmt.Errorf("Could not find the user " + sharee)
}
collectedShareesInfos = append(collectedShareesInfos, shareeInformations.GetUsers()[0].GetId())
}

// also we need resource information to create a share for different users
var res featurecontext.ResourceAlias
res, ok := f.ResourceReferences["Admin Home"]
if !ok {
return fmt.Errorf("cannot find key %s in the remembered resource references map", "Admin Home")
}
if res.Info.Type != providerv1beta1.ResourceType_RESOURCE_TYPE_CONTAINER {
return fmt.Errorf("we cannot call list inside non-container resources")
}
resp, err := f.Client.ListContainer(
ctx,
&providerv1beta1.ListContainerRequest{
Ref: res.Ref,
},
)
if err != nil {
return err
}
if resp.Status.Code != rpc.Code_CODE_OK {
return helpers.FormatError(resp.Status)
}
f.Response = resp
listContainerResponse, ok := f.Response.(*providerv1beta1.ListContainerResponse)
if !ok {
return fmt.Errorf("expected to receive a ListContainerResponse but got something different")
}


// we want specific file info to get shared
var resourceInfo *providerv1beta1.ResourceInfo
for _, resource := range listContainerResponse.GetInfos() {
if resource.Name == resourceName {
resourceInfo = resource
break
}
}

if resourceInfo == nil {
return fmt.Errorf("Resource name " + resourceName + " could not be found in the container " + "Admin Home")
}

// once resourceInformation and sharees information is known then we can make a concurrent share request to the server
var wg sync.WaitGroup
// store each result of the request during concurrent sharing
resultChannel := make(chan CreateShareResult, len(collectedShareesInfos))

for _, UserId := range collectedShareesInfos {
wg.Add(1)
go func(UserId *identityv1beta1.UserId) {
defer wg.Done()
createShareResponse, err := f.Client.CreateShare(
ctx,
&collaborationv1beta1.CreateShareRequest{
ResourceInfo: resourceInfo,
Grant: &collaborationv1beta1.ShareGrant{
Grantee: &providerv1beta1.Grantee{
Type: 1,
Id: &providerv1beta1.Grantee_UserId{UserId: UserId},
},
Permissions: &collaborationv1beta1.SharePermissions{
Permissions: &providerv1beta1.ResourcePermissions{
AddGrant: true,
},
},
},
},
)

result := CreateShareResult{
ResourceInformation: resourceInfo,
InformationOfSharee: UserId,
Status: createShareResponse.GetStatus(),
Error: err,
}
resultChannel <- result

}(UserId)
}
wg.Wait()
close(resultChannel)

for result := range resultChannel {
concurentResults = append(concurentResults, &CreateShareResult{
ResourceInformation: result.ResourceInformation,
InformationOfSharee: result.InformationOfSharee,
Status: result.Status,
Error: result.Error,
})
}

return nil
}

func (f *ShareTestFeatureContext) TheConcurrentUserSharingShouldHaveBeenSuccessfull() error {
//collect the result summary if there is any error while concurrent sharing
var isThereConcurrentError bool
var errorSummary string
for _, concurentResult := range concurentResults {
if concurentResult.Status.Code != rpc.Code_CODE_OK || concurentResult.Error != nil {
isThereConcurrentError = true
errorSummary = errorSummary + concurentResult.ResourceInformation.Name + " did not get shared to user with id " + concurentResult.InformationOfSharee.OpaqueId + "\n"
}
}
if isThereConcurrentError {
return fmt.Errorf(errorSummary)
}
return nil
}