• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

temporalio / sdk-java / #172

pending completion
#172

push

github-actions

web-flow
Update CODEOWNERS (#1773)

## What was changed
Update CODEOWNERS so that Security can own the Semgrep rules files and paths.

## Why?
We are adding Semgrep for static analysis to this repository, and only the security team should be able to approve exclusions from the policy.

## Checklist

How was this tested:
We ran this scanner on internal repos with this CODEOWNERS file and it worked as expected.

18029 of 22084 relevant lines covered (81.64%)

0.82 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

84.07
/temporal-sdk/src/main/java/io/temporal/client/WorkflowStubImpl.java
1
/*
2
 * Copyright (C) 2022 Temporal Technologies, Inc. All Rights Reserved.
3
 *
4
 * Copyright (C) 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
5
 *
6
 * Modifications copyright (C) 2017 Uber Technologies, Inc.
7
 *
8
 * Licensed under the Apache License, Version 2.0 (the "License");
9
 * you may not use this material except in compliance with the License.
10
 * You may obtain a copy of the License at
11
 *
12
 *   http://www.apache.org/licenses/LICENSE-2.0
13
 *
14
 * Unless required by applicable law or agreed to in writing, software
15
 * distributed under the License is distributed on an "AS IS" BASIS,
16
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
 * See the License for the specific language governing permissions and
18
 * limitations under the License.
19
 */
20

21
package io.temporal.client;
22

23
import com.google.common.base.Strings;
24
import io.grpc.Status;
25
import io.grpc.StatusRuntimeException;
26
import io.temporal.api.common.v1.WorkflowExecution;
27
import io.temporal.api.errordetails.v1.QueryFailedFailure;
28
import io.temporal.api.errordetails.v1.WorkflowExecutionAlreadyStartedFailure;
29
import io.temporal.api.errordetails.v1.WorkflowNotReadyFailure;
30
import io.temporal.api.update.v1.WaitPolicy;
31
import io.temporal.common.interceptors.Header;
32
import io.temporal.common.interceptors.WorkflowClientCallsInterceptor;
33
import io.temporal.failure.CanceledFailure;
34
import io.temporal.serviceclient.CheckedExceptionWrapper;
35
import io.temporal.serviceclient.StatusUtils;
36
import java.lang.reflect.Type;
37
import java.util.Optional;
38
import java.util.UUID;
39
import java.util.concurrent.*;
40
import java.util.concurrent.atomic.AtomicReference;
41
import javax.annotation.Nonnull;
42
import javax.annotation.Nullable;
43

44
class WorkflowStubImpl implements WorkflowStub {
45
  private final WorkflowClientOptions clientOptions;
46
  private final WorkflowClientCallsInterceptor workflowClientInvoker;
47
  private final Optional<String> workflowType;
48
  // Execution this stub is bound to
49
  private final AtomicReference<WorkflowExecution> execution = new AtomicReference<>();
1✔
50
  // Full WorkflowExecution that this stub is started if any.
51
  // After a start, WorkflowStub binds to (workflowId, null) to follow the chain of RunIds.
52
  // But this field keeps the full (workflowId, runId) execution info that was started by this stub.
53
  private final AtomicReference<WorkflowExecution> startedExecution = new AtomicReference<>();
1✔
54
  // if null, this stub is created to bound to an existing execution.
55
  // This stub is created to bound to an existing execution otherwise.
56
  private final @Nullable WorkflowOptions options;
57

58
  WorkflowStubImpl(
59
      WorkflowClientOptions clientOptions,
60
      WorkflowClientCallsInterceptor workflowClientInvoker,
61
      Optional<String> workflowType,
62
      WorkflowExecution execution) {
1✔
63
    this.clientOptions = clientOptions;
1✔
64
    this.workflowClientInvoker = workflowClientInvoker;
1✔
65
    this.workflowType = workflowType;
1✔
66
    if (execution == null || execution.getWorkflowId().isEmpty()) {
1✔
67
      throw new IllegalArgumentException("null or empty workflowId");
×
68
    }
69
    this.execution.set(execution);
1✔
70
    this.options = null;
1✔
71
  }
1✔
72

73
  WorkflowStubImpl(
74
      WorkflowClientOptions clientOptions,
75
      WorkflowClientCallsInterceptor workflowClientInvoker,
76
      String workflowType,
77
      @Nonnull WorkflowOptions options) {
1✔
78
    this.clientOptions = clientOptions;
1✔
79
    this.workflowClientInvoker = workflowClientInvoker;
1✔
80
    this.workflowType = Optional.of(workflowType);
1✔
81
    this.options = options;
1✔
82
  }
1✔
83

84
  @Override
85
  public void signal(String signalName, Object... args) {
86
    checkStarted();
1✔
87
    WorkflowExecution targetExecution = currentExecutionWithoutRunId();
1✔
88
    try {
89
      workflowClientInvoker.signal(
1✔
90
          new WorkflowClientCallsInterceptor.WorkflowSignalInput(
91
              targetExecution, signalName, args));
92
    } catch (Exception e) {
1✔
93
      Throwable throwable = throwAsWorkflowFailureException(e, targetExecution);
×
94
      throw new WorkflowServiceException(targetExecution, workflowType.orElse(null), throwable);
×
95
    }
1✔
96
  }
1✔
97

98
  private WorkflowExecution startWithOptions(WorkflowOptions options, Object... args) {
99
    checkExecutionIsNotStarted();
1✔
100
    String workflowId = getWorkflowIdForStart(options);
1✔
101
    WorkflowExecution workflowExecution = null;
1✔
102
    try {
103
      WorkflowClientCallsInterceptor.WorkflowStartOutput workflowStartOutput =
1✔
104
          workflowClientInvoker.start(
1✔
105
              new WorkflowClientCallsInterceptor.WorkflowStartInput(
106
                  workflowId, workflowType.get(), Header.empty(), args, options));
1✔
107
      workflowExecution = workflowStartOutput.getWorkflowExecution();
1✔
108
      populateExecutionAfterStart(workflowExecution);
1✔
109
      return workflowExecution;
1✔
110
    } catch (StatusRuntimeException e) {
1✔
111
      throw wrapStartException(workflowId, workflowType.orElse(null), e);
1✔
112
    } catch (Exception e) {
1✔
113
      if (workflowExecution == null) {
1✔
114
        // if start failed with exception - there could be no valid workflow execution populated
115
        // from the server.
116
        // WorkflowServiceException requires not null workflowExecution, so we have to provide
117
        // an WorkflowExecution instance with just a workflowId
118
        workflowExecution = WorkflowExecution.newBuilder().setWorkflowId(workflowId).build();
1✔
119
      }
120
      throw new WorkflowServiceException(workflowExecution, workflowType.orElse(null), e);
1✔
121
    }
122
  }
123

124
  @Override
125
  public WorkflowExecution start(Object... args) {
126
    if (options == null) {
1✔
127
      throw new IllegalStateException("Required parameter WorkflowOptions is missing");
×
128
    }
129
    return startWithOptions(WorkflowOptions.merge(null, null, options), args);
1✔
130
  }
131

132
  private WorkflowExecution signalWithStartWithOptions(
133
      WorkflowOptions options, String signalName, Object[] signalArgs, Object[] startArgs) {
134
    checkExecutionIsNotStarted();
1✔
135
    String workflowId = getWorkflowIdForStart(options);
1✔
136
    WorkflowExecution workflowExecution = null;
1✔
137
    try {
138
      WorkflowClientCallsInterceptor.WorkflowSignalWithStartOutput workflowStartOutput =
1✔
139
          workflowClientInvoker.signalWithStart(
1✔
140
              new WorkflowClientCallsInterceptor.WorkflowSignalWithStartInput(
141
                  new WorkflowClientCallsInterceptor.WorkflowStartInput(
142
                      workflowId, workflowType.get(), Header.empty(), startArgs, options),
1✔
143
                  signalName,
144
                  signalArgs));
145
      workflowExecution = workflowStartOutput.getWorkflowStartOutput().getWorkflowExecution();
1✔
146
      populateExecutionAfterStart(workflowExecution);
1✔
147
      return workflowExecution;
1✔
148
    } catch (StatusRuntimeException e) {
1✔
149
      throw wrapStartException(workflowId, workflowType.orElse(null), e);
1✔
150
    } catch (Exception e) {
×
151
      if (workflowExecution == null) {
×
152
        // if start failed with exception - there could be no valid workflow execution populated
153
        // from the server.
154
        // WorkflowServiceException requires not null workflowExecution, so we have to provide
155
        // an WorkflowExecution instance with just a workflowId
156
        workflowExecution = WorkflowExecution.newBuilder().setWorkflowId(workflowId).build();
×
157
      }
158
      throw new WorkflowServiceException(workflowExecution, workflowType.orElse(null), e);
×
159
    }
160
  }
161

162
  private static String getWorkflowIdForStart(WorkflowOptions options) {
163
    String workflowId = options.getWorkflowId();
1✔
164
    if (workflowId == null) {
1✔
165
      workflowId = UUID.randomUUID().toString();
1✔
166
    }
167
    return workflowId;
1✔
168
  }
169

170
  @Override
171
  public WorkflowExecution signalWithStart(
172
      String signalName, Object[] signalArgs, Object[] startArgs) {
173
    if (options == null) {
1✔
174
      throw new IllegalStateException("Required parameter WorkflowOptions is missing");
×
175
    }
176
    return signalWithStartWithOptions(
1✔
177
        WorkflowOptions.merge(null, null, options), signalName, signalArgs, startArgs);
1✔
178
  }
179

180
  @Override
181
  public Optional<String> getWorkflowType() {
182
    return workflowType;
1✔
183
  }
184

185
  @Override
186
  public WorkflowExecution getExecution() {
187
    return options != null ? startedExecution.get() : execution.get();
1✔
188
  }
189

190
  @Override
191
  public <R> R getResult(Class<R> resultClass) {
192
    return getResult(resultClass, resultClass);
1✔
193
  }
194

195
  @Override
196
  public <R> R getResult(Class<R> resultClass, Type resultType) {
197
    try {
198
      // int max to not overflow long
199
      return getResult(Integer.MAX_VALUE, TimeUnit.MILLISECONDS, resultClass, resultType);
1✔
200
    } catch (TimeoutException e) {
×
201
      throw new WorkflowServiceException(execution.get(), workflowType.orElse(null), e);
×
202
    }
203
  }
204

205
  @Override
206
  public <R> R getResult(long timeout, TimeUnit unit, Class<R> resultClass)
207
      throws TimeoutException {
208
    return getResult(timeout, unit, resultClass, resultClass);
×
209
  }
210

211
  @Override
212
  public <R> R getResult(long timeout, TimeUnit unit, Class<R> resultClass, Type resultType)
213
      throws TimeoutException {
214
    checkStarted();
1✔
215
    WorkflowExecution targetExecution = execution.get();
1✔
216
    try {
217
      WorkflowClientCallsInterceptor.GetResultOutput<R> result =
1✔
218
          workflowClientInvoker.getResult(
1✔
219
              new WorkflowClientCallsInterceptor.GetResultInput<>(
220
                  targetExecution, workflowType, timeout, unit, resultClass, resultType));
221
      return result.getResult();
1✔
222
    } catch (Exception e) {
1✔
223
      return throwAsWorkflowFailureExceptionForResult(e, resultClass, targetExecution);
×
224
    }
225
  }
226

227
  @Override
228
  public <R> CompletableFuture<R> getResultAsync(Class<R> resultClass) {
229
    return getResultAsync(resultClass, resultClass);
1✔
230
  }
231

232
  @Override
233
  public <R> CompletableFuture<R> getResultAsync(Class<R> resultClass, Type resultType) {
234
    return getResultAsync(Long.MAX_VALUE, TimeUnit.MILLISECONDS, resultClass, resultType);
1✔
235
  }
236

237
  @Override
238
  public <R> CompletableFuture<R> getResultAsync(
239
      long timeout, TimeUnit unit, Class<R> resultClass) {
240
    return getResultAsync(timeout, unit, resultClass, resultClass);
1✔
241
  }
242

243
  @Override
244
  public <R> CompletableFuture<R> getResultAsync(
245
      long timeout, TimeUnit unit, Class<R> resultClass, Type resultType) {
246
    checkStarted();
1✔
247
    WorkflowExecution targetExecution = execution.get();
1✔
248
    WorkflowClientCallsInterceptor.GetResultAsyncOutput<R> result =
1✔
249
        workflowClientInvoker.getResultAsync(
1✔
250
            new WorkflowClientCallsInterceptor.GetResultInput<>(
251
                targetExecution, workflowType, timeout, unit, resultClass, resultType));
252
    return result
1✔
253
        .getResult()
1✔
254
        .exceptionally(
1✔
255
            e -> {
256
              try {
257
                return throwAsWorkflowFailureExceptionForResult(e, resultClass, targetExecution);
×
258
              } catch (TimeoutException ex) {
1✔
259
                throw new CompletionException(ex);
1✔
260
              }
261
            });
262
  }
263

264
  @Override
265
  public <R> R query(String queryType, Class<R> resultClass, Object... args) {
266
    return query(queryType, resultClass, resultClass, args);
1✔
267
  }
268

269
  @Override
270
  public <R> R query(String queryType, Class<R> resultClass, Type resultType, Object... args) {
271
    checkStarted();
1✔
272
    WorkflowClientCallsInterceptor.QueryOutput<R> result;
273
    WorkflowExecution targetExecution = execution.get();
1✔
274
    try {
275
      result =
1✔
276
          workflowClientInvoker.query(
1✔
277
              new WorkflowClientCallsInterceptor.QueryInput<>(
278
                  targetExecution, queryType, args, resultClass, resultType));
279
    } catch (Exception e) {
1✔
280
      return throwAsWorkflowFailureExceptionForQuery(e, resultClass, targetExecution);
×
281
    }
1✔
282
    if (result.isQueryRejected()) {
1✔
283
      throw new WorkflowQueryConditionallyRejectedException(
1✔
284
          targetExecution,
285
          workflowType.orElse(null),
1✔
286
          clientOptions.getQueryRejectCondition(),
1✔
287
          result.getQueryRejectedStatus(),
1✔
288
          null);
289
    }
290
    return result.getResult();
1✔
291
  }
292

293
  @Override
294
  public <R> R update(String updateName, Class<R> resultClass, Object... args) {
295
    checkStarted();
1✔
296
    try {
297
      UpdateOptions<R> options =
298
          UpdateOptions.<R>newBuilder()
1✔
299
              .setUpdateName(updateName)
1✔
300
              .setWaitPolicy(UpdateWaitPolicy.COMPLETED)
1✔
301
              .setResultClass(resultClass)
1✔
302
              .build();
1✔
303
      return startUpdate(options, args).getResultAsync().get();
1✔
304
    } catch (InterruptedException e) {
×
305
      throw new RuntimeException(e);
×
306
    } catch (ExecutionException e) {
×
307
      throw new RuntimeException(e);
×
308
    }
309
  }
310

311
  @Override
312
  public <R> UpdateHandle<R> startUpdate(String updateName, Class<R> resultClass, Object... args) {
313
    UpdateOptions<R> options =
314
        UpdateOptions.<R>newBuilder()
1✔
315
            .setUpdateName(updateName)
1✔
316
            .setWaitPolicy(UpdateWaitPolicy.ACCEPTED)
1✔
317
            .setResultClass(resultClass)
1✔
318
            .setResultType(resultClass)
1✔
319
            .build();
1✔
320

321
    return startUpdate(options, args);
1✔
322
  }
323

324
  @Override
325
  public <R> UpdateHandle<R> startUpdate(UpdateOptions<R> options, Object... args) {
326
    checkStarted();
1✔
327
    options.validate();
1✔
328
    WorkflowExecution targetExecution = execution.get();
1✔
329
    try {
330
      WorkflowClientCallsInterceptor.StartUpdateOutput<R> result =
1✔
331
          workflowClientInvoker.startUpdate(
1✔
332
              new WorkflowClientCallsInterceptor.StartUpdateInput<>(
333
                  targetExecution,
334
                  options.getUpdateName(),
1✔
335
                  options.getUpdateId(),
1✔
336
                  args,
337
                  options.getResultClass(),
1✔
338
                  options.getResultType(),
1✔
339
                  options.getFirstExecutionRunId(),
1✔
340
                  WaitPolicy.newBuilder()
1✔
341
                      .setLifecycleStage(options.getWaitPolicy().getProto())
1✔
342
                      .build()));
1✔
343

344
      if (result.hasResult()) {
1✔
345
        return new CompletedUpdateHandleImpl<>(
1✔
346
            result.getReference().getUpdateId(),
1✔
347
            result.getReference().getWorkflowExecution(),
1✔
348
            result.getResult());
1✔
349
      } else {
350
        return new LazyUpdateHandleImpl<>(
1✔
351
            workflowClientInvoker,
352
            workflowType.orElse(null),
1✔
353
            options.getUpdateName(),
1✔
354
            result.getReference().getUpdateId(),
1✔
355
            result.getReference().getWorkflowExecution(),
1✔
356
            options.getResultClass(),
1✔
357
            options.getResultType());
1✔
358
      }
359
    } catch (Exception e) {
1✔
360
      Throwable throwable = throwAsWorkflowFailureException(e, targetExecution);
×
361
      throw new WorkflowServiceException(targetExecution, workflowType.orElse(null), throwable);
×
362
    }
363
  }
364

365
  @Override
366
  public <R> UpdateHandle<R> getUpdateHandle(String updateId, Class<R> resultClass) {
367
    return new LazyUpdateHandleImpl<>(
1✔
368
        workflowClientInvoker,
369
        workflowType.orElse(null),
1✔
370
        "",
371
        updateId,
372
        execution.get(),
1✔
373
        resultClass,
374
        resultClass);
375
  }
376

377
  @Override
378
  public <R> UpdateHandle<R> getUpdateHandle(
379
      String updateId, Class<R> resultClass, Type resultType) {
380
    return new LazyUpdateHandleImpl<>(
×
381
        workflowClientInvoker,
382
        workflowType.orElse(null),
×
383
        "",
384
        updateId,
385
        execution.get(),
×
386
        resultClass,
387
        resultType);
388
  }
389

390
  @Override
391
  public void cancel() {
392
    checkStarted();
1✔
393
    WorkflowExecution targetExecution = currentExecutionWithoutRunId();
1✔
394
    try {
395
      workflowClientInvoker.cancel(new WorkflowClientCallsInterceptor.CancelInput(targetExecution));
1✔
396
    } catch (Exception e) {
1✔
397
      Throwable failure = throwAsWorkflowFailureException(e, targetExecution);
×
398
      throw new WorkflowServiceException(targetExecution, workflowType.orElse(null), failure);
×
399
    }
1✔
400
  }
1✔
401

402
  @Override
403
  public void terminate(@Nullable String reason, Object... details) {
404
    checkStarted();
1✔
405
    WorkflowExecution targetExecution = currentExecutionWithoutRunId();
1✔
406
    try {
407
      workflowClientInvoker.terminate(
1✔
408
          new WorkflowClientCallsInterceptor.TerminateInput(targetExecution, reason, details));
409
    } catch (Exception e) {
1✔
410
      Throwable failure = throwAsWorkflowFailureException(e, targetExecution);
×
411
      throw new WorkflowServiceException(targetExecution, workflowType.orElse(null), failure);
×
412
    }
1✔
413
  }
1✔
414

415
  @Override
416
  public Optional<WorkflowOptions> getOptions() {
417
    return Optional.ofNullable(options);
1✔
418
  }
419

420
  private void checkStarted() {
421
    if (execution.get() == null || execution.get().getWorkflowId() == null) {
1✔
422
      throw new IllegalStateException("Null workflowId. Was workflow started?");
×
423
    }
424
  }
1✔
425

426
  private void checkExecutionIsNotStarted() {
427
    if (execution.get() != null) {
1✔
428
      throw new IllegalStateException(
1✔
429
          "Cannot reuse a stub instance to start more than one workflow execution. The stub "
430
              + "points to already started execution. If you are trying to wait for a workflow completion either "
431
              + "change WorkflowIdReusePolicy from AllowDuplicate or use WorkflowStub.getResult");
432
    }
433
  }
1✔
434

435
  /*
436
   * Exceptions handling and processing for all methods of the stub
437
   */
438
  private RuntimeException wrapStartException(
439
      String workflowId, String workflowType, StatusRuntimeException e) {
440
    WorkflowExecution.Builder executionBuilder =
441
        WorkflowExecution.newBuilder().setWorkflowId(workflowId);
1✔
442

443
    WorkflowExecutionAlreadyStartedFailure f =
1✔
444
        StatusUtils.getFailure(e, WorkflowExecutionAlreadyStartedFailure.class);
1✔
445
    if (f != null) {
1✔
446
      WorkflowExecution exe = executionBuilder.setRunId(f.getRunId()).build();
1✔
447
      populateExecutionAfterStart(exe);
1✔
448
      return new WorkflowExecutionAlreadyStarted(exe, workflowType, e);
1✔
449
    } else {
450
      WorkflowExecution exe = executionBuilder.build();
1✔
451
      return new WorkflowServiceException(exe, workflowType, e);
1✔
452
    }
453
  }
454

455
  /**
456
   * RunId can change e.g. workflow does ContinueAsNew. Emptying runId in workflowExecution allows
457
   * Temporal server figure out the current run id dynamically.
458
   */
459
  private WorkflowExecution currentExecutionWithoutRunId() {
460
    WorkflowExecution workflowExecution = execution.get();
1✔
461
    if (Strings.isNullOrEmpty(workflowExecution.getRunId())) {
1✔
462
      return workflowExecution;
1✔
463
    } else {
464
      return WorkflowExecution.newBuilder(workflowExecution).setRunId("").build();
1✔
465
    }
466
  }
467

468
  private <R> R throwAsWorkflowFailureExceptionForQuery(
469
      Throwable failure,
470
      @SuppressWarnings("unused") Class<R> returnType,
471
      WorkflowExecution targetExecution) {
472
    failure = throwAsWorkflowFailureException(failure, targetExecution);
1✔
473
    if (failure instanceof StatusRuntimeException) {
1✔
474
      StatusRuntimeException sre = (StatusRuntimeException) failure;
1✔
475
      if (StatusUtils.hasFailure(sre, QueryFailedFailure.class)) {
1✔
476
        throw new WorkflowQueryException(execution.get(), workflowType.orElse(null), failure);
1✔
477
      } else if (Status.Code.FAILED_PRECONDITION.equals(sre.getStatus().getCode())
×
478
          && StatusUtils.hasFailure(sre, WorkflowNotReadyFailure.class)) {
×
479
        // Processes the edge case introduced by https://github.com/temporalio/temporal/pull/2826
480
        throw new WorkflowQueryRejectedException(
×
481
            targetExecution, workflowType.orElse(null), failure);
×
482
      }
483
    }
484
    throw new WorkflowServiceException(targetExecution, workflowType.orElse(null), failure);
×
485
  }
486

487
  // This function never returns anything, it only throws
488
  private <R> R throwAsWorkflowFailureExceptionForResult(
489
      Throwable failure,
490
      @SuppressWarnings("unused") Class<R> returnType,
491
      WorkflowExecution targetExecution)
492
      throws TimeoutException {
493
    failure = throwAsWorkflowFailureException(failure, targetExecution);
1✔
494
    if (failure instanceof TimeoutException) {
1✔
495
      throw (TimeoutException) failure;
1✔
496
    } else if (failure instanceof CanceledFailure) {
1✔
497
      throw (CanceledFailure) failure;
×
498
    }
499
    throw new WorkflowServiceException(targetExecution, workflowType.orElse(null), failure);
1✔
500
  }
501

502
  private Throwable throwAsWorkflowFailureException(
503
      Throwable failure, WorkflowExecution targetExecution) {
504
    if (failure instanceof CompletionException) {
1✔
505
      // if we work with CompletableFuture, the exception may be wrapped into CompletionException
506
      failure = failure.getCause();
1✔
507
    }
508
    failure = CheckedExceptionWrapper.unwrap(failure);
1✔
509
    if (failure instanceof Error) {
1✔
510
      throw (Error) failure;
×
511
    }
512
    if (failure instanceof StatusRuntimeException) {
1✔
513
      StatusRuntimeException sre = (StatusRuntimeException) failure;
1✔
514
      if (Status.Code.NOT_FOUND.equals(sre.getStatus().getCode())) {
1✔
515
        throw new WorkflowNotFoundException(targetExecution, workflowType.orElse(null), sre);
1✔
516
      }
517
    } else if (failure instanceof WorkflowException) {
1✔
518
      throw (WorkflowException) failure;
1✔
519
    }
520
    return failure;
1✔
521
  }
522

523
  private void populateExecutionAfterStart(WorkflowExecution startedExecution) {
524
    this.startedExecution.set(startedExecution);
1✔
525
    // bind to an execution without a runId, so queries follow runId chains by default
526
    this.execution.set(WorkflowExecution.newBuilder(startedExecution).setRunId("").build());
1✔
527
  }
1✔
528
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc