Services

Anatomy of a service spec #

A service spec consists of 2 main sections (services and __proto) and some properties on the root node.

name #

The type of the service without the package name. Follow the style guide and write it in CamelCase with an initial capital.

version #

Put any version information of the service. This field does not affect the URL of the services/methods.

This property has just informative character at the moment.

description #

Describe the intention of your service in some sentences. You can ommit this field. Furo will add a default description in the generates “developer was to lazy to give a description”.

lifecycle #

Lifecycle information for the service. If you set to true, put in a info with an alternative solution and maybe a deadline.

1
2
3
lifecycle:
    deprecated: false
    info: This version is still valid

__proto #

The proto section defines some properties to generate the proto files.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
__proto:
  package: fruit
  targetfile: fruitservice.proto
  imports:
    - google/api/annotations.proto
    - fruit/reqmsgs.proto
    - google/protobuf/empty.proto
    - fruit/fruit.proto
    - google/protobuf/field_mask.proto
  options:
    go_package: github.com/veith/doit-specs/dist/pb/fruit;fruitpb
    java_multiple_files: "true"
    java_outer_classname: FruitserviceProto
    java_package: com.furo.basefruit

Field package string #

This is the desired proto package name, his field is not optional

Field imports []string #

The imports are checked by Furo (missing imports will be added). Additional imports will not be removed (maybe you have to do a import for side effects).

This can be done with the command furo checkImports and is done in some other commands too. Imports that can not be found would be reported. So you can check for typos like fruit.FruitCollections which should be a fruit.FruitCollection.

1
2
3

specs/Fruitservice.service.spec :Import fruit.FruitCollections not found in Service FruitService on param ListFruitService

field options map<string,string> #

When needed, you can add options for your protos. This can be something like the following:

1
2
3
4
5
  options:
    go_package: github.com/veith/doit-specs/dist/pb/fruit;fruitpb
    java_multiple_files: "true"
    java_outer_classname: FruitserviceProto
    java_package: com.furo.basefruit

services map<string, service> #

The most important part of a service spec are the services, what a surprise. The service section contains a map with services. The key is the “name” of the service and usualy something like List, Get, Delete, Create, Update, CustomName. This names are not the rpc_name.

A service itself has the properties description, data, deeplink, query and rpc_name.

a single service in detail

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
  List:
    description: List fruits with pagination.
    data:
      request: google.protobuf.Empty
      response: fruit.FruitCollection
      bodyfield: body
    deeplink:
      description: 'List: GET /fruits google.protobuf.Empty , fruit.FruitCollection #List fruits with pagination.'
      href: /fruits
      method: GET
      rel: list
    query:
      q:
        description: Use this to search for a fruit.
        type: string
      filter:
        description: Use this field to filter the fruits, this is not searching.
        type: string
      order_by:
        description: Use this field to specify the ordering.
        type: string
      page:
        description: Use this field to specify page to display.
        type: string
    rpc_name: ListFruits

Field description string #

It is a good practice to give a good description of the type. This description will go to the generated protos and other generates.

Field data Servicereqres #

Data contains the repuest and response types for the service.

1
2
3
4
    data:
      request: google.protobuf.Empty
      response: fruit.FruitCollection
      bodyfield: body

request string #

Define the request type. Use google.protobuf.Empty if not needed.

response string #

Define the response type. Use google.protobuf.Empty if not needed.

bodyfield string #

This defines the body field in request the type The name of the request field whose value is mapped to the HTTP request body, or * for mapping all request fields not captured by the path pattern to the HTTP body, or omitted for not having any HTTP request body.

NOTE: the referred field must be present at the top-level of the request message type.

General URL Paht information for the service.

deeplink for GetFruit

1
2
3
4
5
    deeplink:
      description: 'Get: GET /fruits/{fr} google.protobuf.Empty , fruit.FruitEntity #Returns a single fruit.'
      href: /fruits/{fr}
      method: GET
      rel: self

description string #

Writing something like ‘Get: GET /fruits/{fr} google.protobuf.Empty , fruit.FruitEntity #Returns a single fruit.’ is a good idea. Give additional information for the dev. if needed.

href string #

The URL pattern with placeholders, like /fruits/{fr} the service is listening to .

The placeholders are defined in the request types.

method string #

The request method/verb the service is listening to.

This should be one of the following verbs:

  • GET
  • PUT
  • PATCH
  • POST
  • DELETE
Side note: If you set the verb to PUT and add a field update_mask of type google.protobuf.FieldMask to the request type update_mask: 'google.protobuf.FieldMask #Needed to patch a record' , an additional binding for the PATCH verb will be created in the proto. Because it is assumable that your service has patch and put capabilities.

rel string #

Give an according relation type for your link. For GET on entities this is usualy a self and on collections a list. For DELETE a rel delete is set. And on custom methods it is mostly the name of the custom method.

Use lowercase for the rel.

Field query Queryparam #

The query params for this service. This fields are used by the client lib to proove the capabilities of the service. Furo will update this list for you, when you come from µSpec. In near future this will be removed, because the information is already available in the request type and must not be written twice.

