Skip to content

Commit f0a9bc9

Browse files
committed
feat(sidekick/rust): Generate samples for LRO RPCs.
1 parent 241bbc3 commit f0a9bc9

File tree

4 files changed

+196
-17
lines changed

4 files changed

+196
-17
lines changed

internal/sidekick/api/model.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,30 @@ func (m *Method) IsSimple() bool {
372372
m.OperationInfo == nil && m.DiscoveryLro == nil
373373
}
374374

375+
// IsLRO returns true if the method is a long-running operation.
376+
func (m *Method) IsLRO() bool {
377+
return m.OperationInfo != nil
378+
}
379+
380+
// IsSimpleOrLRO returns true if the method is simple or a long-running operation.
381+
func (m *Method) IsSimpleOrLRO() bool {
382+
return m.IsSimple() || m.IsLRO()
383+
}
384+
385+
// LongRunningResponseType returns the response type of the long-running operation.
386+
func (m *Method) LongRunningResponseType() *Message {
387+
if m.OperationInfo == nil {
388+
return nil
389+
}
390+
return m.Model.State.MessageByID[m.OperationInfo.ResponseTypeID]
391+
}
392+
393+
// LongRunningReturnsEmpty returns true if the long-running operation returns an empty response.
394+
func (m *Method) LongRunningReturnsEmpty() bool {
395+
responseType := m.LongRunningResponseType()
396+
return responseType != nil && responseType.ID == ".google.protobuf.Empty"
397+
}
398+
375399
// IsAIPStandard returns true if the method is one of the AIP standard methods.
376400
// IsAIPStandard simplifies writing mustache templates, mostly for samples.
377401
func (m *Method) IsAIPStandard() bool {

internal/sidekick/api/model_test.go

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,121 @@ func TestIsSimpleMethod(t *testing.T) {
284284
}
285285
}
286286

