From b628902eb5e35e1f9b5c3b2cb23b58a2f5a8c6b6 Mon Sep 17 00:00:00 2001 From: 8bitpal Date: Wed, 15 Apr 2026 20:45:14 +0200 Subject: [PATCH] Add 12 new snippet test files for Canton Network documentation 4 Java snippets (observability, error handling, error codes) 8 TypeScript/TSX snippets (network architecture, frontend components) All 44 snippets pass validate_snippets.py validation. --- .../appdev_modules_m4-backend-dev_L126.java | 8 +++ .../appdev_modules_m4-backend-dev_L141.java | 9 ++++ .../appdev_modules_m4-backend-dev_L182.java | 18 +++++++ .../appdev_modules_m4-backend-dev_L216.java | 12 +++++ .../appdev_modules_m4-backend-dev_L22.java | 8 +++ .../appdev_modules_m4-backend-dev_L35.java | 12 +++++ .../appdev_modules_m4-backend-dev_L406.java | 7 +++ .../appdev_modules_m4-backend-dev_L424.java | 20 +++++++ .../appdev_modules_m4-backend-dev_L449.java | 26 +++++++++ .../appdev_modules_m4-backend-dev_L482.java | 13 +++++ .../appdev_modules_m4-backend-dev_L60.java | 9 ++++ .../appdev_modules_m4-backend-dev_L78.java | 9 ++++ .../appdev_modules_m4-backend-dev_L92.java | 15 ++++++ .../appdev_modules_m4-observability_L64.java | 6 +++ ...ev_modules_m5-testing-strategies_L122.java | 17 ++++++ ...ev_modules_m5-testing-strategies_L144.java | 9 ++++ .../appdev_modules_m7-error-handling_L46.java | 19 +++++++ .../appdev_modules_m7-error-handling_L88.java | 3 ++ .../java/appdev_modules_m7-security_L58.java | 6 +++ .../appdev_reference_error-codes_L176.java | 34 ++++++++++++ .../appdev_reference_error-codes_L76.java | 34 ++++++++++++ ...ev_modules_m2-network-architecture_L136.ts | 8 +++ .../appdev_modules_m4-frontend-dev_L105.ts | 19 +++++++ .../appdev_modules_m4-frontend-dev_L129.tsx | 43 +++++++++++++++ .../appdev_modules_m4-frontend-dev_L14.ts | 11 ++++ .../appdev_modules_m4-frontend-dev_L181.ts | 10 ++++ .../appdev_modules_m4-frontend-dev_L200.ts | 41 ++++++++++++++ .../appdev_modules_m4-frontend-dev_L275.ts | 27 ++++++++++ .../appdev_modules_m4-frontend-dev_L30.ts | 4 ++ .../appdev_modules_m4-frontend-dev_L329.tsx | 53 +++++++++++++++++++ .../appdev_modules_m4-frontend-dev_L389.tsx | 8 +++ .../appdev_modules_m4-frontend-dev_L404.tsx | 6 +++ .../appdev_modules_m4-frontend-dev_L415.tsx | 7 +++ .../appdev_modules_m4-frontend-dev_L427.tsx | 35 ++++++++++++ .../appdev_modules_m4-frontend-dev_L71.tsx | 25 +++++++++ 35 files changed, 591 insertions(+) create mode 100644 docs-snippet-tests/java/appdev_modules_m4-backend-dev_L126.java create mode 100644 docs-snippet-tests/java/appdev_modules_m4-backend-dev_L141.java create mode 100644 docs-snippet-tests/java/appdev_modules_m4-backend-dev_L182.java create mode 100644 docs-snippet-tests/java/appdev_modules_m4-backend-dev_L216.java create mode 100644 docs-snippet-tests/java/appdev_modules_m4-backend-dev_L22.java create mode 100644 docs-snippet-tests/java/appdev_modules_m4-backend-dev_L35.java create mode 100644 docs-snippet-tests/java/appdev_modules_m4-backend-dev_L406.java create mode 100644 docs-snippet-tests/java/appdev_modules_m4-backend-dev_L424.java create mode 100644 docs-snippet-tests/java/appdev_modules_m4-backend-dev_L449.java create mode 100644 docs-snippet-tests/java/appdev_modules_m4-backend-dev_L482.java create mode 100644 docs-snippet-tests/java/appdev_modules_m4-backend-dev_L60.java create mode 100644 docs-snippet-tests/java/appdev_modules_m4-backend-dev_L78.java create mode 100644 docs-snippet-tests/java/appdev_modules_m4-backend-dev_L92.java create mode 100644 docs-snippet-tests/java/appdev_modules_m4-observability_L64.java create mode 100644 docs-snippet-tests/java/appdev_modules_m5-testing-strategies_L122.java create mode 100644 docs-snippet-tests/java/appdev_modules_m5-testing-strategies_L144.java create mode 100644 docs-snippet-tests/java/appdev_modules_m7-error-handling_L46.java create mode 100644 docs-snippet-tests/java/appdev_modules_m7-error-handling_L88.java create mode 100644 docs-snippet-tests/java/appdev_modules_m7-security_L58.java create mode 100644 docs-snippet-tests/java/appdev_reference_error-codes_L176.java create mode 100644 docs-snippet-tests/java/appdev_reference_error-codes_L76.java create mode 100644 docs-snippet-tests/typescript/appdev_modules_m2-network-architecture_L136.ts create mode 100644 docs-snippet-tests/typescript/appdev_modules_m4-frontend-dev_L105.ts create mode 100644 docs-snippet-tests/typescript/appdev_modules_m4-frontend-dev_L129.tsx create mode 100644 docs-snippet-tests/typescript/appdev_modules_m4-frontend-dev_L14.ts create mode 100644 docs-snippet-tests/typescript/appdev_modules_m4-frontend-dev_L181.ts create mode 100644 docs-snippet-tests/typescript/appdev_modules_m4-frontend-dev_L200.ts create mode 100644 docs-snippet-tests/typescript/appdev_modules_m4-frontend-dev_L275.ts create mode 100644 docs-snippet-tests/typescript/appdev_modules_m4-frontend-dev_L30.ts create mode 100644 docs-snippet-tests/typescript/appdev_modules_m4-frontend-dev_L329.tsx create mode 100644 docs-snippet-tests/typescript/appdev_modules_m4-frontend-dev_L389.tsx create mode 100644 docs-snippet-tests/typescript/appdev_modules_m4-frontend-dev_L404.tsx create mode 100644 docs-snippet-tests/typescript/appdev_modules_m4-frontend-dev_L415.tsx create mode 100644 docs-snippet-tests/typescript/appdev_modules_m4-frontend-dev_L427.tsx create mode 100644 docs-snippet-tests/typescript/appdev_modules_m4-frontend-dev_L71.tsx diff --git a/docs-snippet-tests/java/appdev_modules_m4-backend-dev_L126.java b/docs-snippet-tests/java/appdev_modules_m4-backend-dev_L126.java new file mode 100644 index 000000000..c9834c880 --- /dev/null +++ b/docs-snippet-tests/java/appdev_modules_m4-backend-dev_L126.java @@ -0,0 +1,8 @@ +public CompletableFuture>> active(Class clazz) { + Identifier identifier = Utils.getTemplateIdByClass(clazz); + String sql = "select contract_id, payload from active(?)"; + return runAndTraceAsync(ctx, () -> + jdbcTemplate.query(sql, new PqsContractRowMapper<>(identifier), + identifier.qualifiedName()) + ); +} diff --git a/docs-snippet-tests/java/appdev_modules_m4-backend-dev_L141.java b/docs-snippet-tests/java/appdev_modules_m4-backend-dev_L141.java new file mode 100644 index 000000000..3072a94ad --- /dev/null +++ b/docs-snippet-tests/java/appdev_modules_m4-backend-dev_L141.java @@ -0,0 +1,9 @@ +public CompletableFuture>> activeWhere( + Class clazz, String whereClause, Object... params) { + Identifier identifier = Utils.getTemplateIdByClass(clazz); + String sql = "select contract_id, payload from active(?) where " + whereClause; + return runAndTraceAsync(ctx, () -> + jdbcTemplate.query(sql, new PqsContractRowMapper<>(identifier), + combineParams(identifier.qualifiedName(), params)) + ); +} diff --git a/docs-snippet-tests/java/appdev_modules_m4-backend-dev_L182.java b/docs-snippet-tests/java/appdev_modules_m4-backend-dev_L182.java new file mode 100644 index 000000000..d8ad7a602 --- /dev/null +++ b/docs-snippet-tests/java/appdev_modules_m4-backend-dev_L182.java @@ -0,0 +1,18 @@ +UpdateServiceGrpc.UpdateServiceStub updateServiceStub = UpdateServiceGrpc.newStub(channel); +updateServiceStub.getUpdates(getUpdatesRequest.toProto(), new StreamObserver<>() { + public void onNext(UpdateServiceOuterClass.GetUpdatesResponse r) { + GetUpdatesResponse response = GetUpdatesResponse.fromProto(r); + response.getTransaction().ifPresent(transaction -> { + for (Event event : transaction.getEvents()) { + if (event instanceof CreatedEvent createdEvent) { + Iou.Contract contract = Iou.Contract.fromCreatedEvent(createdEvent); + // update local cache or trigger side effects + } else if (event instanceof ArchivedEvent archivedEvent) { + // remove from local cache + } + } + }); + } + public void onError(Throwable throwable) { /* handle error */ } + public void onCompleted() { /* stream ended */ } +}); diff --git a/docs-snippet-tests/java/appdev_modules_m4-backend-dev_L216.java b/docs-snippet-tests/java/appdev_modules_m4-backend-dev_L216.java new file mode 100644 index 000000000..fe780c7d7 --- /dev/null +++ b/docs-snippet-tests/java/appdev_modules_m4-backend-dev_L216.java @@ -0,0 +1,12 @@ +import io.grpc.StatusRuntimeException; +import io.grpc.Status; + +try { + ledger.exerciseAndGetResult(contractId, choice, commandId).join(); +} catch (CompletionException e) { + if (e.getCause() instanceof StatusRuntimeException sre) { + if (sre.getStatus().getCode() == Status.Code.NOT_FOUND) { + // contract was archived — re-query PQS and retry + } + } +} diff --git a/docs-snippet-tests/java/appdev_modules_m4-backend-dev_L22.java b/docs-snippet-tests/java/appdev_modules_m4-backend-dev_L22.java new file mode 100644 index 000000000..29dc15279 --- /dev/null +++ b/docs-snippet-tests/java/appdev_modules_m4-backend-dev_L22.java @@ -0,0 +1,8 @@ +ManagedChannelBuilder builder = ManagedChannelBuilder + .forAddress(ledgerConfig.getHost(), ledgerConfig.getPort()) + .usePlaintext(); +builder.intercept(new Interceptor(tokenProvider)); +ManagedChannel channel = builder.build(); + +submission = CommandSubmissionServiceGrpc.newFutureStub(channel); +commands = CommandServiceGrpc.newFutureStub(channel); diff --git a/docs-snippet-tests/java/appdev_modules_m4-backend-dev_L35.java b/docs-snippet-tests/java/appdev_modules_m4-backend-dev_L35.java new file mode 100644 index 000000000..f5188b5cb --- /dev/null +++ b/docs-snippet-tests/java/appdev_modules_m4-backend-dev_L35.java @@ -0,0 +1,12 @@ +@Override +public ClientCall interceptCall( + MethodDescriptor method, CallOptions callOptions, Channel next) { + ClientCall clientCall = next.newCall(method, callOptions); + return new ForwardingClientCall.SimpleForwardingClientCall<>(clientCall) { + @Override + public void start(Listener responseListener, Metadata headers) { + headers.put(AUTHORIZATION_HEADER, "Bearer " + tokenProvider.getToken()); + super.start(responseListener, headers); + } + }; +} diff --git a/docs-snippet-tests/java/appdev_modules_m4-backend-dev_L406.java b/docs-snippet-tests/java/appdev_modules_m4-backend-dev_L406.java new file mode 100644 index 000000000..7db11e590 --- /dev/null +++ b/docs-snippet-tests/java/appdev_modules_m4-backend-dev_L406.java @@ -0,0 +1,7 @@ +public CompletableFuture>> findCommentsByLicenseNum(int licenseNum) { + return pqs.activeWhere( + LicenseComment.class, + "payload->>'licenseNum' = ?", + String.valueOf(licenseNum) + ); +} diff --git a/docs-snippet-tests/java/appdev_modules_m4-backend-dev_L424.java b/docs-snippet-tests/java/appdev_modules_m4-backend-dev_L424.java new file mode 100644 index 000000000..de4cd75b7 --- /dev/null +++ b/docs-snippet-tests/java/appdev_modules_m4-backend-dev_L424.java @@ -0,0 +1,20 @@ +@Override +@WithSpan +public CompletableFuture>> listLicenseComments( + String contractId) { + var ctx = tracingCtx(logger, "listLicenseComments", "contractId", contractId); + return auth.asAuthenticatedParty(party -> traceServiceCallAsync(ctx, () -> + damlRepository.findLicenseById(contractId).thenCompose(optLicense -> { + var license = ensurePresent(optLicense, + "License not found for contract %s", contractId); + return damlRepository.findCommentsByLicenseNum( + license.payload.getLicenseNum.intValue()) + .thenApply(comments -> { + var result = comments.stream() + .map(LicenseApiImpl::toLicenseCommentApi) + .toList(); + return ResponseEntity.ok(result); + }); + }) + )); +} diff --git a/docs-snippet-tests/java/appdev_modules_m4-backend-dev_L449.java b/docs-snippet-tests/java/appdev_modules_m4-backend-dev_L449.java new file mode 100644 index 000000000..3d15d5ea8 --- /dev/null +++ b/docs-snippet-tests/java/appdev_modules_m4-backend-dev_L449.java @@ -0,0 +1,26 @@ +@Override +@WithSpan +public CompletableFuture> addLicenseComment( + String contractId, String commandId, AddCommentRequest request) { + var ctx = tracingCtx(logger, "addLicenseComment", + "contractId", contractId, "commandId", commandId); + return auth.asAuthenticatedParty(party -> traceServiceCallAsync(ctx, () -> + damlRepository.findLicenseById(contractId).thenCompose(optLicense -> { + var license = ensurePresent(optLicense, + "License not found for contract %s", contractId); + var now = Instant.now(); + var comment = new quickstart_licensing.licensing + .licensecomment.LicenseComment( + license.payload.getProvider, + license.payload.getUser, + license.payload.getLicenseNum, + new Party(party), + request.getBody(), + now + ); + return ledger.create(comment, commandId) + .thenApply(v -> ResponseEntity.status(HttpStatus.CREATED) + .build()); + }) + )); +} diff --git a/docs-snippet-tests/java/appdev_modules_m4-backend-dev_L482.java b/docs-snippet-tests/java/appdev_modules_m4-backend-dev_L482.java new file mode 100644 index 000000000..2443d92e9 --- /dev/null +++ b/docs-snippet-tests/java/appdev_modules_m4-backend-dev_L482.java @@ -0,0 +1,13 @@ +private static LicenseComment toLicenseCommentApi( + Contract contract) { + var p = contract.payload; + var api = new LicenseComment(); + api.setContractId(contract.contractId.getContractId); + api.setProvider(p.getProvider.getParty); + api.setUser(p.getUser.getParty); + api.setLicenseNum(p.getLicenseNum.intValue()); + api.setCommenter(p.getCommenter.getParty); + api.setBody(p.getBody); + api.setCreatedAt(toOffsetDateTime(p.getCreatedAt)); + return api; +} diff --git a/docs-snippet-tests/java/appdev_modules_m4-backend-dev_L60.java b/docs-snippet-tests/java/appdev_modules_m4-backend-dev_L60.java new file mode 100644 index 000000000..f6f681032 --- /dev/null +++ b/docs-snippet-tests/java/appdev_modules_m4-backend-dev_L60.java @@ -0,0 +1,9 @@ +public CompletableFuture create(T entity, String commandId) { + CommandsOuterClass.Command.Builder command = CommandsOuterClass.Command.newBuilder(); + ValueOuterClass.Value payload = dto2Proto.template(entity.templateId()).convert(entity); + command.getCreateBuilder() + .setTemplateId(toIdentifier(entity.templateId())) + .setCreateArguments(payload.getRecord()); + return submitCommands(List.of(command.build()), commandId) + .thenApply(submitResponse -> null); +} diff --git a/docs-snippet-tests/java/appdev_modules_m4-backend-dev_L78.java b/docs-snippet-tests/java/appdev_modules_m4-backend-dev_L78.java new file mode 100644 index 000000000..f4eb93462 --- /dev/null +++ b/docs-snippet-tests/java/appdev_modules_m4-backend-dev_L78.java @@ -0,0 +1,9 @@ +CommandsOuterClass.Command.Builder cmdBuilder = CommandsOuterClass.Command.newBuilder(); +ValueOuterClass.Value payload = + dto2Proto.choiceArgument(choice.templateId(), choice.choiceName()).convert(choice); + +cmdBuilder.getExerciseBuilder() + .setTemplateId(toIdentifier(choice.templateId())) + .setContractId(contractId.getContractId) + .setChoice(choice.choiceName()) + .setChoiceArgument(payload); diff --git a/docs-snippet-tests/java/appdev_modules_m4-backend-dev_L92.java b/docs-snippet-tests/java/appdev_modules_m4-backend-dev_L92.java new file mode 100644 index 000000000..311d7ad68 --- /dev/null +++ b/docs-snippet-tests/java/appdev_modules_m4-backend-dev_L92.java @@ -0,0 +1,15 @@ +@Override +public CompletableFuture> expireLicense( + String contractId, String commandId, LicenseExpireRequest request) { + return auth.asAuthenticatedParty(party -> { + return damlRepository.findLicenseById(contractId).thenCompose(optContract -> { + var license = ensurePresent(optContract, + "License not found for contract %s", contractId); + License_Expire choice = new License_Expire( + new Party(auth.getAppProviderPartyId()), + toTokenStandardMetadata(request.getMeta().getData())); + return ledger.exerciseAndGetResult(license.contractId, choice, commandId) + .thenApply(result -> ResponseEntity.ok("License expired successfully")); + }); + }); +} diff --git a/docs-snippet-tests/java/appdev_modules_m4-observability_L64.java b/docs-snippet-tests/java/appdev_modules_m4-observability_L64.java new file mode 100644 index 000000000..86b74af5f --- /dev/null +++ b/docs-snippet-tests/java/appdev_modules_m4-observability_L64.java @@ -0,0 +1,6 @@ +@WithSpan +public CompletableFuture>> listLicenses() { + // The @WithSpan annotation creates a trace span automatically + return damlRepository.findActiveLicenses() + .thenApply(licenses -> ResponseEntity.ok(licenses)); +} diff --git a/docs-snippet-tests/java/appdev_modules_m5-testing-strategies_L122.java b/docs-snippet-tests/java/appdev_modules_m5-testing-strategies_L122.java new file mode 100644 index 000000000..bef4afcab --- /dev/null +++ b/docs-snippet-tests/java/appdev_modules_m5-testing-strategies_L122.java @@ -0,0 +1,17 @@ +// Set up a gRPC channel to the participant's Ledger API +Channel channel = ManagedChannelBuilder + .forAddress(ledgerhost, ledgerport) + .usePlaintext() + .build(); + +// Create a blocking stub for command submission +CommandServiceGrpc.CommandServiceBlockingStub commandService = + CommandServiceGrpc.newBlockingStub(channel); + +// Submit a contract creation and wait for the transaction result +var updateSubmission = UpdateSubmission + .create(APP_ID, randomUUID().toString(), update) + .withActAs(party); +var request = new SubmitAndWaitForTransactionRequest( + updateSubmission.toCommandsSubmission()); +var response = commandService.submitAndWaitForTransaction(request.toProto()); diff --git a/docs-snippet-tests/java/appdev_modules_m5-testing-strategies_L144.java b/docs-snippet-tests/java/appdev_modules_m5-testing-strategies_L144.java new file mode 100644 index 000000000..a695a20fd --- /dev/null +++ b/docs-snippet-tests/java/appdev_modules_m5-testing-strategies_L144.java @@ -0,0 +1,9 @@ +StateServiceGrpc.StateServiceBlockingStub stateService = + StateServiceGrpc.newBlockingStub(channel); +long ledgerEnd = stateService + .getLedgerEnd(GetLedgerEndRequest.newBuilder().build()) + .getOffset(); + +var request = new GetActiveContractsRequest(eventFormat, ledgerEnd); +Iterator activeContracts = + stateService.getActiveContracts(request.toProto()); diff --git a/docs-snippet-tests/java/appdev_modules_m7-error-handling_L46.java b/docs-snippet-tests/java/appdev_modules_m7-error-handling_L46.java new file mode 100644 index 000000000..e411301b6 --- /dev/null +++ b/docs-snippet-tests/java/appdev_modules_m7-error-handling_L46.java @@ -0,0 +1,19 @@ +int maxRetries = 3; +for (int attempt = 0; attempt < maxRetries; attempt++) { + try { + var contract = damlRepository.findActiveAsset(assetId).join(); + if (contract.isEmpty()) { + throw new NotFoundException("Asset no longer active"); + } + ledger.exerciseAndGetResult( + contract.get().contractId, choice, UUID.randomUUID().toString() + ).join(); + return; // success + } catch (CompletionException e) { + if (isContention(e) && attempt < maxRetries - 1) { + Thread.sleep((long) Math.pow(2, attempt) * 100); // exponential backoff + continue; + } + throw e; + } +} diff --git a/docs-snippet-tests/java/appdev_modules_m7-error-handling_L88.java b/docs-snippet-tests/java/appdev_modules_m7-error-handling_L88.java new file mode 100644 index 000000000..a8b4d9205 --- /dev/null +++ b/docs-snippet-tests/java/appdev_modules_m7-error-handling_L88.java @@ -0,0 +1,3 @@ +String commandId = "renew-license-" + licenseNum + "-" + requestNonce; +ledger.exerciseAndGetResult(contractId, renewChoice, commandId).join(); +// Safe to retry with the same commandId if the response is lost diff --git a/docs-snippet-tests/java/appdev_modules_m7-security_L58.java b/docs-snippet-tests/java/appdev_modules_m7-security_L58.java new file mode 100644 index 000000000..d3243b6cc --- /dev/null +++ b/docs-snippet-tests/java/appdev_modules_m7-security_L58.java @@ -0,0 +1,6 @@ +ManagedChannel channel = NettyChannelBuilder + .forAddress(host, port) + .sslContext(GrpcSslContexts.forClient() + .trustManager(new File("ca-cert.pem")) + .build()) + .build(); diff --git a/docs-snippet-tests/java/appdev_reference_error-codes_L176.java b/docs-snippet-tests/java/appdev_reference_error-codes_L176.java new file mode 100644 index 000000000..a61cceda1 --- /dev/null +++ b/docs-snippet-tests/java/appdev_reference_error-codes_L176.java @@ -0,0 +1,34 @@ +import com.google.rpc.ErrorInfo; +import com.google.rpc.RequestInfo; +import com.google.rpc.RetryInfo; +import io.grpc.StatusRuntimeException; +import io.grpc.protobuf.StatusProto; + +try { + // your gRPC call here +} catch (StatusRuntimeException e) { + com.google.rpc.Status status = StatusProto.fromThrowable(e); + + // gRPC status code + int code = status.getCode(); + + // Full error description + String message = status.getMessage(); + + // Extract structured details + for (com.google.protobuf.Any detail : status.getDetailsList()) { + if (detail.is(ErrorInfo.class)) { + ErrorInfo info = detail.unpack(ErrorInfo.class); + String errorId = info.getReason(); + String category = info.getMetadataMap().get("category"); + } + if (detail.is(RequestInfo.class)) { + String requestId = detail.unpack(RequestInfo.class).getRequestId(); + } + if (detail.is(RetryInfo.class)) { + RetryInfo retry = detail.unpack(RetryInfo.class); + long retryMs = retry.getRetryDelay().getSeconds() * 1000 + + retry.getRetryDelay().getNanos() / 1_000_000; + } + } +} diff --git a/docs-snippet-tests/java/appdev_reference_error-codes_L76.java b/docs-snippet-tests/java/appdev_reference_error-codes_L76.java new file mode 100644 index 000000000..a61cceda1 --- /dev/null +++ b/docs-snippet-tests/java/appdev_reference_error-codes_L76.java @@ -0,0 +1,34 @@ +import com.google.rpc.ErrorInfo; +import com.google.rpc.RequestInfo; +import com.google.rpc.RetryInfo; +import io.grpc.StatusRuntimeException; +import io.grpc.protobuf.StatusProto; + +try { + // your gRPC call here +} catch (StatusRuntimeException e) { + com.google.rpc.Status status = StatusProto.fromThrowable(e); + + // gRPC status code + int code = status.getCode(); + + // Full error description + String message = status.getMessage(); + + // Extract structured details + for (com.google.protobuf.Any detail : status.getDetailsList()) { + if (detail.is(ErrorInfo.class)) { + ErrorInfo info = detail.unpack(ErrorInfo.class); + String errorId = info.getReason(); + String category = info.getMetadataMap().get("category"); + } + if (detail.is(RequestInfo.class)) { + String requestId = detail.unpack(RequestInfo.class).getRequestId(); + } + if (detail.is(RetryInfo.class)) { + RetryInfo retry = detail.unpack(RetryInfo.class); + long retryMs = retry.getRetryDelay().getSeconds() * 1000 + + retry.getRetryDelay().getNanos() / 1_000_000; + } + } +} diff --git a/docs-snippet-tests/typescript/appdev_modules_m2-network-architecture_L136.ts b/docs-snippet-tests/typescript/appdev_modules_m2-network-architecture_L136.ts new file mode 100644 index 000000000..cceae124a --- /dev/null +++ b/docs-snippet-tests/typescript/appdev_modules_m2-network-architecture_L136.ts @@ -0,0 +1,8 @@ +// Query only your party's data via your validator +const myContracts = await ledgerApi.getActiveContracts({ + party: myParty, + templateId: "Token" +}); + +// Can't query other parties' balances +// Must be added as observer to see their data diff --git a/docs-snippet-tests/typescript/appdev_modules_m4-frontend-dev_L105.ts b/docs-snippet-tests/typescript/appdev_modules_m4-frontend-dev_L105.ts new file mode 100644 index 000000000..0a450ca45 --- /dev/null +++ b/docs-snippet-tests/typescript/appdev_modules_m4-frontend-dev_L105.ts @@ -0,0 +1,19 @@ +export const LicenseProvider = ({ children }: { children: React.ReactNode }) => { + const [licenses, setLicenses] = useState([]); + const toast = useToast(); + + const fetchLicenses = useCallback( + withErrorHandling(`Fetching Licenses`)(async () => { + const client: Client = await api.getClient(); + const response = await client.listLicenses(); + setLicenses(response.data); + }), [withErrorHandling, setLicenses, toast]); + + // ... other operations (renew, expire, complete renewal) + + return ( + + {children} + + ); +}; diff --git a/docs-snippet-tests/typescript/appdev_modules_m4-frontend-dev_L129.tsx b/docs-snippet-tests/typescript/appdev_modules_m4-frontend-dev_L129.tsx new file mode 100644 index 000000000..120de1570 --- /dev/null +++ b/docs-snippet-tests/typescript/appdev_modules_m4-frontend-dev_L129.tsx @@ -0,0 +1,43 @@ +const LicensesView: React.FC = () => { + const { licenses, fetchLicenses, initiateLicenseRenewal, + initiateLicenseExpiration, completeLicenseRenewal } = useLicenseStore(); + const { user } = useUserStore(); + + useEffect(() => { + fetchLicenses(); + const intervalId = setInterval(() => { + fetchLicenses(); + }, 5000); + return () => clearInterval(intervalId); + }, [fetchLicenses]); + + return ( +
+

Licenses

+ + + + + + + + + + + + {licenses.map((license) => ( + + + + + + + + ))} + +
License Contract IDExpires AtLicense #StatusActions
{license.contractId}{formatDateTime(license.expiresAt)}{license.licenseNum}{license.isExpired ? 'EXPIRED' : 'ACTIVE'} + {/* Renew, Archive buttons */} +
+
+ ); +}; diff --git a/docs-snippet-tests/typescript/appdev_modules_m4-frontend-dev_L14.ts b/docs-snippet-tests/typescript/appdev_modules_m4-frontend-dev_L14.ts new file mode 100644 index 000000000..e58213d1f --- /dev/null +++ b/docs-snippet-tests/typescript/appdev_modules_m4-frontend-dev_L14.ts @@ -0,0 +1,11 @@ +import OpenAPIClientAxios from 'openapi-client-axios'; +import openApi from '../../common/openapi.yaml' + +const api: OpenAPIClientAxios = new OpenAPIClientAxios({ + definition: openApi as any, + withServer: { url: '/api' }, +}); + +api.init(); + +export default api; diff --git a/docs-snippet-tests/typescript/appdev_modules_m4-frontend-dev_L181.ts b/docs-snippet-tests/typescript/appdev_modules_m4-frontend-dev_L181.ts new file mode 100644 index 000000000..afbab0d83 --- /dev/null +++ b/docs-snippet-tests/typescript/appdev_modules_m4-frontend-dev_L181.ts @@ -0,0 +1,10 @@ +const expireLicense = useCallback( + withErrorHandling(`Archiving License`)(async (contractId: string, meta: Metadata) => { + const client: Client = await api.getClient(); + const commandId = generateCommandId(); + await client.expireLicense({ contractId, commandId }, { meta }); + await fetchLicenses(); + toast.displaySuccess('License archived successfully'); + }), + [withErrorHandling, fetchLicenses, toast] +); diff --git a/docs-snippet-tests/typescript/appdev_modules_m4-frontend-dev_L200.ts b/docs-snippet-tests/typescript/appdev_modules_m4-frontend-dev_L200.ts new file mode 100644 index 000000000..8a4d2117a --- /dev/null +++ b/docs-snippet-tests/typescript/appdev_modules_m4-frontend-dev_L200.ts @@ -0,0 +1,41 @@ +export const UserProvider = ({ children }: { children: React.ReactNode }) => { + const [user, setUser] = useState(null); + const [loading, setLoading] = useState(true); + + const fetchUser = useCallback(async () => { + setLoading(true); + try { + const client: Client = await api.getClient(); + const response = await client.getAuthenticatedUser(); + setUser(response.data); + } catch (error) { + if ((error as any)?.response?.status === 401) { + setUser(null); + } else { + toast.displayError('Error fetching user'); + } + } finally { + setLoading(false); + } + }, [setUser, setLoading, toast]); + + const logout = useCallback(async () => { + const response = await fetch('/api/logout', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-XSRF-TOKEN': getCsrfToken(), + }, + }); + if (response.ok) { + clearUser(); + navigate('/'); + } + }, [clearUser, toast, navigate]); + + return ( + + {children} + + ); +}; diff --git a/docs-snippet-tests/typescript/appdev_modules_m4-frontend-dev_L275.ts b/docs-snippet-tests/typescript/appdev_modules_m4-frontend-dev_L275.ts new file mode 100644 index 000000000..2b2aa6ca4 --- /dev/null +++ b/docs-snippet-tests/typescript/appdev_modules_m4-frontend-dev_L275.ts @@ -0,0 +1,27 @@ +export default defineConfig(({ mode }: ConfigEnv) => { + const env = loadEnv(mode, '../'); + const backendPort = env.VITE_BACKEND_PORT || 8080; + return { + plugins: [react(), ViteYaml()], + server: { + host: 'localhost', + strictPort: true, + allowedHosts: ['app-provider.localhost'], + proxy: { + '/api': { + target: `http://localhost:${backendPort}/`, + changeOrigin: false, + rewrite: path => path.replace(/^\/api/, ''), + }, + '/login': { + target: `http://localhost:${backendPort}/`, + changeOrigin: false, + }, + '/oauth2': { + target: `http://localhost:${backendPort}/`, + changeOrigin: false, + }, + }, + }, + } +}); diff --git a/docs-snippet-tests/typescript/appdev_modules_m4-frontend-dev_L30.ts b/docs-snippet-tests/typescript/appdev_modules_m4-frontend-dev_L30.ts new file mode 100644 index 000000000..4710bda00 --- /dev/null +++ b/docs-snippet-tests/typescript/appdev_modules_m4-frontend-dev_L30.ts @@ -0,0 +1,4 @@ +import type { Client, License } from '../openapi.d.ts'; + +const client: Client = await api.getClient(); +const response = await client.listLicenses(); // typed as License[] diff --git a/docs-snippet-tests/typescript/appdev_modules_m4-frontend-dev_L329.tsx b/docs-snippet-tests/typescript/appdev_modules_m4-frontend-dev_L329.tsx new file mode 100644 index 000000000..13f1316fc --- /dev/null +++ b/docs-snippet-tests/typescript/appdev_modules_m4-frontend-dev_L329.tsx @@ -0,0 +1,53 @@ +import React, { createContext, useContext, useState, useCallback } from 'react'; +import { useToast } from './toastStore'; +import api from '../api'; +import { generateCommandId } from '../utils/commandId'; +import type { Client, LicenseComment } from '../openapi.d.ts'; +import { withErrorHandling } from '../utils/error'; + +interface CommentContextType { + comments: LicenseComment[]; + fetchComments: (contractId: string) => Promise; + addComment: (contractId: string, body: string) => Promise; +} + +const CommentContext = createContext(undefined); + +export const CommentProvider = ({ children }: { children: React.ReactNode }) => { + const [comments, setComments] = useState([]); + const toast = useToast(); + + const fetchComments = useCallback( + withErrorHandling('Fetching Comments')(async (contractId: string) => { + const client: Client = await api.getClient(); + const response = await client.listLicenseComments({ contractId }); + setComments(response.data); + }), + [withErrorHandling, setComments, toast] + ); + + const addComment = useCallback( + withErrorHandling('Adding Comment')(async (contractId: string, body: string) => { + const client: Client = await api.getClient(); + const commandId = generateCommandId(); + await client.addLicenseComment({ contractId, commandId }, { body }); + await fetchComments(contractId); + toast.displaySuccess('Comment added successfully'); + }), + [withErrorHandling, fetchComments, toast] + ); + + return ( + + {children} + + ); +}; + +export const useCommentStore = () => { + const context = useContext(CommentContext); + if (context === undefined) { + throw new Error('useCommentStore must be used within a CommentProvider'); + } + return context; +}; diff --git a/docs-snippet-tests/typescript/appdev_modules_m4-frontend-dev_L389.tsx b/docs-snippet-tests/typescript/appdev_modules_m4-frontend-dev_L389.tsx new file mode 100644 index 000000000..d4ad71f59 --- /dev/null +++ b/docs-snippet-tests/typescript/appdev_modules_m4-frontend-dev_L389.tsx @@ -0,0 +1,8 @@ +const AppProviders = composeProviders( + ToastProvider, + UserProvider, + TenantRegistrationProvider, + AppInstallProvider, + LicenseProvider, + CommentProvider // add this +); diff --git a/docs-snippet-tests/typescript/appdev_modules_m4-frontend-dev_L404.tsx b/docs-snippet-tests/typescript/appdev_modules_m4-frontend-dev_L404.tsx new file mode 100644 index 000000000..1c20dfb51 --- /dev/null +++ b/docs-snippet-tests/typescript/appdev_modules_m4-frontend-dev_L404.tsx @@ -0,0 +1,6 @@ +import { useCommentStore } from '../stores/commentStore'; + +// inside the component: +const { comments, fetchComments, addComment } = useCommentStore(); +const [showComments, setShowComments] = useState(null); +const [commentBody, setCommentBody] = useState(''); diff --git a/docs-snippet-tests/typescript/appdev_modules_m4-frontend-dev_L415.tsx b/docs-snippet-tests/typescript/appdev_modules_m4-frontend-dev_L415.tsx new file mode 100644 index 000000000..6428aadb5 --- /dev/null +++ b/docs-snippet-tests/typescript/appdev_modules_m4-frontend-dev_L415.tsx @@ -0,0 +1,7 @@ +useEffect(() => { + if (showComments) { + fetchComments(showComments); + const intervalId = setInterval(() => fetchComments(showComments), 5000); + return () => clearInterval(intervalId); + } +}, [showComments, fetchComments]); diff --git a/docs-snippet-tests/typescript/appdev_modules_m4-frontend-dev_L427.tsx b/docs-snippet-tests/typescript/appdev_modules_m4-frontend-dev_L427.tsx new file mode 100644 index 000000000..709891062 --- /dev/null +++ b/docs-snippet-tests/typescript/appdev_modules_m4-frontend-dev_L427.tsx @@ -0,0 +1,35 @@ +{showComments && ( +
+

+ Comments for License # + {licenses.find(l => l.contractId === showComments)?.licenseNum} +

+ {comments.length === 0 ? ( +

No comments yet.

+ ) : ( +
    + {comments.map((c) => ( +
  • + {c.commenter} + + {formatDateTime(c.createdAt)} + +

    {c.body}

    +
  • + ))} +
+ )} +
+ setCommentBody(e.target.value)} + placeholder="Add a comment..." + /> + +
+
+)} diff --git a/docs-snippet-tests/typescript/appdev_modules_m4-frontend-dev_L71.tsx b/docs-snippet-tests/typescript/appdev_modules_m4-frontend-dev_L71.tsx new file mode 100644 index 000000000..487b00a80 --- /dev/null +++ b/docs-snippet-tests/typescript/appdev_modules_m4-frontend-dev_L71.tsx @@ -0,0 +1,25 @@ +const App: React.FC = () => { + const AppProviders = composeProviders( + ToastProvider, + UserProvider, + TenantRegistrationProvider, + AppInstallProvider, + LicenseProvider + ); + + return ( + +
+
+ + } /> + } /> + } /> + } /> + } /> + +
+ + + ); +};