github.com/josephbuchma/goa@v1.2.0/design/apidsl/type.go (about)

     1  package apidsl
     2  
     3  import (
     4  	"github.com/goadesign/goa/design"
     5  	"github.com/goadesign/goa/dslengine"
     6  )
     7  
     8  // Type implements the type definition dsl. A type definition describes a data structure consisting
     9  // of attributes. Each attribute has a type which can also refer to a type definition (or use a
    10  // primitive type or nested attibutes). The dsl syntax for define a type definition is the
    11  // Attribute dsl, see Attribute.
    12  //
    13  // On top of specifying any attribute type, type definitions can also be used to describe the data
    14  // structure of a request payload. They can also be used by media type definitions as reference, see
    15  // Reference. Here is an example:
    16  //
    17  //	Type("createPayload", func() {
    18  //		Description("Type of create and upload action payloads")
    19  //		Attribute("name", String, "name of bottle")
    20  //		Attribute("origin", Origin, "Details on wine origin")  // See Origin definition below
    21  //		Required("name")
    22  //	})
    23  //
    24  //	var Origin = Type("origin", func() {
    25  //		Description("Origin of bottle")
    26  //		Attribute("Country")
    27  //	})
    28  //
    29  // This function returns the newly defined type so the value can be used throughout the dsl.
    30  func Type(name string, dsl func()) *design.UserTypeDefinition {
    31  	if design.Design.Types == nil {
    32  		design.Design.Types = make(map[string]*design.UserTypeDefinition)
    33  	} else if _, ok := design.Design.Types[name]; ok {
    34  		dslengine.ReportError("type %#v defined twice", name)
    35  		return nil
    36  	}
    37  
    38  	if !dslengine.IsTopLevelDefinition() {
    39  		dslengine.IncompatibleDSL()
    40  		return nil
    41  	}
    42  
    43  	t := &design.UserTypeDefinition{
    44  		TypeName:            name,
    45  		AttributeDefinition: &design.AttributeDefinition{DSLFunc: dsl},
    46  	}
    47  	if dsl == nil {
    48  		t.Type = design.String
    49  	} else {
    50  		t.Type = make(design.Object)
    51  	}
    52  	design.Design.Types[name] = t
    53  	return t
    54  }
    55  
    56  // ArrayOf creates an array type from its element type. The result can be used anywhere a type can.
    57  // Examples:
    58  //
    59  //	var Bottle = Type("bottle", func() {
    60  //		Attribute("name")
    61  //	})
    62  //
    63  //	var Bottles = ArrayOf(Bottle)
    64  //
    65  //	Action("update", func() {
    66  //		Params(func() {
    67  //			Param("ids", ArrayOf(Integer))
    68  //		})
    69  //		Payload(ArrayOf(Bottle))  // Equivalent to Payload(Bottles)
    70  //	})
    71  //
    72  // ArrayOf accepts an optional DSL as second argument which allows providing validations for the
    73  // elements of the array:
    74  //
    75  //      var Names = ArrayOf(String, func() {
    76  //          Pattern("[a-zA-Z]+")
    77  //      })
    78  //
    79  // If you are looking to return a collection of elements in a Response clause, refer to
    80  // CollectionOf.  ArrayOf creates a type, where CollectionOf creates a media type.
    81  func ArrayOf(v interface{}, dsl ...func()) *design.Array {
    82  	var t design.DataType
    83  	var ok bool
    84  	t, ok = v.(design.DataType)
    85  	if !ok {
    86  		if name, ok := v.(string); ok {
    87  			if ut, ok := design.Design.Types[name]; ok {
    88  				t = ut
    89  			} else if mt, ok := design.Design.MediaTypes[name]; ok {
    90  				t = mt
    91  			}
    92  		}
    93  	}
    94  	// never return nil to avoid panics, errors are reported after DSL execution
    95  	res := &design.Array{ElemType: &design.AttributeDefinition{Type: design.String}}
    96  	if t == nil {
    97  		dslengine.ReportError("invalid ArrayOf argument: not a type and not a known user type name")
    98  		return res
    99  	}
   100  	if len(dsl) > 1 {
   101  		dslengine.ReportError("ArrayOf: too many arguments")
   102  		return res
   103  	}
   104  	at := design.AttributeDefinition{Type: t}
   105  	if len(dsl) == 1 {
   106  		dslengine.Execute(dsl[0], &at)
   107  	}
   108  	return &design.Array{ElemType: &at}
   109  }
   110  
   111  // HashOf creates a hash map from its key and element types. The result can be used anywhere a type
   112  // can. Examples:
   113  //
   114  //	var Bottle = Type("bottle", func() {
   115  //		Attribute("name")
   116  //	})
   117  //
   118  //	var RatedBottles = HashOf(String, Bottle)
   119  //
   120  //	Action("updateRatings", func() {
   121  //		Payload(func() {
   122  //			Member("ratings", HashOf(String, Integer))  // Artificial examples...
   123  //			Member("bottles", RatedBottles)
   124  //	})
   125  //
   126  // HashOf accepts optional DSLs as third and fourth argument which allows providing validations for
   127  // the keys and values of the hash respectively:
   128  //
   129  //	var RatedBottles = HashOf(String, Bottle, func() {
   130  //          Pattern("[a-zA-Z]+") // Validate bottle names
   131  //      })
   132  //
   133  //      func ValidateKey() {
   134  //          Pattern("^foo")
   135  //      }
   136  //
   137  //      func TypeValue() {
   138  //          Metadata("struct:field:type", "json.RawMessage", "encoding/json")
   139  //      }
   140  //
   141  //	var Mappings = HashOf(String, String, ValidateKey, TypeValue)
   142  //
   143  func HashOf(k, v design.DataType, dsls ...func()) *design.Hash {
   144  	kat := design.AttributeDefinition{Type: k}
   145  	vat := design.AttributeDefinition{Type: v}
   146  	if len(dsls) > 2 {
   147  		// never return nil to avoid panics, errors are reported after DSL execution
   148  		dslengine.ReportError("HashOf: too many arguments")
   149  		return &design.Hash{KeyType: &kat, ElemType: &vat}
   150  	}
   151  	if len(dsls) >= 1 {
   152  		dslengine.Execute(dsls[0], &kat)
   153  		if len(dsls) == 2 {
   154  			dslengine.Execute(dsls[1], &vat)
   155  		}
   156  	}
   157  	return &design.Hash{KeyType: &kat, ElemType: &vat}
   158  }