github.com/voedger/voedger@v0.0.0-20240520144910-273e84102129/pkg/state/impl_federation_cmd_storage.go (about)

     1  /*
     2   * Copyright (c) 2022-present unTill Pro, Ltd.
     3   */
     4  
     5  package state
     6  
     7  import (
     8  	"encoding/json"
     9  	"fmt"
    10  	"strconv"
    11  	"strings"
    12  
    13  	"github.com/voedger/voedger/pkg/appdef"
    14  	"github.com/voedger/voedger/pkg/istructs"
    15  	"github.com/voedger/voedger/pkg/itokens"
    16  	payloads "github.com/voedger/voedger/pkg/itokens-payloads"
    17  	coreutils "github.com/voedger/voedger/pkg/utils"
    18  	"github.com/voedger/voedger/pkg/utils/federation"
    19  )
    20  
    21  const (
    22  	ContentType = "Content-Type"
    23  )
    24  
    25  type FederationCommandHandler = func(owner, appname string, wsid istructs.WSID, command appdef.QName, body string) (statusCode int, newIDs map[string]int64, result string, err error)
    26  
    27  type federationCommandStorage struct {
    28  	appStructs AppStructsFunc
    29  	wsid       WSIDFunc
    30  	federation federation.IFederation
    31  	tokens     itokens.ITokens
    32  	emulation  FederationCommandHandler
    33  }
    34  
    35  func (s *federationCommandStorage) NewKeyBuilder(appdef.QName, istructs.IStateKeyBuilder) istructs.IStateKeyBuilder {
    36  	return newKeyBuilder(FederationCommand, appdef.NullQName)
    37  }
    38  func (s *federationCommandStorage) Get(key istructs.IStateKeyBuilder) (istructs.IStateValue, error) {
    39  	appqname := s.appStructs().AppQName()
    40  	var owner string
    41  	var appname string
    42  	var wsid istructs.WSID
    43  	var command appdef.QName
    44  	var body string
    45  	opts := make([]coreutils.ReqOptFunc, 0)
    46  
    47  	kb := key.(*keyBuilder)
    48  
    49  	if v, ok := kb.data[Field_ExpectedCodes]; ok {
    50  		for _, ec := range strings.Split(v.(string), ",") {
    51  			code, err := strconv.Atoi(ec)
    52  			if err != nil {
    53  				return nil, err
    54  			}
    55  			opts = append(opts, coreutils.WithExpectedCode(code))
    56  		}
    57  	}
    58  
    59  	if v, ok := kb.data[Field_Owner]; ok {
    60  		owner = v.(string)
    61  	} else {
    62  		owner = appqname.Owner()
    63  	}
    64  
    65  	if v, ok := kb.data[Field_AppName]; ok {
    66  		appname = v.(string)
    67  	} else {
    68  		appname = appqname.Name()
    69  	}
    70  
    71  	if v, ok := kb.data[Field_WSID]; ok {
    72  		wsid = v.(istructs.WSID)
    73  	} else {
    74  		wsid = s.wsid()
    75  	}
    76  
    77  	if v, ok := kb.data[Field_Command]; ok {
    78  		command = v.(appdef.QName)
    79  	} else {
    80  		return nil, errCommandNotSpecified
    81  	}
    82  
    83  	if v, ok := kb.data[Field_Body]; ok {
    84  		body = v.(string)
    85  	}
    86  	if v, ok := kb.data[Field_Token]; ok {
    87  		opts = append(opts, coreutils.WithAuthorizeBy(v.(string)))
    88  	} else {
    89  		appQName := istructs.NewAppQName(owner, appname)
    90  		systemPrincipalToken, err := payloads.GetSystemPrincipalToken(s.tokens, appQName)
    91  		if err != nil {
    92  			return nil, err
    93  		}
    94  		opts = append(opts, coreutils.WithAuthorizeBy(systemPrincipalToken))
    95  	}
    96  
    97  	appOwnerAndName := owner + istructs.AppQNameQualifierChar + appname
    98  
    99  	relativeUrl := fmt.Sprintf("api/%s/%d/%s", appOwnerAndName, wsid, command)
   100  
   101  	var resStatus int
   102  	var resBody string
   103  	var newIDs map[string]int64
   104  	var err error
   105  
   106  	if s.emulation != nil {
   107  		resStatus, newIDs, resBody, err = s.emulation(owner, appname, wsid, command, body)
   108  		if err != nil {
   109  			return nil, err
   110  		}
   111  	} else {
   112  		resp, err := s.federation.Func(relativeUrl, body, opts...)
   113  		if err != nil {
   114  			return nil, err
   115  		}
   116  		resBody = resp.Body
   117  		newIDs = resp.NewIDs
   118  		resStatus = resp.HTTPResp.StatusCode
   119  	}
   120  
   121  	result := map[string]interface{}{}
   122  	err = json.Unmarshal([]byte(resBody), &result)
   123  	if err != nil {
   124  		return nil, err
   125  	}
   126  
   127  	return &fcCmdValue{
   128  		statusCode: resStatus,
   129  		newIds:     &fcCmdNewIds{newIds: newIDs},
   130  		result:     &jsonValue{json: result},
   131  	}, nil
   132  }
   133  func (s *federationCommandStorage) Read(key istructs.IStateKeyBuilder, callback istructs.ValueCallback) (err error) {
   134  	v, err := s.Get(key)
   135  	if err != nil {
   136  		return err
   137  	}
   138  	return callback(nil, v)
   139  }
   140  
   141  type fcCmdValue struct {
   142  	baseStateValue
   143  	statusCode int
   144  	newIds     istructs.IStateValue
   145  	result     istructs.IStateValue
   146  }
   147  
   148  func (v *fcCmdValue) AsInt32(name string) int32 {
   149  	if name == Field_StatusCode {
   150  		return int32(v.statusCode)
   151  	}
   152  	panic(errUndefined(name))
   153  }
   154  
   155  func (v *fcCmdValue) AsValue(name string) istructs.IStateValue {
   156  	if name == Field_NewIDs {
   157  		return v.newIds
   158  	}
   159  	if name == Field_Result {
   160  		return v.result
   161  	}
   162  	panic(errUndefined(name))
   163  }
   164  
   165  type fcCmdNewIds struct {
   166  	baseStateValue
   167  	newIds map[string]int64
   168  }
   169  
   170  func (v *fcCmdNewIds) AsInt64(name string) int64 {
   171  	if id, ok := v.newIds[name]; ok {
   172  		return id
   173  	}
   174  	panic(errUndefined(name))
   175  }