Skip to content

Commit

Permalink
Merge branch 'master' into transforms
Browse files Browse the repository at this point in the history
  • Loading branch information
stevededalus committed Jun 1, 2017
2 parents eff195d + a26a478 commit ce54f34
Show file tree
Hide file tree
Showing 69 changed files with 3,436 additions and 967 deletions.
14 changes: 8 additions & 6 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,13 @@ EXAMPLE_SERVICES = $(sort $(dir $(wildcard $(EXAMPLE_SERVICES_DIR)*/)))

.PHONY: check-licence
check-licence:
ls ./node_modules/.bin/uber-licence >/dev/null 2>&1 || npm i uber-licence
./node_modules/.bin/uber-licence --dry --file '*.go' --dir '!vendor' --dir '!examples' --dir '!.tmp_gen' --dir '!template_bundle'
@echo "Checking uber-licence..."
@ls ./node_modules/.bin/uber-licence >/dev/null 2>&1 || npm i uber-licence
@./node_modules/.bin/uber-licence --dry --file '*.go' --dir '!vendor' --dir '!examples' --dir '!.tmp_gen' --dir '!template_bundle'

.PHONY: fix-licence
fix-licence:
ls ./node_modules/.bin/uber-licence >/dev/null 2>&1 || npm i uber-licence
@ls ./node_modules/.bin/uber-licence >/dev/null 2>&1 || npm i uber-licence
./node_modules/.bin/uber-licence --file '*.go' --dir '!vendor' --dir '!examples' --dir '!.tmp_gen' --dir '!template_bundle'

.PHONY: install
Expand All @@ -39,12 +40,13 @@ install:

.PHONY: eclint-check
eclint-check:
ls ./node_modules/.bin/eclint >/dev/null 2>&1 || npm i eclint@v1.1.5
./node_modules/.bin/eclint check "./{codegen,examples}/**/*.{json,tmpl}"
@echo "Checking eclint..."
@ls ./node_modules/.bin/eclint >/dev/null 2>&1 || npm i eclint@v1.1.5
@./node_modules/.bin/eclint check "./{codegen,examples}/**/*.{json,tmpl}"

.PHONY: eclint-fix
eclint-fix:
ls ./node_modules/.bin/eclint >/dev/null 2>&1 || npm i eclint@v1.1.5
@ls ./node_modules/.bin/eclint >/dev/null 2>&1 || npm i eclint@v1.1.5
./node_modules/.bin/eclint fix "./{codegen,examples}/**/*.{json,tmpl}"

.PHONY: lint
Expand Down
113 changes: 113 additions & 0 deletions codegen/casing.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
// Copyright (c) 2017 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package codegen

import (
"bytes"
"regexp"
)

// commonInitialisms, taken from
// https://github.com/golang/lint/blob/206c0f020eba0f7fbcfbc467a5eb808037df2ed6/lint.go#L731
var commonInitialisms = map[string]bool{
"ACL": true,
"API": true,
"ASCII": true,
"CPU": true,
"CSS": true,
"DNS": true,
"EOF": true,
"GUID": true,
"HTML": true,
"HTTP": true,
"HTTPS": true,
"ID": true,
"IP": true,
"JSON": true,
"LHS": true,
"OS": true,
"QPS": true,
"RAM": true,
"RHS": true,
"RPC": true,
"SLA": true,
"SMTP": true,
"SQL": true,
"SSH": true,
"TCP": true,
"TLS": true,
"TTL": true,
"UDP": true,
"UI": true,
"UID": true,
"UUID": true,
"URI": true,
"URL": true,
"UTF8": true,
"VM": true,
"XML": true,
"XMPP": true,
"XSRF": true,
"XSS": true,
}
var camelingRegex = regexp.MustCompile("[0-9A-Za-z]+")

// startsWithInitialism returns the initialism if the given string begins with it
func startsWithInitialism(s string) string {
var initialism string
// the longest initialism is 5 char, the shortest 2
for i := 1; i <= 5; i++ {
if len(s) > i-1 && commonInitialisms[s[:i]] {
initialism = s[:i]
}
}
return initialism
}

func camelCase(src string) string {
byteSrc := []byte(src)
chunks := camelingRegex.FindAll(byteSrc, -1)
for idx, val := range chunks {
if idx > 0 {
chunks[idx] = ensureGolangAncronymCasing(bytes.Title(val))
} else {
chunks[idx][0] = bytes.ToLower(val[0:1])[0]
}
}
return string(bytes.Join(chunks, nil))
}