A query param consist of a descritpion for the documentation and a type.

1
2
3
4
5
6
7
    query:
      q:
        description: Use this to search for a fruit.
        type: string
      filter:
        description: Use this field to filter the fruits, this is not searching.
        type: string
The types and their values must be url safe. They will appear in the query string of the request.

Field rpc_name string #

The rpc name which should appear in the proto. In the example below you can see that the rpc_name was set to CreateFruit

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
service FruitService {

  // Use this to create new fruits.
  rpc CreateFruit (CreateFruitRequest) returns (google.protobuf.Empty){
	//Create: POST /fruits fruit.Fruit , google.protobuf.Empty #Use this to create new fruits.
	option (google.api.http) = {
		post: "/fruits"
		body: "body"
	};
  }
}

Example of a “Complete” Service #

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142

name: FruitService
version: ""
description: |
  Fruits are healthy, so having a service which can list some fruits would be nice.
  We do not cover all fruits, but some. The list will grow with time, hopefully.  
lifecycle: null
__proto:
  package: fruit
  targetfile: fruitservice.proto
  imports:
    - google/api/annotations.proto
    - fruit/reqmsgs.proto
    - google/protobuf/empty.proto
    - fruit/fruit.proto
    - google/protobuf/field_mask.proto
  options:
    go_package: github.com/veith/doit-specs/dist/pb/fruit;fruitpb
    java_multiple_files: "true"
    java_outer_classname: FruitserviceProto
    java_package: com.furo.basefruit
services:
  List:
    description: List fruits with pagination.
    data:
      request: google.protobuf.Empty
      response: fruit.FruitCollection
      bodyfield: body
    deeplink:
      description: 'List: GET /fruits google.protobuf.Empty , fruit.FruitCollection #List fruits with pagination.'
      href: /fruits
      method: GET
      rel: list
    query:
      q:
        description: Use this to search for a fruit.
        type: string
      filter:
        description: Use this field to filter the fruits, this is not searching.
        type: string
      order_by:
        description: Use this field to specify the ordering.
        type: string
      page:
        description: Use this field to specify page to display.
        type: string
    rpc_name: ListFruits
  Get:
    description: Returns a single fruit.
    data:
      request: google.protobuf.Empty
      response: fruit.FruitEntity
      bodyfield: body
    deeplink:
      description: 'Get: GET /fruits/{fr} google.protobuf.Empty , fruit.FruitEntity #Returns a single fruit.'
      href: /fruits/{fr}
      method: GET
      rel: self
    query:
      fr:
        description: The query param fr stands for FR id.
        type: string
    rpc_name: GetFruit
  Create:
    description: Use this to create new fruits.
    data:
      request: fruit.Fruit
      response: google.protobuf.Empty
      bodyfield: body
    deeplink:
      description: 'Create: POST /fruits fruit.Fruit , google.protobuf.Empty #Use this to create new fruits.'
      href: /fruits
      method: POST
      rel: create
    query: { }
    rpc_name: CreateFruit
  Update:
    description: Use this to update existing fruits.
    data:
      request: fruit.Fruit
      response: fruit.FruitEntity
      bodyfield: body
    deeplink:
      description: 'Update: PUT /fruits/{fr} fruit.Fruit , fruit.FruitEntity #Use this to update existing fruits.'
      href: /fruits/{fr}
      method: PUT
      rel: update
    query:
      fr:
        description: fr string.
        type: string
      update_mask:
        description: Needed to patch a record
        type: google.protobuf.FieldMask
    rpc_name: UpdateFruit
  Delete:
    description: Use this to delete existing fruits.
    data:
      request: google.protobuf.Empty
      response: google.protobuf.Empty
      bodyfield: body
    deeplink:
      description: 'Delete: DELETE /fruits/{fr} google.protobuf.Empty , google.protobuf.Empty #Use this to delete existing fruits.'
      href: /fruits/{fr}
      method: DELETE
      rel: delete
    query:
      fr:
        description: fr string.
        type: string
    rpc_name: DeleteFruit
  DeleteAll:
    description: Use this to delete ALL fruits.
    data:
      request: google.protobuf.Empty
      response: google.protobuf.Empty
      bodyfield: body
    deeplink:
      description: 'DeleteAll: DELETE /fruits google.protobuf.Empty , google.protobuf.Empty #Use this to delete ALL fruits.'
      href: /fruits
      method: DELETE
      rel: deleteall
    query: { }
    rpc_name: DeleteAllFruits
  Ferment:
    description: Fermented fruits tastes very good in liquid form.
    data:
      request: google.protobuf.Empty
      response: google.protobuf.Empty
      bodyfield: body
    deeplink:
      description: 'Ferment: POST /fruits/{fr}:ferment google.protobuf.Empty , google.protobuf.Empty #Custom methods are always POST.'
      href: /fruits/{fr}:ferment
      method: POST
      rel: ferment
    query:
      fr:
        description: fr is the placeholder for the fruit id.
        type: string
    rpc_name: FermentFruit