287+
func TestIsLRO(t *testing.T) {
288+
testCases := []struct {
289+
name string
290+
method *Method
291+
want bool
292+
}{
293+
{
294+
name: "simple method is not LRO",
295+
method: &Method{},
296+
want: false,
297+
},
298+
{
299+
name: "LRO method is LRO",
300+
method: &Method{OperationInfo: &OperationInfo{}},
301+
want: true,
302+
},
303+
}
304+
for _, tc := range testCases {
305+
t.Run(tc.name, func(t *testing.T) {
306+
if got := tc.method.IsLRO(); got != tc.want {
307+
t.Errorf("IsLRO() = %v, want %v", got, tc.want)
308+
}
309+
})
310+
}
311+
}
312+
313+
func TestIsSimpleOrLRO(t *testing.T) {
314+
testCases := []struct {
315+
name string
316+
method *Method
317+
want bool
318+
}{
319+
{
320+
name: "simple method",
321+
method: &Method{},
322+
want: true,
323+
},
324+
{
325+
name: "LRO method",
326+
method: &Method{OperationInfo: &OperationInfo{}},
327+
want: true,
328+
},
329+
{
330+
name: "streaming method",
331+
method: &Method{ClientSideStreaming: true},
332+
want: false,
333+
},
334+
}
335+
for _, tc := range testCases {
336+
t.Run(tc.name, func(t *testing.T) {
337+
if got := tc.method.IsSimpleOrLRO(); got != tc.want {
338+
t.Errorf("IsSimpleOrLRO() = %v, want %v", got, tc.want)
339+
}
340+
})
341+
}
342+
}
343+
344+
func TestLongRunningHelpers(t *testing.T) {
345+
emptyMsg := &Message{ID: ".google.protobuf.Empty"}
346+
responseMsg := &Message{ID: "some.response.Message"}
347+
model := &API{
348+
State: &APIState{
349+
MessageByID: map[string]*Message{
350+
emptyMsg.ID: emptyMsg,
351+
responseMsg.ID: responseMsg,
352+
},
353+
},
354+
}
355+
356+
testCases := []struct {
357+
name string
358+
method *Method
359+
wantResponse *Message
360+
wantEmpty bool
361+
}{
362+
{
363+
name: "LRO with empty response",
364+
method: &Method{
365+
OperationInfo: &OperationInfo{ResponseTypeID: emptyMsg.ID},
366+
Model: model,
367+
},
368+
wantResponse: emptyMsg,
369+
wantEmpty: true,
370+
},
371+
{
372+
name: "LRO with non-empty response",
373+
method: &Method{
374+
OperationInfo: &OperationInfo{ResponseTypeID: responseMsg.ID},
375+
Model: model,
376+
},
377+
wantResponse: responseMsg,
378+
wantEmpty: false,
379+
},
380+
{
381+
name: "non-LRO method",
382+
method: &Method{
383+
Model: model,
384+
},
385+
wantResponse: nil,
386+
wantEmpty: false,
387+
},
388+
}
389+
390+
for _, tc := range testCases {
391+
t.Run(tc.name, func(t *testing.T) {
392+
if got := tc.method.LongRunningResponseType(); got != tc.wantResponse {
393+
t.Errorf("LongRunningResponseType() = %v, want %v", got, tc.wantResponse)
394+
}
395+
if got := tc.method.LongRunningReturnsEmpty(); got != tc.wantEmpty {
396+
t.Errorf("LongRunningReturnsEmpty() = %v, want %v", got, tc.wantEmpty)
397+
}
398+
})
399+
}
400+
}
401+
287402
type aipTestFixture struct {
288403
resource *Resource
289404
resourceWithoutSingular *Resource

internal/sidekick/rust/templates/common/client_method_samples/client_method_sample.mustache

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -14,32 +14,21 @@ See the License for the specific language governing permissions and
1414
limitations under the License.
1515
}}
1616
{{#ModelCodec.GenerateRpcSamples}}
17-
{{#IsSimple}}
17+
{{#IsSimpleOrLRO}}
1818
///
1919
/// # Example
2020
/// ```
2121
/// # use {{Service.Model.Codec.PackageNamespace}}::client::{{Service.Codec.Name}};
22+
{{#IsLRO}}
23+
/// # use lro::Poller;
24+
{{/IsLRO}}
2225
/// use {{Service.Model.Codec.PackageNamespace}}::Result;
2326
/// async fn sample(
2427
{{> /templates/common/client_method_samples/parameters}}
2528
/// ) -> Result<()> {
26-
{{#ReturnsEmpty}}
27-
/// client
28-
/// .{{Codec.Name}}()
29-
{{> /templates/common/client_method_samples/builder_fields}}
30-
/// .send()
31-
/// .await?;
32-
{{/ReturnsEmpty}}
33-
{{^ReturnsEmpty}}
34-
/// let response = client
35-
/// .{{Codec.Name}}()
36-
{{> /templates/common/client_method_samples/builder_fields}}
37-
/// .send()
38-
/// .await?;
39-
/// println!("response {:?}", response);
40-
{{/ReturnsEmpty}}
29+
{{> /templates/common/client_method_samples/method_call}}
4130
/// Ok(())
4231
/// }
4332
/// ```
44-
{{/IsSimple}}
33+
{{/IsSimpleOrLRO}}
4534
{{/ModelCodec.GenerateRpcSamples}}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
{{!
2+
Copyright 2025 Google LLC
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
https://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
}}
16+
{{#IsSimple}}
17+
{{#ReturnsEmpty}}
18+
/// client
19+
/// .{{Codec.Name}}()
20+
{{> /templates/common/client_method_samples/builder_fields}}
21+
/// .send()
22+
/// .await?;
23+
{{/ReturnsEmpty}}
24+
{{^ReturnsEmpty}}
25+
/// let response = client
26+
/// .{{Codec.Name}}()
27+
{{> /templates/common/client_method_samples/builder_fields}}
28+
/// .send()
29+
/// .await?;
30+
/// println!("response {:?}", response);
31+
{{/ReturnsEmpty}}
32+
{{/IsSimple}}
33+
{{#IsLRO}}
34+
{{#LongRunningReturnsEmpty}}
35+
/// client
36+
/// .{{Codec.Name}}()
37+
{{> /templates/common/client_method_samples/builder_fields}}
38+
/// .poller()
39+
/// .until_done()
40+
/// .await?;
41+
{{/LongRunningReturnsEmpty}}
42+
{{^LongRunningReturnsEmpty}}
43+
/// let response = client
44+
/// .{{Codec.Name}}()
45+
{{> /templates/common/client_method_samples/builder_fields}}
46+
/// .poller()
47+
/// .until_done()
48+
/// .await?;
49+
/// println!("response {:?}", response);
50+
{{/LongRunningReturnsEmpty}}
51+
{{/IsLRO}}

0 commit comments

Comments
 (0)