blob: 4f90807f0f523fa092ac191fb26df100b9f1aa6d [file] [log] [blame]
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -07001"""Generated an open-api spec for a grpc api spec.
2
3Reads the the api spec in protobuf format and generate an open-api spec.
4Optionally applies settings from the grpc-service configuration.
5"""
6
7def _collect_includes(gen_dir, srcs):
8 """Build an include path mapping.
9
10 It is important to not just collect unique dirnames, to also support
11 proto files of the same name from different packages.
12
13 The implementation below is similar to what bazel does in its
14 ProtoCompileActionBuilder.java
15 """
16 includes = []
17 for src in srcs:
18 ref_path = src.path
19
20 if ref_path.startswith(gen_dir):
21 ref_path = ref_path[len(gen_dir):].lstrip("/")
22
23 if src.owner.workspace_root:
24 workspace_root = src.owner.workspace_root
25 ref_path = ref_path[len(workspace_root):].lstrip("/")
26
27 include = ref_path + "=" + src.path
28 if include not in includes:
29 includes.append(include)
30
31 return includes
32
33def _run_proto_gen_swagger(ctx, direct_proto_srcs, transitive_proto_srcs, actions, protoc, protoc_gen_swagger, grpc_api_configuration, single_output):
34 swagger_files = []
35
36 inputs = direct_proto_srcs + transitive_proto_srcs
37 tools = [protoc_gen_swagger]
38
39 options = ["logtostderr=true", "allow_repeated_fields_in_body=true"]
40 if grpc_api_configuration:
41 options.append("grpc_api_configuration=%s" % grpc_api_configuration.path)
42 inputs.append(grpc_api_configuration)
43
44 includes = _collect_includes(ctx.genfiles_dir.path, direct_proto_srcs + transitive_proto_srcs)
45
46 if single_output:
47 swagger_file = actions.declare_file(
48 "%s.swagger.json" % ctx.attr.name,
49 sibling = direct_proto_srcs[0],
50 )
51 output_dir = ctx.bin_dir.path
52 if direct_proto_srcs[0].owner.workspace_root:
53 output_dir = "/".join([output_dir, direct_proto_srcs[0].owner.workspace_root])
54
55 output_dir = "/".join([output_dir, direct_proto_srcs[0].dirname])
56
57 options.append("allow_merge=true")
58 options.append("merge_file_name=%s" % ctx.attr.name)
59
60 args = actions.args()
61 args.add("--plugin=%s" % protoc_gen_swagger.path)
62 args.add("--swagger_out=%s:%s" % (",".join(options), output_dir))
63 args.add_all(["-I%s" % include for include in includes])
64 args.add_all([src.path for src in direct_proto_srcs])
65
66 actions.run(
67 executable = protoc,
68 inputs = inputs,
69 tools = tools,
70 outputs = [swagger_file],
71 arguments = [args],
72 )
73
74 swagger_files.append(swagger_file)
75 else:
76 for proto in direct_proto_srcs:
77 swagger_file = actions.declare_file(
78 "%s.swagger.json" % proto.basename[:-len(".proto")],
79 sibling = proto,
80 )
81
82 output_dir = ctx.bin_dir.path
83 if proto.owner.workspace_root:
84 output_dir = "/".join([output_dir, proto.owner.workspace_root])
85
86 args = actions.args()
87 args.add("--plugin=%s" % protoc_gen_swagger.path)
88 args.add("--swagger_out=%s:%s" % (",".join(options), output_dir))
89 args.add_all(["-I%s" % include for include in includes])
90 args.add(proto.path)
91
92 actions.run(
93 executable = protoc,
94 inputs = inputs,
95 tools = tools,
96 outputs = [swagger_file],
97 arguments = [args],
98 )
99
100 swagger_files.append(swagger_file)
101
102 return swagger_files
103
104def _proto_gen_swagger_impl(ctx):
105 proto = ctx.attr.proto[ProtoInfo]
106 grpc_api_configuration = ctx.file.grpc_api_configuration
107
108 return [DefaultInfo(
109 files = depset(
110 _run_proto_gen_swagger(
111 ctx,
112 direct_proto_srcs = proto.direct_sources,
113 transitive_proto_srcs = ctx.files._well_known_protos + proto.transitive_sources.to_list(),
114 actions = ctx.actions,
115 protoc = ctx.executable._protoc,
116 protoc_gen_swagger = ctx.executable._protoc_gen_swagger,
117 grpc_api_configuration = grpc_api_configuration,
118 single_output = ctx.attr.single_output,
119 ),
120 ),
121 )]
122
123protoc_gen_swagger = rule(
124 attrs = {
125 "proto": attr.label(
126 allow_rules = ["proto_library"],
127 mandatory = True,
128 providers = ["proto"],
129 ),
130 "grpc_api_configuration": attr.label(
131 allow_single_file = True,
132 mandatory = False,
133 ),
134 "single_output": attr.bool(
135 default = False,
136 mandatory = False,
137 ),
138 "_protoc": attr.label(
139 default = "@com_google_protobuf//:protoc",
140 executable = True,
141 cfg = "host",
142 ),
143 "_well_known_protos": attr.label(
144 default = "@com_google_protobuf//:well_known_protos",
145 allow_files = True,
146 ),
147 "_protoc_gen_swagger": attr.label(
148 default = Label("//protoc-gen-swagger:protoc-gen-swagger"),
149 executable = True,
150 cfg = "host",
151 ),
152 },
153 implementation = _proto_gen_swagger_impl,
154)