Skip to content

Commit 144843a

Browse files
hypnocejohanbrandhorst
authored andcommitted
Add read-only support to protoc-gen-swagger output (#882)
Can be set via: * explicit swagger option on the field * Implicit comment "Output only." on the field
1 parent 0c2b3b1 commit 144843a

File tree

5 files changed

+284
-132
lines changed

5 files changed

+284
-132
lines changed

protoc-gen-swagger/genswagger/template.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,7 @@ func renderMessagesAsDefinition(messages messageMap, d swaggerDefinitionsObject,
275275

276276
// Warning: Make sure not to overwrite any fields already set on the schema type.
277277
schema.ExternalDocs = protoSchema.ExternalDocs
278+
schema.ReadOnly = protoSchema.ReadOnly
278279
schema.MultipleOf = protoSchema.MultipleOf
279280
schema.Maximum = protoSchema.Maximum
280281
schema.ExclusiveMaximum = protoSchema.ExclusiveMaximum
@@ -1223,6 +1224,12 @@ func updateSwaggerDataFromComments(swaggerObject interface{}, comment string, is
12231224
// Figure out which properties to update.
12241225
summaryValue := infoObjectValue.FieldByName("Summary")
12251226
descriptionValue := infoObjectValue.FieldByName("Description")
1227+
readOnlyValue := infoObjectValue.FieldByName("ReadOnly")
1228+
1229+
if readOnlyValue.Kind() == reflect.Bool && readOnlyValue.CanSet() && strings.Contains(comment, "Output only.") {
1230+
readOnlyValue.Set(reflect.ValueOf(true))
1231+
}
1232+
12261233
usingTitle := false
12271234
if !summaryValue.CanSet() {
12281235
summaryValue = infoObjectValue.FieldByName("Title")
@@ -1539,6 +1546,7 @@ func protoJSONSchemaToSwaggerSchemaCore(j *swagger_options.JSONSchema, reg *desc
15391546
func updateSwaggerObjectFromJSONSchema(s *swaggerSchemaObject, j *swagger_options.JSONSchema) {
15401547
s.Title = j.GetTitle()
15411548
s.Description = j.GetDescription()
1549+
s.ReadOnly = j.GetReadOnly()
15421550
s.MultipleOf = j.GetMultipleOf()
15431551
s.Maximum = j.GetMaximum()
15441552
s.ExclusiveMaximum = j.GetExclusiveMaximum()

protoc-gen-swagger/genswagger/template_test.go

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package genswagger
22

33
import (
44
"encoding/json"
5+
"errors"
56
"fmt"
67
"reflect"
78
"testing"
@@ -1274,6 +1275,7 @@ func TestRenderMessagesAsDefinition(t *testing.T) {
12741275
MaxProperties: 33,
12751276
MinProperties: 22,
12761277
Required: []string{"req"},
1278+
ReadOnly: true,
12771279
},
12781280
},
12791281
},
@@ -1298,6 +1300,7 @@ func TestRenderMessagesAsDefinition(t *testing.T) {
12981300
MaxProperties: 33,
12991301
MinProperties: 22,
13001302
Required: []string{"req"},
1303+
ReadOnly: true,
13011304
},
13021305
},
13031306
},
@@ -1498,3 +1501,135 @@ func TestProtoComments(t *testing.T) {
14981501
}
14991502
}
15001503
}
1504+
1505+
func TestUpdateSwaggerDataFromComments(t *testing.T) {
1506+
1507+
tests := []struct {
1508+
descr string
1509+
swaggerObject interface{}
1510+
comments string
1511+
expectedError error
1512+
expectedSwaggerObject interface{}
1513+
}{
1514+
{
1515+
descr: "empty comments",
1516+
swaggerObject: nil,
1517+
expectedSwaggerObject: nil,
1518+
comments: "",
1519+
expectedError: nil,
1520+
},
1521+
{
1522+
descr: "set field to read only",
1523+
swaggerObject: &swaggerSchemaObject{},
1524+
expectedSwaggerObject: &swaggerSchemaObject{
1525+
ReadOnly: true,
1526+
Description: "... Output only. ...",
1527+
},
1528+
comments: "... Output only. ...",
1529+
expectedError: nil,
1530+
},
1531+
{
1532+
descr: "set title",
1533+
swaggerObject: &swaggerSchemaObject{},
1534+
expectedSwaggerObject: &swaggerSchemaObject{
1535+
Title: "Comment with no trailing dot",
1536+
},
1537+
comments: "Comment with no trailing dot",
1538+
expectedError: nil,
1539+
},
1540+
{
1541+
descr: "set description",
1542+
swaggerObject: &swaggerSchemaObject{},
1543+
expectedSwaggerObject: &swaggerSchemaObject{
1544+
Description: "Comment with trailing dot.",
1545+
},
1546+
comments: "Comment with trailing dot.",
1547+
expectedError: nil,
1548+
},
1549+
{
1550+
descr: "use info object",
1551+
swaggerObject: &swaggerObject{
1552+
Info: swaggerInfoObject{
1553+
},
1554+
},
1555+
expectedSwaggerObject: &swaggerObject{
1556+
Info: swaggerInfoObject{
1557+
Description: "Comment with trailing dot.",
1558+
},
1559+
},
1560+
comments: "Comment with trailing dot.",
1561+
expectedError: nil,
1562+
},
1563+
{
1564+
descr: "multi line comment with title",
1565+
swaggerObject: &swaggerSchemaObject{},
1566+
expectedSwaggerObject: &swaggerSchemaObject {
1567+
Title: "First line",
1568+
Description: "Second line",
1569+
},
1570+
comments: "First line\n\nSecond line",
1571+
expectedError: nil,
1572+
},
1573+
{
1574+
descr: "multi line comment no title",
1575+
swaggerObject: &swaggerSchemaObject{},
1576+
expectedSwaggerObject: &swaggerSchemaObject {
1577+
Description: "First line.\n\nSecond line",
1578+
},
1579+
comments: "First line.\n\nSecond line",
1580+
expectedError: nil,
1581+
},
1582+
{
1583+
descr: "multi line comment with summary with dot",
1584+
swaggerObject: &swaggerOperationObject{},
1585+
expectedSwaggerObject: &swaggerOperationObject {
1586+
Summary: "First line.",
1587+
Description: "Second line",
1588+
},
1589+
comments: "First line.\n\nSecond line",
1590+
expectedError: nil,
1591+
},
1592+
{
1593+
descr: "multi line comment with summary no dot",
1594+
swaggerObject: &swaggerOperationObject{},
1595+
expectedSwaggerObject: &swaggerOperationObject {
1596+
Summary: "First line",
1597+
Description: "Second line",
1598+
},
1599+
comments: "First line\n\nSecond line",
1600+
expectedError: nil,
1601+
},
1602+
{
1603+
descr: "multi line comment with summary no dot",
1604+
swaggerObject: &schemaCore{},
1605+
expectedSwaggerObject: &schemaCore{},
1606+
comments: "Any comment",
1607+
expectedError: errors.New("no description nor summary property"),
1608+
},
1609+
}
1610+
1611+
for _, test := range tests {
1612+
t.Run(test.descr, func(t *testing.T) {
1613+
err := updateSwaggerDataFromComments(test.swaggerObject, test.comments, false)
1614+
1615+
if test.expectedError == nil {
1616+
if err != nil {
1617+
t.Errorf("unexpected error '%v'", err)
1618+
}
1619+
if !reflect.DeepEqual(test.swaggerObject, test.expectedSwaggerObject) {
1620+
t.Errorf("swaggerObject was not updated corretly, expected '%+v', got '%+v'", test.expectedSwaggerObject, test.swaggerObject)
1621+
}
1622+
} else {
1623+
if err == nil {
1624+
t.Error("expected update error not returned")
1625+
}
1626+
if !reflect.DeepEqual(test.swaggerObject, test.expectedSwaggerObject) {
1627+
t.Errorf("swaggerObject was not updated corretly, expected '%+v', got '%+v'", test.expectedSwaggerObject, test.swaggerObject)
1628+
}
1629+
if err.Error() != test.expectedError.Error() {
1630+
t.Errorf("expected error malformed, expected %q, got %q", test.expectedError.Error(), err.Error())
1631+
}
1632+
}
1633+
})
1634+
}
1635+
}

protoc-gen-swagger/genswagger/types.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,7 @@ type swaggerSchemaObject struct {
209209

210210
ExternalDocs *swaggerExternalDocumentationObject `json:"externalDocs,omitempty"`
211211

212+
ReadOnly bool `json:"readOnly,omitempty"`
212213
MultipleOf float64 `json:"multipleOf,omitempty"`
213214
Maximum float64 `json:"maximum,omitempty"`
214215
ExclusiveMaximum bool `json:"exclusiveMaximum,omitempty"`

0 commit comments

Comments
 (0)