func ensureGolangAncronymCasing(segment []byte) []byte {
if upper := bytes.ToUpper(segment); commonInitialisms[string(upper)] {
return upper
}

return segment
}

func pascalCase(src string) string {
byteSrc := []byte(src)
chunks := camelingRegex.FindAll(byteSrc, -1)
for idx, val := range chunks {
chunks[idx] = ensureGolangAncronymCasing(bytes.Title(val))
}
return string(bytes.Join(chunks, nil))
}
176 changes: 140 additions & 36 deletions codegen/method.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,13 @@ type ExceptionSpec struct {
StatusCode StatusCode
}

// HeaderFieldInfo contains information about where to store
// the string from headers into the request/response body.
type HeaderFieldInfo struct {
FieldIdentifier string
IsPointer bool
}

// MethodSpec specifies all needed parts to generate code for a method in service.
type MethodSpec struct {
Name string
Expand All @@ -50,6 +57,17 @@ type MethodSpec struct {
EndpointName string
HTTPPath string
PathSegments []PathSegment

// ReqHeaderFields is a map of "header name" to
// a golang field accessor expression like ".Foo.Bar"
// Use to place request headers in the body
ReqHeaderFields map[string]HeaderFieldInfo

// ResHeaderFields is a map of header name to a golang
// field accessor expression used to read fields out
// of the response body and place them into response headers
ResHeaderFields map[string]HeaderFieldInfo

// ReqHeaders needed, generated from "zanzibar.http.reqHeaders"
ReqHeaders []string
// ResHeaders needed, generated from "zanzibar.http.resHeaders"
Expand Down Expand Up @@ -180,6 +198,9 @@ func NewMethod(
}
method.setHTTPPath(httpPath, funcSpec)

method.setRequestHeaderFields(funcSpec)
method.setResponseHeaderFields(funcSpec)

return method, nil
}

Expand All @@ -195,9 +216,7 @@ func (ms *MethodSpec) setRequestType(curThriftFile string, funcSpec *compile.Fun
var err error
if isRequestBoxed(funcSpec) {
ms.RequestBoxed = true
ms.RequestType, err = packageHelper.TypeFullName(
curThriftFile, funcSpec.ArgsSpec[0].Type,
)
ms.RequestType, err = packageHelper.TypeFullName(funcSpec.ArgsSpec[0].Type)
if err == nil && isStructType(funcSpec.ArgsSpec[0].Type) {
ms.RequestType = "*" + ms.RequestType
}
Expand Down Expand Up @@ -227,7 +246,7 @@ func (ms *MethodSpec) setResponseType(curThriftFile string, respSpec *compile.Re
ms.ResponseType = ""
return nil
}
typeName, err := packageHelper.TypeFullName(curThriftFile, respSpec.ReturnType)
typeName, err := packageHelper.TypeFullName(respSpec.ReturnType)
if isStructType(respSpec.ReturnType) {
typeName = "*" + typeName
}
Expand Down Expand Up @@ -279,7 +298,7 @@ func (ms *MethodSpec) setExceptions(
)

for i, e := range resultSpec.Exceptions {
typeName, err := h.TypeFullName(curThriftFile, e.Type)
typeName, err := h.TypeFullName(e.Type)
if err != nil {
return errors.Wrapf(
err,
Expand Down Expand Up @@ -337,45 +356,77 @@ func (ms *MethodSpec) setExceptions(
}

func findParamsAnnotation(
fields compile.FieldGroup, paramName string, prefix string,
fields compile.FieldGroup, paramName string,
) (string, bool) {
for i := 0; i < len(fields); i++ {
field := fields[i]

var identifier string
visitor := func(prefix string, field *compile.FieldSpec) bool {
if param, ok := field.Annotations[antHTTPRef]; ok {
if param == "params."+paramName[1:] {
return prefix + field.Name, true
identifier = prefix + "." + strings.Title(field.Name)
return true
}
}

switch t := field.Type.(type) {
case *compile.BinarySpec:
case *compile.StringSpec:
case *compile.BoolSpec:
case *compile.DoubleSpec:
case *compile.I8Spec:
case *compile.I16Spec:
case *compile.I32Spec:
case *compile.I64Spec:
case *compile.StructSpec:
path, ok := findParamsAnnotation(
t.Fields, paramName, field.Name+".",
)
if ok {
return path, true
return false
}
walkFieldGroups(fields, visitor)

if identifier == "" {
return "", false
}

return identifier, true
}

func (ms *MethodSpec) setRequestHeaderFields(
funcSpec *compile.FunctionSpec,
) {
fields := compile.FieldGroup(funcSpec.ArgsSpec)
ms.ReqHeaderFields = map[string]HeaderFieldInfo{}

// Scan for all annotations
visitor := func(prefix string, field *compile.FieldSpec) bool {
if param, ok := field.Annotations[antHTTPRef]; ok {
if param[0:8] == "headers." {
headerName := param[8:]
ms.ReqHeaderFields[headerName] = HeaderFieldInfo{
FieldIdentifier: prefix + "." + strings.Title(field.Name),
IsPointer: !field.Required,
}
}
case *compile.SetSpec:
// TODO: implement
case *compile.MapSpec:
// TODO: implement
case *compile.ListSpec:
// TODO: implement
default:
panic("unknown Spec")
}
return false
}
walkFieldGroups(fields, visitor)
}

func (ms *MethodSpec) setResponseHeaderFields(
funcSpec *compile.FunctionSpec,
) {
structType, ok := funcSpec.ResultSpec.ReturnType.(*compile.StructSpec)
// If the result is not a struct then there are zero response header
// annotations.
if !ok {
return
}

return "", false
fields := structType.Fields
ms.ResHeaderFields = map[string]HeaderFieldInfo{}

// Scan for all annotations
visitor := func(prefix string, field *compile.FieldSpec) bool {
if param, ok := field.Annotations[antHTTPRef]; ok {
if param[0:8] == "headers." {
headerName := param[8:]
ms.ResHeaderFields[headerName] = HeaderFieldInfo{
FieldIdentifier: prefix + "." + strings.Title(field.Name),
IsPointer: !field.Required,
}
}
}
return false
}
walkFieldGroups(fields, visitor)
}

func (ms *MethodSpec) setHTTPPath(httpPath string, funcSpec *compile.FunctionSpec) {
Expand All @@ -398,11 +449,11 @@ func (ms *MethodSpec) setHTTPPath(httpPath string, funcSpec *compile.FunctionSpe
// Boxed requests mean first arg is struct
structType := funcSpec.ArgsSpec[0].Type.(*compile.StructSpec)
fieldSelect, ok = findParamsAnnotation(
structType.Fields, segment, "",
structType.Fields, segment,
)
} else {
fieldSelect, ok = findParamsAnnotation(
compile.FieldGroup(funcSpec.ArgsSpec), segment, "",
compile.FieldGroup(funcSpec.ArgsSpec), segment,
)
}

Expand Down Expand Up @@ -515,3 +566,56 @@ func headers(annotation string) []string {
}
return strings.Split(annotation, ",")
}

func walkFieldGroups(
fields compile.FieldGroup,
visitField func(string, *compile.FieldSpec) bool,
) bool {
return walkFieldGroupsInternal("", fields, visitField)
}

func walkFieldGroupsInternal(
prefix string, fields compile.FieldGroup,
visitField func(string, *compile.FieldSpec) bool,
) bool {
for i := 0; i < len(fields); i++ {
field := fields[i]

bail := visitField(prefix, field)
if bail {
return true
}

realType := compile.RootTypeSpec(field.Type)
switch t := realType.(type) {
case *compile.BinarySpec:
case *compile.StringSpec:
case *compile.BoolSpec:
case *compile.DoubleSpec:
case *compile.I8Spec:
case *compile.I16Spec:
case *compile.I32Spec:
case *compile.I64Spec:
case *compile.EnumSpec:
case *compile.StructSpec:
bail := walkFieldGroupsInternal(
prefix+"."+strings.Title(field.Name),
t.Fields,
visitField,
)
if bail {
return true
}
case *compile.SetSpec:
// TODO: implement
case *compile.MapSpec:
// TODO: implement
case *compile.ListSpec:
// TODO: implement
default:
panic("unknown Spec")
}
}

return false
}

0 comments on commit ce54f34

Please sign in to comment.