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

     1  /*
     2   * Copyright (c) 2020-present unTill Pro, Ltd.
     3   * @author Denis Gribanov
     4   */
     5  
     6  package workspace
     7  
     8  import (
     9  	"fmt"
    10  	"net/http"
    11  
    12  	"github.com/voedger/voedger/pkg/appdef"
    13  	"github.com/voedger/voedger/pkg/goutils/logger"
    14  	"github.com/voedger/voedger/pkg/istructs"
    15  	"github.com/voedger/voedger/pkg/istructsmem"
    16  	"github.com/voedger/voedger/pkg/itokens"
    17  	payloads "github.com/voedger/voedger/pkg/itokens-payloads"
    18  	"github.com/voedger/voedger/pkg/state"
    19  	"github.com/voedger/voedger/pkg/sys/authnz"
    20  	"github.com/voedger/voedger/pkg/sys/collection"
    21  	"github.com/voedger/voedger/pkg/sys/invite"
    22  	coreutils "github.com/voedger/voedger/pkg/utils"
    23  	"github.com/voedger/voedger/pkg/utils/federation"
    24  )
    25  
    26  func provideDeactivateWorkspace(cfg *istructsmem.AppConfigType, tokensAPI itokens.ITokens, federation federation.IFederation) {
    27  
    28  	// c.sys.DeactivateWorkspace
    29  	// target app, target WSID
    30  	cfg.Resources.Add(istructsmem.NewCommandFunction(
    31  		qNameCmdInitiateDeactivateWorkspace,
    32  		cmdInitiateDeactivateWorkspaceExec,
    33  	))
    34  
    35  	// c.sys.OnWorkspaceDeactivated
    36  	// owner app, owner WSID
    37  	cfg.Resources.Add(istructsmem.NewCommandFunction(
    38  		appdef.NewQName(appdef.SysPackage, "OnWorkspaceDeactivated"),
    39  		cmdOnWorkspaceDeactivatedExec,
    40  	))
    41  
    42  	// c.sys.OnJoinedWorkspaceDeactivated
    43  	// target app, profile WSID
    44  	cfg.Resources.Add(istructsmem.NewCommandFunction(
    45  		appdef.NewQName(appdef.SysPackage, "OnJoinedWorkspaceDeactivated"),
    46  		cmdOnJoinedWorkspaceDeactivateExec,
    47  	))
    48  
    49  	// c.sys.OnChildWorkspaceDeactivated
    50  	// ownerApp/ownerWSID
    51  	cfg.Resources.Add(istructsmem.NewCommandFunction(
    52  		appdef.NewQName(appdef.SysPackage, "OnChildWorkspaceDeactivated"),
    53  		cmdOnChildWorkspaceDeactivatedExec,
    54  	))
    55  
    56  	// target app, target WSID
    57  	cfg.AddAsyncProjectors(istructs.Projector{
    58  		Name: qNameProjectorApplyDeactivateWorkspace,
    59  		Func: projectorApplyDeactivateWorkspace(federation, tokensAPI),
    60  	})
    61  }
    62  
    63  func cmdInitiateDeactivateWorkspaceExec(args istructs.ExecCommandArgs) (err error) {
    64  	kb, err := args.State.KeyBuilder(state.Record, authnz.QNameCDocWorkspaceDescriptor)
    65  	if err != nil {
    66  		// notest
    67  		return err
    68  	}
    69  	kb.PutQName(state.Field_Singleton, authnz.QNameCDocWorkspaceDescriptor)
    70  	wsDesc, err := args.State.MustExist(kb)
    71  	if err != nil {
    72  		// notest
    73  		return err
    74  	}
    75  	status := wsDesc.AsInt32(authnz.Field_Status)
    76  	if status != int32(authnz.WorkspaceStatus_Active) {
    77  		return coreutils.NewHTTPErrorf(http.StatusConflict, "Workspace Status is not Active")
    78  	}
    79  
    80  	wsDescUpdater, err := args.Intents.UpdateValue(kb, wsDesc)
    81  	if err != nil {
    82  		// notest
    83  		return err
    84  	}
    85  	wsDescUpdater.PutInt32(authnz.Field_Status, int32(authnz.WorkspaceStatus_ToBeDeactivated))
    86  	return nil
    87  }
    88  
    89  func cmdOnJoinedWorkspaceDeactivateExec(args istructs.ExecCommandArgs) (err error) {
    90  	invitedToWSID := args.ArgumentObject.AsInt64(field_InvitedToWSID)
    91  	svCDocJoinedWorkspace, skb, ok, err := invite.GetCDocJoinedWorkspace(args.State, invitedToWSID)
    92  	if err != nil || !ok {
    93  		return err
    94  	}
    95  	if !svCDocJoinedWorkspace.AsBool(appdef.SystemField_IsActive) {
    96  		return nil
    97  	}
    98  	cdocJoinedWorkspaceUpdater, err := args.Intents.UpdateValue(skb, svCDocJoinedWorkspace)
    99  	if err != nil {
   100  		// notest
   101  		return err
   102  	}
   103  	cdocJoinedWorkspaceUpdater.PutBool(appdef.SystemField_IsActive, false)
   104  	return nil
   105  }
   106  
   107  // app/pseudoProfileWSID, ownerApp
   108  func cmdOnWorkspaceDeactivatedExec(args istructs.ExecCommandArgs) (err error) {
   109  	ownerWSID := args.ArgumentObject.AsInt64(Field_OwnerWSID)
   110  	wsName := args.ArgumentObject.AsString(authnz.Field_WSName)
   111  	kb, err := args.State.KeyBuilder(state.View, QNameViewWorkspaceIDIdx)
   112  	if err != nil {
   113  		// notest
   114  		return err
   115  	}
   116  	kb.PutInt64(Field_OwnerWSID, ownerWSID)
   117  	kb.PutString(authnz.Field_WSName, wsName)
   118  	viewRec, ok, err := args.State.CanExist(kb)
   119  	if err != nil {
   120  		// notest
   121  		return err
   122  	}
   123  	if !ok {
   124  		logger.Verbose("workspace", wsName, ":", ownerWSID, "is not mentioned in view.sys.WorkspaceIDId")
   125  		return
   126  	}
   127  	idOfCDocWorkspaceID := viewRec.AsRecordID(field_IDOfCDocWorkspaceID)
   128  	kb, err = args.State.KeyBuilder(state.Record, QNameCDocWorkspaceID)
   129  	if err != nil {
   130  		// notest
   131  		return err
   132  	}
   133  	kb.PutRecordID(state.Field_ID, idOfCDocWorkspaceID)
   134  	cdocWorkspaceID, err := args.State.MustExist(kb)
   135  	if err != nil {
   136  		// notest
   137  		return err
   138  	}
   139  
   140  	if !cdocWorkspaceID.AsBool(appdef.SystemField_IsActive) {
   141  		logger.Verbose("cdoc.sys.WorkspaceID is inactive already")
   142  		return nil
   143  	}
   144  
   145  	cdocWorkspaceIDUpdater, err := args.Intents.UpdateValue(kb, cdocWorkspaceID)
   146  	if err != nil {
   147  		// notest
   148  		return err
   149  	}
   150  	cdocWorkspaceIDUpdater.PutBool(appdef.SystemField_IsActive, false)
   151  	return nil
   152  }
   153  
   154  // ownerApp/ownerWSID
   155  func cmdOnChildWorkspaceDeactivatedExec(args istructs.ExecCommandArgs) (err error) {
   156  	ownerID := args.ArgumentObject.AsInt64(Field_OwnerID)
   157  	kb, err := args.State.KeyBuilder(state.Record, appdef.NullQName)
   158  	if err != nil {
   159  		// notest
   160  		return err
   161  	}
   162  	kb.PutRecordID(state.Field_ID, istructs.RecordID(ownerID))
   163  	cdocOwnerSV, err := args.State.MustExist(kb)
   164  	if err != nil {
   165  		// notest
   166  		return err
   167  	}
   168  	if !cdocOwnerSV.AsBool(appdef.SystemField_IsActive) {
   169  		return nil
   170  	}
   171  	cdocOwnerUpdater, err := args.Intents.UpdateValue(kb, cdocOwnerSV)
   172  	if err != nil {
   173  		// notest
   174  		return err
   175  	}
   176  	cdocOwnerUpdater.PutBool(appdef.SystemField_IsActive, false)
   177  	return nil
   178  }
   179  
   180  // target app, target WSID
   181  func projectorApplyDeactivateWorkspace(federation federation.IFederation, tokensAPI itokens.ITokens) func(event istructs.IPLogEvent, s istructs.IState, intents istructs.IIntents) (err error) {
   182  	return func(event istructs.IPLogEvent, s istructs.IState, intents istructs.IIntents) (err error) {
   183  		kb, err := s.KeyBuilder(state.Record, authnz.QNameCDocWorkspaceDescriptor)
   184  		if err != nil {
   185  			// notest
   186  			return err
   187  		}
   188  		kb.PutQName(state.Field_Singleton, authnz.QNameCDocWorkspaceDescriptor)
   189  		wsDesc, err := s.MustExist(kb)
   190  		if err != nil {
   191  			// notest
   192  			return err
   193  		}
   194  		ownerApp := wsDesc.AsString(Field_OwnerApp)
   195  		ownerWSID := wsDesc.AsInt64(Field_OwnerWSID)
   196  		ownerID := wsDesc.AsInt64(Field_OwnerID)
   197  
   198  		appQName := s.App()
   199  
   200  		sysToken, err := payloads.GetSystemPrincipalToken(tokensAPI, appQName)
   201  		if err != nil {
   202  			// notest
   203  			return err
   204  		}
   205  
   206  		// Foreach cdoc.sys.Subject
   207  		subjectsKB, err := s.KeyBuilder(state.View, collection.QNameCollectionView)
   208  		if err != nil {
   209  			// notest
   210  			return err
   211  		}
   212  		subjectsKB.PutInt32(collection.Field_PartKey, collection.PartitionKeyCollection)
   213  		subjectsKB.PutQName(collection.Field_DocQName, invite.QNameCDocSubject)
   214  		err = s.Read(subjectsKB, func(_ istructs.IKey, value istructs.IStateValue) (err error) {
   215  			subject := value.AsRecord(collection.Field_Record)
   216  			if istructs.SubjectKindType(subject.AsInt32(authnz.Field_SubjectKind)) != istructs.SubjectKind_User {
   217  				return nil
   218  			}
   219  			profileWSID := istructs.WSID(subject.AsInt64(invite.Field_ProfileWSID))
   220  
   221  			// app is always current
   222  			// impossible to have logins from different apps among subjects (Michael said)
   223  			url := fmt.Sprintf(`api/%s/%d/c.sys.OnJoinedWorkspaceDeactivated`, appQName, profileWSID)
   224  			body := fmt.Sprintf(`{"args":{"InvitedToWSID":%d}}`, event.Workspace())
   225  			_, err = federation.Func(url, body, coreutils.WithAuthorizeBy(sysToken), coreutils.WithDiscardResponse())
   226  			return err
   227  		})
   228  		if err != nil {
   229  			// notestdebt
   230  			return err
   231  		}
   232  
   233  		// currentApp/ApplicationWS/c.sys.OnWorkspaceDeactivated(OnwerWSID, WSName)
   234  		wsName := wsDesc.AsString(authnz.Field_WSName)
   235  		body := fmt.Sprintf(`{"args":{"OwnerWSID":%d, "WSName":"%s"}}`, ownerWSID, wsName)
   236  		cdocWorkspaceIDWSID := coreutils.GetPseudoWSID(istructs.WSID(ownerWSID), wsName, event.Workspace().ClusterID())
   237  		if _, err := federation.Func(fmt.Sprintf("api/%s/%d/c.sys.OnWorkspaceDeactivated", ownerApp, cdocWorkspaceIDWSID), body,
   238  			coreutils.WithDiscardResponse(), coreutils.WithAuthorizeBy(sysToken)); err != nil {
   239  			return fmt.Errorf("c.sys.OnWorkspaceDeactivated failed: %w", err)
   240  		}
   241  
   242  		// c.sys.OnChildWorkspaceDeactivated(ownerID))
   243  		body = fmt.Sprintf(`{"args":{"OwnerID":%d}}`, ownerID)
   244  		if _, err := federation.Func(fmt.Sprintf("api/%s/%d/c.sys.OnChildWorkspaceDeactivated", ownerApp, ownerWSID), body,
   245  			coreutils.WithDiscardResponse(), coreutils.WithAuthorizeBy(sysToken)); err != nil {
   246  			return fmt.Errorf("c.sys.OnChildWorkspaceDeactivated failed: %w", err)
   247  		}
   248  
   249  		// cdoc.sys.WorkspaceDescriptor.Status = Inactive
   250  		body = fmt.Sprintf(`{"cuds":[{"sys.ID":%d,"fields":{"Status":%d}}]}`, wsDesc.AsRecordID(appdef.SystemField_ID), authnz.WorkspaceStatus_Inactive)
   251  		if _, err := federation.Func(fmt.Sprintf("api/%s/%d/c.sys.CUD", appQName, event.Workspace()), body,
   252  			coreutils.WithDiscardResponse(), coreutils.WithAuthorizeBy(sysToken)); err != nil {
   253  			return fmt.Errorf("cdoc.sys.WorkspaceDescriptor.Status=Inactive failed: %w", err)
   254  		}
   255  
   256  		logger.Info("workspace", wsDesc.AsString(authnz.Field_WSName), "deactivated")
   257  		return nil
   258  	}
   259  }