github.com/voedger/voedger@v0.0.0-20240520144910-273e84102129/pkg/sys/invite/impl_applyjoinworkspace.go (about)

     1  /*
     2   * Copyright (c) 2023-present unTill Pro, Ltd.
     3   */
     4  
     5  package invite
     6  
     7  import (
     8  	"fmt"
     9  
    10  	"github.com/voedger/voedger/pkg/appdef"
    11  	"github.com/voedger/voedger/pkg/istructs"
    12  	"github.com/voedger/voedger/pkg/itokens"
    13  	payloads "github.com/voedger/voedger/pkg/itokens-payloads"
    14  	"github.com/voedger/voedger/pkg/state"
    15  	"github.com/voedger/voedger/pkg/sys/authnz"
    16  	"github.com/voedger/voedger/pkg/sys/collection"
    17  	coreutils "github.com/voedger/voedger/pkg/utils"
    18  	"github.com/voedger/voedger/pkg/utils/federation"
    19  )
    20  
    21  func asyncProjectorApplyJoinWorkspace(timeFunc coreutils.TimeFunc, federation federation.IFederation, tokens itokens.ITokens) istructs.Projector {
    22  	return istructs.Projector{
    23  		Name: qNameAPApplyJoinWorkspace,
    24  		Func: applyJoinWorkspace(timeFunc, federation, tokens),
    25  	}
    26  }
    27  
    28  func applyJoinWorkspace(timeFunc coreutils.TimeFunc, federation federation.IFederation, tokens itokens.ITokens) func(event istructs.IPLogEvent, state istructs.IState, intents istructs.IIntents) (err error) {
    29  	return func(event istructs.IPLogEvent, s istructs.IState, intents istructs.IIntents) (err error) {
    30  		// it is AFTER EXECUTE ON (InitiateJoinWorkspace) so no doc checking here
    31  		skbCDocInvite, err := s.KeyBuilder(state.Record, qNameCDocInvite)
    32  		if err != nil {
    33  			return
    34  		}
    35  		skbCDocInvite.PutRecordID(state.Field_ID, event.ArgumentObject().AsRecordID(field_InviteID))
    36  		svCDocInvite, err := s.MustExist(skbCDocInvite)
    37  		if err != nil {
    38  			return
    39  		}
    40  
    41  		login := svCDocInvite.AsString(Field_Login)
    42  		subjectExists, err := SubjectExistsByLogin(login, s) // for backward compatibility
    43  		if err == nil && !subjectExists {
    44  			actualLogin := svCDocInvite.AsString(field_ActualLogin)
    45  			subjectExists, err = SubjectExistsByLogin(actualLogin, s)
    46  		}
    47  		if err != nil {
    48  			// notest
    49  			return err
    50  		}
    51  
    52  		if subjectExists {
    53  			// cdoc.sys.Subject exists by login -> skip
    54  			// see https://github.com/voedger/voedger/issues/1107
    55  			// && svCDocInvite.AsInt32(field_State) == State_Joined -> insert cdoc.sys.Subject with an existing login -> unique violation -> the projector stuck
    56  			return nil
    57  		}
    58  
    59  		skbCDocWorkspaceDescriptor, err := s.KeyBuilder(state.Record, authnz.QNameCDocWorkspaceDescriptor)
    60  		if err != nil {
    61  			return err
    62  		}
    63  		skbCDocWorkspaceDescriptor.PutQName(state.Field_Singleton, authnz.QNameCDocWorkspaceDescriptor)
    64  		svCDocWorkspaceDescriptor, err := s.MustExist(skbCDocWorkspaceDescriptor)
    65  		if err != nil {
    66  			return
    67  		}
    68  
    69  		appQName := s.App()
    70  
    71  		token, err := payloads.GetSystemPrincipalToken(tokens, appQName)
    72  		if err != nil {
    73  			return
    74  		}
    75  		_, err = federation.Func(
    76  			fmt.Sprintf("api/%s/%d/c.sys.CreateJoinedWorkspace", appQName, svCDocInvite.AsInt64(field_InviteeProfileWSID)),
    77  			fmt.Sprintf(`{"args":{"Roles":"%s","InvitingWorkspaceWSID":%d,"WSName":"%s"}}`,
    78  				svCDocInvite.AsString(Field_Roles), event.Workspace(), svCDocWorkspaceDescriptor.AsString(authnz.Field_WSName)),
    79  			coreutils.WithAuthorizeBy(token),
    80  			coreutils.WithDiscardResponse(),
    81  		)
    82  		if err != nil {
    83  			return
    84  		}
    85  
    86  		// Find cdoc.sys.Subject by cdoc.air.Invite
    87  		skbViewCollection, err := s.KeyBuilder(state.View, collection.QNameCollectionView)
    88  		if err != nil {
    89  			return
    90  		}
    91  		skbViewCollection.PutInt32(collection.Field_PartKey, collection.PartitionKeyCollection)
    92  		skbViewCollection.PutQName(collection.Field_DocQName, QNameCDocSubject)
    93  
    94  		var svCDocSubject istructs.IStateValue
    95  		if svCDocInvite.AsRecordID(field_SubjectID) != istructs.NullRecordID {
    96  			err = s.Read(skbViewCollection, func(key istructs.IKey, value istructs.IStateValue) (err error) {
    97  				if svCDocSubject != nil {
    98  					return nil
    99  				}
   100  				if svCDocInvite.AsRecordID(field_SubjectID) == value.AsRecordID(appdef.SystemField_ID) {
   101  					svCDocSubject = value
   102  				}
   103  				return nil
   104  			})
   105  			if err != nil {
   106  				return
   107  			}
   108  		}
   109  
   110  		var body string
   111  		// Store cdoc.sys.Subject
   112  		if svCDocSubject == nil {
   113  			// svCDocInvite.AsString(Field_Login) is actually c.sys.InitiateInvitationByEMail.Email
   114  			body = fmt.Sprintf(`{"cuds":[{"fields":{"sys.ID":1,"sys.QName":"sys.Subject","Login":"%s","Roles":"%s","SubjectKind":%d,"ProfileWSID":%d}}]}`,
   115  				svCDocInvite.AsString(field_ActualLogin), svCDocInvite.AsString(Field_Roles), svCDocInvite.AsInt32(authnz.Field_SubjectKind),
   116  				svCDocInvite.AsInt64(field_InviteeProfileWSID))
   117  		} else {
   118  			body = fmt.Sprintf(`{"cuds":[{"sys.ID":%d,"fields":{"Roles":"%s"}}]}`,
   119  				svCDocSubject.AsRecordID(appdef.SystemField_ID), svCDocInvite.AsString(Field_Roles))
   120  		}
   121  		resp, err := federation.Func(
   122  			fmt.Sprintf("api/%s/%d/c.sys.CUD", appQName, event.Workspace()),
   123  			body,
   124  			coreutils.WithAuthorizeBy(token))
   125  		if err != nil {
   126  			return
   127  		}
   128  
   129  		//Store cdoc.sys.Invite
   130  		//TODO why Login update???
   131  		if svCDocSubject == nil {
   132  			body = fmt.Sprintf(`{"cuds":[{"sys.ID":%d,"fields":{"State":%d,"SubjectID":%d,"Updated":%d}}]}`,
   133  				svCDocInvite.AsRecordID(appdef.SystemField_ID), State_Joined, resp.NewID(), timeFunc().UnixMilli())
   134  		} else {
   135  			body = fmt.Sprintf(`{"cuds":[{"sys.ID":%d,"fields":{"State":%d,"Updated":%d}}]}`,
   136  				svCDocInvite.AsRecordID(appdef.SystemField_ID), State_Joined, timeFunc().UnixMilli())
   137  		}
   138  		_, err = federation.Func(
   139  			fmt.Sprintf("api/%s/%d/c.sys.CUD", appQName, event.Workspace()),
   140  			body,
   141  			coreutils.WithAuthorizeBy(token),
   142  			coreutils.WithDiscardResponse())
   143  
   144  		return err
   145  	}
   146  }