github.com/vcilabs/webrpc@v0.5.2-0.20201116131534-162e27b1b33b/schema/service.go (about)

     1  package schema
     2  
     3  import (
     4  	"strings"
     5  
     6  	"github.com/pkg/errors"
     7  )
     8  
     9  type Service struct {
    10  	Name    VarName   `json:"name"`
    11  	Methods []*Method `json:"methods"`
    12  
    13  	Schema *WebRPCSchema `json:"-"` // denormalize/back-reference
    14  }
    15  
    16  type Method struct {
    17  	Name VarName `json:"name"`
    18  
    19  	StreamInput  bool `json:"streamInput,omitempty"`
    20  	StreamOutput bool `json:"streamOutput,omitempty"`
    21  	Proxy        bool `json:"-"` // TODO: actual implementation
    22  
    23  	Inputs  []*MethodArgument `json:"inputs"`
    24  	Outputs []*MethodArgument `json:"outputs"`
    25  
    26  	Service *Service `json:"-"` // denormalize/back-reference
    27  }
    28  
    29  type MethodArgument struct {
    30  	Name     VarName  `json:"name"`
    31  	Type     *VarType `json:"type"`
    32  	Optional bool     `json:"optional"`
    33  
    34  	InputArg  bool `json:"-"` // denormalize/back-reference
    35  	OutputArg bool `json:"-"` // denormalize/back-reference
    36  }
    37  
    38  func (s *Service) Parse(schema *WebRPCSchema) error {
    39  	s.Schema = schema // back-ref
    40  
    41  	// Service name
    42  	serviceName := string(s.Name)
    43  	if string(s.Name) == "" {
    44  		return errors.Errorf("schema error: service name cannot be empty")
    45  	}
    46  
    47  	// Ensure we don't have dupe service names (w/ normalization)
    48  	name := strings.ToLower(string(s.Name))
    49  	for _, svc := range schema.Services {
    50  		if svc != s && name == strings.ToLower(string(svc.Name)) {
    51  			return errors.Errorf("schema error: duplicate service name detected in service '%s'", serviceName)
    52  		}
    53  	}
    54  
    55  	// Ensure methods is defined
    56  	if len(s.Methods) == 0 {
    57  		return errors.Errorf("schema error: methods cannot be empty for service '%s'", serviceName)
    58  	}
    59  
    60  	// Verify method names and ensure we don't have any duplicate method names
    61  	methodList := map[string]string{}
    62  	for _, method := range s.Methods {
    63  		if string(method.Name) == "" {
    64  			return errors.Errorf("schema error: detected empty method name in service '%s", serviceName)
    65  		}
    66  
    67  		methodName := string(method.Name)
    68  		nMethodName := strings.ToLower(methodName)
    69  
    70  		if _, ok := methodList[nMethodName]; ok {
    71  			return errors.Errorf("schema error: detected duplicate method name of '%s' in service '%s'", methodName, serviceName)
    72  		}
    73  		methodList[nMethodName] = methodName
    74  	}
    75  
    76  	// Parse+validate methods
    77  	for _, method := range s.Methods {
    78  		err := method.Parse(schema, s)
    79  		if err != nil {
    80  			return err
    81  		}
    82  	}
    83  
    84  	return nil
    85  }
    86  
    87  func (m *Method) Parse(schema *WebRPCSchema, service *Service) error {
    88  	if service == nil {
    89  		return errors.Errorf("parse error, service arg cannot be nil")
    90  	}
    91  	m.Service = service // back-ref
    92  	serviceName := string(service.Name)
    93  
    94  	// Parse+validate inputs
    95  	for _, input := range m.Inputs {
    96  		input.InputArg = true // back-ref
    97  		if input.Name == "" {
    98  			return errors.Errorf("schema error: detected empty input argument name for method '%s' in service '%s'", m.Name, serviceName)
    99  		}
   100  		err := input.Type.Parse(schema)
   101  		if err != nil {
   102  			return err
   103  		}
   104  	}
   105  
   106  	// Parse+validate outputs
   107  	for _, output := range m.Outputs {
   108  		output.OutputArg = true // back-ref
   109  		if output.Name == "" {
   110  			return errors.Errorf("schema error: detected empty output name for method '%s' in service '%s'", m.Name, serviceName)
   111  		}
   112  		err := output.Type.Parse(schema)
   113  		if err != nil {
   114  			return err
   115  		}
   116  	}
   117  
   118  	// Note, we allow zero inputs and zero outputs
   119  
   120  	return nil
   121  }