github.com/authzed/spicedb@v1.32.1-0.20240520085336-ebda56537386/pkg/development/wasm/request.go (about)

     1  //go:build wasm
     2  // +build wasm
     3  
     4  package main
     5  
     6  import (
     7  	"context"
     8  	"fmt"
     9  	"syscall/js"
    10  
    11  	"github.com/authzed/spicedb/pkg/development"
    12  
    13  	devinterface "github.com/authzed/spicedb/pkg/proto/developer/v1"
    14  	"google.golang.org/protobuf/encoding/protojson"
    15  )
    16  
    17  // runDeveloperRequest is the function exported into the WASM environment for invoking
    18  // one or more development operations.
    19  //
    20  // The arguments are:
    21  //
    22  //  1. Message in the form of a DeveloperRequest containing the context and the operation(s) to
    23  //     run, in JSON form.
    24  //
    25  // The function returns:
    26  //
    27  //		A single JSON-encoded string of messages of type DeveloperResponse representing the errors
    28  //	 encountered (if any), or the responses for each operation.
    29  //
    30  // See example/wasm.html for an example.
    31  func runDeveloperRequest(this js.Value, args []js.Value) any {
    32  	if len(args) != 1 {
    33  		return respErr(fmt.Errorf("invalid number of arguments specified"))
    34  	}
    35  
    36  	// Unmarshal the developer request.
    37  	encoded := args[0].String()
    38  	devRequest := &devinterface.DeveloperRequest{}
    39  	err := protojson.Unmarshal([]byte(encoded), devRequest)
    40  	if err != nil {
    41  		return respErr(fmt.Errorf("could not decode developer request: %w", err))
    42  	}
    43  
    44  	if devRequest.Context == nil {
    45  		return respErr(fmt.Errorf("missing required context"))
    46  	}
    47  
    48  	// Construct the developer context.
    49  	devContext, devErrors, err := development.NewDevContext(context.Background(), devRequest.Context)
    50  	if err != nil {
    51  		return respErr(err)
    52  	}
    53  
    54  	if devErrors != nil && len(devErrors.InputErrors) > 0 {
    55  		return respInputErr(devErrors.InputErrors)
    56  	}
    57  
    58  	// Run operations.
    59  	results := make(map[uint64]*devinterface.OperationResult, len(devRequest.Operations))
    60  	for index, op := range devRequest.Operations {
    61  		result, err := runOperation(devContext, op)
    62  		if err != nil {
    63  			return respErr(err)
    64  		}
    65  
    66  		results[uint64(index)] = result
    67  	}
    68  
    69  	return encode(&devinterface.DeveloperResponse{
    70  		OperationsResults: &devinterface.OperationsResults{
    71  			Results: results,
    72  		},
    73  	})
    74  }
    75  
    76  func encode(response *devinterface.DeveloperResponse) js.Value {
    77  	encoded, err := protojson.Marshal(response)
    78  	if err != nil {
    79  		panic(err)
    80  	}
    81  
    82  	return js.ValueOf(string(encoded))
    83  }
    84  
    85  func respErr(err error) js.Value {
    86  	return encode(&devinterface.DeveloperResponse{
    87  		InternalError: err.Error(),
    88  	})
    89  }
    90  
    91  func respInputErr(inputErrors []*devinterface.DeveloperError) js.Value {
    92  	return encode(&devinterface.DeveloperResponse{
    93  		DeveloperErrors: &devinterface.DeveloperErrors{
    94  			InputErrors: inputErrors,
    95  		},
    96  	})
    97  }