cabloy 5.1.117 → 5.1.119
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.cabloy-version +1 -1
- package/.github/workflows/vona-test-pg.yml +2 -2
- package/.github/workflows/vona-test-sqlite3.yml +2 -2
- package/CHANGELOG.md +19 -0
- package/cabloy-docs/backend/metrics-guide.md +75 -0
- package/e2e/specs/a-commerce/commerce.spec.ts +111 -0
- package/observability/README.md +18 -0
- package/observability/collector/otel-collector.yaml +34 -0
- package/observability/docker-compose.metrics.yml +25 -0
- package/observability/grafana/provisioning/datasources/prometheus.yaml +8 -0
- package/observability/prometheus/prometheus.yaml +7 -0
- package/package.json +1 -1
- package/scripts/upgrade.ts +1 -0
- package/vona/env/.env +6 -0
- package/vona/packages-cli/cli/package.json +1 -1
- package/vona/packages-cli/cli-set-api/package.json +1 -1
- package/vona/packages-vona/vona/package.json +1 -1
- package/vona/packages-vona/vona-core/package.json +1 -1
- package/vona/packages-vona/vona-core/src/lib/framework/processWorker.ts +1 -1
- package/vona/packages-vona/vona-mock/package.json +1 -1
- package/vona/packages-vona/vona-mock/src/index.ts +2 -0
- package/vona/packages-vona/vona-mock/src/lib/testLock.ts +21 -0
- package/vona/pnpm-lock.yaml +167 -64
- package/vona/src/suite/a-commerce/modules/commerce-catalog/test/catalog.test.ts +13 -3
- package/vona/src/suite/a-commerce/modules/commerce-catalog/test/product.test.ts +124 -70
- package/vona/src/suite/a-commerce/modules/commerce-payment/src/.metadata/index.ts +221 -0
- package/vona/src/suite/a-commerce/modules/commerce-payment/src/bean/meta.index.ts +12 -0
- package/vona/src/suite/a-commerce/modules/commerce-payment/src/bean/meta.version.ts +91 -0
- package/vona/src/suite/a-commerce/modules/commerce-payment/src/entity/refundAttempt.tsx +37 -0
- package/vona/src/suite/a-commerce/modules/commerce-payment/src/entity/refundAudit.tsx +47 -0
- package/vona/src/suite/a-commerce/modules/commerce-payment/src/entity/refundRequest.tsx +43 -0
- package/vona/src/suite/a-commerce/modules/commerce-payment/src/model/refundAttempt.ts +10 -0
- package/vona/src/suite/a-commerce/modules/commerce-payment/src/model/refundAudit.ts +10 -0
- package/vona/src/suite/a-commerce/modules/commerce-payment/src/model/refundRequest.ts +10 -0
- package/vona/src/suite/a-commerce/modules/commerce-trade/src/.metadata/index.ts +36 -0
- package/vona/src/suite/a-commerce/modules/commerce-trade/src/controller/order.ts +43 -0
- package/vona/src/suite/a-commerce/modules/commerce-trade/src/dto/orderDetail.tsx +23 -2
- package/vona/src/suite/a-commerce/modules/commerce-trade/src/dto/orderSelectResItem.tsx +3 -0
- package/vona/src/suite/a-commerce/modules/commerce-trade/src/dto/orderSummary.tsx +23 -2
- package/vona/src/suite/a-commerce/modules/commerce-trade/src/dto/refundOutcomeCreate.tsx +16 -0
- package/vona/src/suite/a-commerce/modules/commerce-trade/src/dto/refundRequestCreate.tsx +15 -0
- package/vona/src/suite/a-commerce/modules/commerce-trade/src/dto/refundResult.tsx +35 -0
- package/vona/src/suite/a-commerce/modules/commerce-trade/src/dto/refundReview.tsx +15 -0
- package/vona/src/suite/a-commerce/modules/commerce-trade/src/entity/orderAudit.tsx +55 -13
- package/vona/src/suite/a-commerce/modules/commerce-trade/src/service/order.ts +459 -10
- package/vona/src/suite/a-commerce/modules/commerce-trade/test/cartOwnership.test.ts +2 -4
- package/vona/src/suite/a-commerce/modules/commerce-trade/test/checkoutReservation.test.ts +2 -4
- package/vona/src/suite/a-commerce/modules/commerce-trade/test/checkoutTransaction.test.ts +2 -4
- package/vona/src/suite/a-commerce/modules/commerce-trade/test/orderSnapshot.test.ts +2 -4
- package/vona/src/suite/a-commerce/modules/commerce-trade/test/paymentOutcome.test.ts +2 -4
- package/vona/src/suite/a-commerce/modules/commerce-trade/test/refundLifecycle.test.ts +337 -0
- package/vona/src/suite/a-commerce/modules/commerce-trade/test/reservationExpiry.test.ts +2 -3
- package/vona/src/suite/a-commerce/modules/commerce-trade/test/shipment.test.ts +2 -4
- package/vona/src/suite/a-commerce/modules/commerce-trade/test/shipmentRefundRace.test.ts +143 -0
- package/vona/src/suite/a-commerce/modules/commerce-trade/test/stockBalance.test.ts +2 -4
- package/vona/src/suite/a-commerce/modules/commerce-trade/test/stockReservation.test.ts +2 -4
- package/vona/src/suite/cabloy-basic/modules/basic-metrics/package.json +54 -0
- package/vona/src/suite/cabloy-basic/modules/basic-metrics/src/.metadata/index.ts +189 -0
- package/vona/src/suite/cabloy-basic/modules/basic-metrics/src/.metadata/this.ts +1 -0
- package/vona/src/suite/cabloy-basic/modules/basic-metrics/src/bean/ssrMenu.metrics.ts +23 -0
- package/vona/src/suite/cabloy-basic/modules/basic-metrics/src/controller/metrics.ts +21 -0
- package/vona/src/suite/cabloy-basic/modules/basic-metrics/src/dto/metricsQueue.ts +25 -0
- package/vona/src/suite/cabloy-basic/modules/basic-metrics/src/dto/metricsRuntime.ts +36 -0
- package/vona/src/suite/cabloy-basic/modules/basic-metrics/src/dto/metricsSnapshot.ts +21 -0
- package/vona/src/suite/cabloy-basic/modules/basic-metrics/src/index.ts +1 -0
- package/vona/src/suite/cabloy-basic/modules/basic-metrics/src/service/metrics.ts +11 -0
- package/vona/src/suite/cabloy-basic/modules/basic-metrics/tsconfig.build.json +11 -0
- package/vona/src/suite/cabloy-basic/modules/basic-metrics/tsconfig.json +5 -0
- package/vona/src/suite/cabloy-basic/package.json +1 -0
- package/vona/src/suite-vendor/a-vona/modules/a-core/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-instance/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-instance/src/bean/middlewareSystem.app.ts +2 -0
- package/vona/src/suite-vendor/a-vona/modules/a-instance/src/bean/middlewareSystem.instance.ts +2 -0
- package/vona/src/suite-vendor/a-vona/modules/a-metrics/package.json +62 -0
- package/vona/src/suite-vendor/a-vona/modules/a-metrics/src/.metadata/index.ts +178 -0
- package/vona/src/suite-vendor/a-vona/modules/a-metrics/src/.metadata/this.ts +1 -0
- package/vona/src/suite-vendor/a-vona/modules/a-metrics/src/bean/bean.metrics.ts +15 -0
- package/vona/src/suite-vendor/a-vona/modules/a-metrics/src/bean/middlewareSystem.health.ts +24 -0
- package/vona/src/suite-vendor/a-vona/modules/a-metrics/src/bean/middlewareSystem.metrics.ts +51 -0
- package/vona/src/suite-vendor/a-vona/modules/a-metrics/src/config/config.ts +75 -0
- package/vona/src/suite-vendor/a-vona/modules/a-metrics/src/index.ts +2 -0
- package/vona/src/suite-vendor/a-vona/modules/a-metrics/src/monkey.ts +27 -0
- package/vona/src/suite-vendor/a-vona/modules/a-metrics/src/service/health.ts +53 -0
- package/vona/src/suite-vendor/a-vona/modules/a-metrics/src/service/metrics.ts +222 -0
- package/vona/src/suite-vendor/a-vona/modules/a-metrics/src/service/metricsRuntime.ts +69 -0
- package/vona/src/suite-vendor/a-vona/modules/a-metrics/src/types/context.ts +9 -0
- package/vona/src/suite-vendor/a-vona/modules/a-metrics/src/types/index.ts +2 -0
- package/vona/src/suite-vendor/a-vona/modules/a-metrics/src/types/metrics.ts +50 -0
- package/vona/src/suite-vendor/a-vona/modules/a-metrics/tsconfig.build.json +11 -0
- package/vona/src/suite-vendor/a-vona/modules/a-metrics/tsconfig.json +7 -0
- package/vona/src/suite-vendor/a-vona/package.json +2 -1
- package/zova/pnpm-lock.yaml +104 -89
- package/zova/src/suite/a-commerce/modules/commerce-trade/cli/openapi.config.ts +4 -0
- package/zova/src/suite/a-commerce/modules/commerce-trade/src/.metadata/component/tableCellActionRefund.ts +31 -0
- package/zova/src/suite/a-commerce/modules/commerce-trade/src/.metadata/index.ts +35 -6
- package/zova/src/suite/a-commerce/modules/commerce-trade/src/api/commerceTradeOrder.ts +104 -0
- package/zova/src/suite/a-commerce/modules/commerce-trade/src/api/openapi/schemas.ts +16 -0
- package/zova/src/suite/a-commerce/modules/commerce-trade/src/api/openapi/types.ts +231 -2
- package/zova/src/suite/a-commerce/modules/commerce-trade/src/apiSchema/commerceTradeOrder.ts +20 -0
- package/zova/src/suite/a-commerce/modules/commerce-trade/src/bean/tableCell.actionRefund.tsx +40 -0
- package/zova/src/suite/a-commerce/modules/commerce-trade/src/component/tableCellActionRefund/controller.tsx +106 -0
- package/zova/src/suite/a-commerce/modules/commerce-trade/src/config/locale/en-us.ts +7 -0
- package/zova/src/suite/a-commerce/modules/commerce-trade/src/config/locale/zh-cn.ts +7 -0
- package/zova/src/suite/a-commerce/modules/commerce-trade/src/model/order.ts +45 -1
- package/zova/src/suite/a-commerce/modules/commerce-trade/src/model/orderMine.ts +15 -0
- package/zova/src/suite/a-commerce/modules/commerce-trade/src/page/order/controller.tsx +41 -0
- package/zova/src/suite/cabloy-basic/modules/basic-metrics/package.json +53 -0
- package/zova/src/suite/cabloy-basic/modules/basic-metrics/src/.metadata/index.ts +151 -0
- package/zova/src/suite/cabloy-basic/modules/basic-metrics/src/.metadata/page/dashboard.ts +9 -0
- package/zova/src/suite/cabloy-basic/modules/basic-metrics/src/.metadata/this.ts +1 -0
- package/zova/src/suite/cabloy-basic/modules/basic-metrics/src/api/basicMetricsMetrics.ts +41 -0
- package/zova/src/suite/cabloy-basic/modules/basic-metrics/src/index.ts +2 -0
- package/zova/src/suite/cabloy-basic/modules/basic-metrics/src/model/metrics.ts +16 -0
- package/zova/src/suite/cabloy-basic/modules/basic-metrics/src/page/dashboard/controller.tsx +95 -0
- package/zova/src/suite/cabloy-basic/modules/basic-metrics/src/routes.ts +5 -0
- package/zova/src/suite/cabloy-basic/modules/basic-metrics/tsconfig.build.json +13 -0
- package/zova/src/suite/cabloy-basic/modules/basic-metrics/tsconfig.json +5 -0
- package/zova/src/suite/cabloy-basic/package.json +1 -0
- package/vona/src/suite/a-commerce/modules/commerce-trade/test/testLock.ts +0 -11
package/.cabloy-version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
5.1.
|
|
1
|
+
5.1.119
|
|
@@ -42,8 +42,8 @@ jobs:
|
|
|
42
42
|
DATABASE_DEFAULT_CLIENT=pg npm run vona :bin:test -- commerce-trade/test/orderSnapshot.test.ts --flavor=normal
|
|
43
43
|
DATABASE_DEFAULT_CLIENT=pg npm run vona :bin:test -- commerce-trade/test/reservationExpiry.test.ts --flavor=normal
|
|
44
44
|
working-directory: vona
|
|
45
|
-
- name: run Commerce payment and
|
|
46
|
-
run: DATABASE_DEFAULT_CLIENT=pg npm run vona :bin:test -- commerce-trade/test/paymentOutcome.test.ts commerce-trade/test/reservationExpiry.test.ts commerce-trade/test/shipment.test.ts --flavor=normal
|
|
45
|
+
- name: run Commerce payment, shipment, and refund lifecycle gate
|
|
46
|
+
run: DATABASE_DEFAULT_CLIENT=pg npm run vona :bin:test -- commerce-trade/test/paymentOutcome.test.ts commerce-trade/test/reservationExpiry.test.ts commerce-trade/test/shipment.test.ts commerce-trade/test/refundLifecycle.test.ts commerce-trade/test/shipmentRefundRace.test.ts --flavor=normal
|
|
47
47
|
working-directory: vona
|
|
48
48
|
- run: DATABASE_DEFAULT_CLIENT=pg npm run test
|
|
49
49
|
working-directory: vona
|
|
@@ -27,8 +27,8 @@ jobs:
|
|
|
27
27
|
version: 11.5.2
|
|
28
28
|
- name: init
|
|
29
29
|
run: npm run init
|
|
30
|
-
- name: run Commerce payment and
|
|
31
|
-
run: DATABASE_DEFAULT_CLIENT=sqlite3 npm run vona :bin:test -- commerce-trade/test/paymentOutcome.test.ts commerce-trade/test/reservationExpiry.test.ts commerce-trade/test/shipment.test.ts --flavor=normal
|
|
30
|
+
- name: run Commerce payment, shipment, and refund lifecycle gate
|
|
31
|
+
run: DATABASE_DEFAULT_CLIENT=sqlite3 npm run vona :bin:test -- commerce-trade/test/paymentOutcome.test.ts commerce-trade/test/reservationExpiry.test.ts commerce-trade/test/shipment.test.ts commerce-trade/test/refundLifecycle.test.ts --flavor=normal
|
|
32
32
|
working-directory: vona
|
|
33
33
|
- run: DATABASE_DEFAULT_CLIENT=sqlite3 npm run test
|
|
34
34
|
working-directory: vona
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 5.1.119
|
|
4
|
+
|
|
5
|
+
### Improvements
|
|
6
|
+
|
|
7
|
+
- Update the upgrade script.
|
|
8
|
+
|
|
9
|
+
## 5.1.118
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
- Complete refund lifecycle coverage for commerce workflows.
|
|
14
|
+
- Add metrics support.
|
|
15
|
+
- Update application functionality and dependencies.
|
|
16
|
+
|
|
17
|
+
### Improvements
|
|
18
|
+
|
|
19
|
+
- Extract the scene test lock into `vona-mock`.
|
|
20
|
+
- Update the pnPM lockfile and environment configuration.
|
|
21
|
+
|
|
3
22
|
## 5.1.117
|
|
4
23
|
|
|
5
24
|
### Features
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# Metrics Guide
|
|
2
|
+
|
|
3
|
+
Cabloy Metrics uses OpenTelemetry Metrics (OTLP/HTTP) and is disabled by default. It does not publish an application `/metrics` endpoint. A Vona application normally has several independent workers, so an in-memory Prometheus registry on the shared application port would expose only one worker per scrape.
|
|
4
|
+
|
|
5
|
+
## Architecture
|
|
6
|
+
|
|
7
|
+
```text
|
|
8
|
+
Vona workers -> private OpenTelemetry Collector -> Prometheus exporter -> Prometheus
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Each worker exports its own metric stream. Preserve `service.instance.id` during ingestion and aggregate workers in PromQL with `sum`, `rate`, and `histogram_quantile` as appropriate.
|
|
12
|
+
|
|
13
|
+
Prometheus must scrape the Collector exporter on a private monitoring network. Do not route Collector OTLP or Prometheus exporter ports through Cloudflare or a public Nginx listener.
|
|
14
|
+
|
|
15
|
+
## Enablement
|
|
16
|
+
|
|
17
|
+
Inject configuration through the deployment platform. Do not commit credentials to environment files.
|
|
18
|
+
|
|
19
|
+
```text
|
|
20
|
+
METRICS_ENABLED=true
|
|
21
|
+
METRICS_SERVICE_NAME=cabloy
|
|
22
|
+
METRICS_OTLP_HTTP_URL=https://collector.internal/v1/metrics
|
|
23
|
+
METRICS_OTLP_HTTP_HEADERS=Authorization=Bearer <secret>
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Additional bounded settings are available:
|
|
27
|
+
|
|
28
|
+
- `METRICS_EXPORT_INTERVAL_MS` — export cadence, default `15000`
|
|
29
|
+
- `METRICS_EXPORT_TIMEOUT_MS` — one export timeout, default `10000`
|
|
30
|
+
- `METRICS_RUNTIME_INTERVAL_MS` — process/runtime snapshot cadence, default `15000`
|
|
31
|
+
- `METRICS_QUEUE_INTERVAL_MS` — reserved queue collection cadence, default `30000`
|
|
32
|
+
- `METRICS_SNAPSHOT_CACHE_MS` — Admin snapshot cache lifetime, default `5000`
|
|
33
|
+
- `METRICS_SHUTDOWN_TIMEOUT_MS` — bounded final flush time, default `4000`
|
|
34
|
+
|
|
35
|
+
Invalid numeric values are clamped to safe ranges. Disabling Metrics creates no exporter or background sampler.
|
|
36
|
+
|
|
37
|
+
## Initial metric families
|
|
38
|
+
|
|
39
|
+
- HTTP request count, duration histogram, and active-request count
|
|
40
|
+
- process uptime, RSS, heap, external memory, event-loop delay, and active Vona contexts
|
|
41
|
+
- metrics observation failure counters
|
|
42
|
+
|
|
43
|
+
HTTP labels are deliberately restricted to normalized method, route template, final status, and a bounded aborted marker. Literal URLs, query strings, hosts, tenant/instance names, users, request IDs, trace IDs, cookies, authorization values, Redis keys, queue job IDs, and job payloads are prohibited.
|
|
44
|
+
|
|
45
|
+
HTTP duration buckets in seconds are fixed as:
|
|
46
|
+
|
|
47
|
+
```text
|
|
48
|
+
0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10, 30
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Health probes
|
|
52
|
+
|
|
53
|
+
Use these private infrastructure endpoints:
|
|
54
|
+
|
|
55
|
+
| Endpoint | Meaning |
|
|
56
|
+
| --------------------- | --------------------------------------------------------- |
|
|
57
|
+
| `GET /health/startup` | Vona startup completed |
|
|
58
|
+
| `GET /health/live` | process is serving and not closing |
|
|
59
|
+
| `GET /health/ready` | startup complete plus bounded PostgreSQL and Redis checks |
|
|
60
|
+
|
|
61
|
+
They return only a small status response. Collector and queue-observation failures never make the readiness probe fail.
|
|
62
|
+
|
|
63
|
+
## Admin dashboard
|
|
64
|
+
|
|
65
|
+
The Basic Admin **Metrics** page is available only to `systemAdmin`. It shows a redacted, short-lived runtime snapshot and clear `disabled`, `fresh`, `stale`, or `unavailable` states. It is not a Prometheus query UI and never sends browser traffic to OTLP, Collector, or probe endpoints.
|
|
66
|
+
|
|
67
|
+
## Local example
|
|
68
|
+
|
|
69
|
+
A loopback-only local stack is available under the repository's `observability/README.md`:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
docker compose -f observability/docker-compose.metrics.yml up
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Use it for local validation only. Production networking, TLS/mTLS, secret injection, retention, dashboards, and alert routing are operator-owned.
|
|
@@ -525,6 +525,117 @@ test(
|
|
|
525
525
|
},
|
|
526
526
|
);
|
|
527
527
|
|
|
528
|
+
test(
|
|
529
|
+
'Phase 60: customer requests and operator executes a whole-order refund',
|
|
530
|
+
{ tag: ['@web', '@admin', '@flow', '@payment', '@refund'] },
|
|
531
|
+
async ({ browser, request }, testInfo) => {
|
|
532
|
+
test.setTimeout(120_000);
|
|
533
|
+
const { context, fixture, page, pageErrors } = await createAddressThroughCustomerPage(
|
|
534
|
+
browser,
|
|
535
|
+
request,
|
|
536
|
+
testInfo,
|
|
537
|
+
);
|
|
538
|
+
try {
|
|
539
|
+
await page.goto('/commerce', { waitUntil: 'load' });
|
|
540
|
+
await expect(page.locator('html')).toHaveAttribute('data-zova-hydrated', 'commerce');
|
|
541
|
+
await expect(page.getByRole('heading', { name: 'Commerce catalogue' })).toBeVisible();
|
|
542
|
+
const productLink = page.getByRole('link', { name: 'Pour-Over Coffee Set', exact: true });
|
|
543
|
+
await expect(productLink).toBeVisible();
|
|
544
|
+
await productLink.click();
|
|
545
|
+
await expect(page).toHaveURL(/\/commerce\/product\/\d+(?:\/|$)/);
|
|
546
|
+
await expect(page.getByRole('heading', { name: 'Pour-Over Coffee Set' })).toBeVisible();
|
|
547
|
+
await expect(page.getByText('COF-SET-01')).toBeVisible();
|
|
548
|
+
await expect(page.getByText('$45.99')).toBeVisible();
|
|
549
|
+
const addResponse = waitForApiResponse(page, 'POST', '/api/commerce/trade/cart/items');
|
|
550
|
+
await page.getByRole('button', { name: 'Add to cart', exact: true }).click();
|
|
551
|
+
await addResponse;
|
|
552
|
+
await page.getByRole('link', { name: /^Cart/ }).click();
|
|
553
|
+
await expect(page).toHaveURL(/\/commerce\/cart(?:\/|$)/);
|
|
554
|
+
await page.getByRole('link', { name: 'Checkout', exact: true }).click();
|
|
555
|
+
await expect(page).toHaveURL(/\/commerce\/checkout(?:\/|$)/);
|
|
556
|
+
const addressChoice = page.getByRole('radio', { name: new RegExp(fixture.recipientName) });
|
|
557
|
+
await addressChoice.check();
|
|
558
|
+
await page.getByRole('radio', { name: 'No coupon', exact: true }).check();
|
|
559
|
+
const checkoutResponse = waitForApiResponse(page, 'POST', '/api/commerce/trade/checkout');
|
|
560
|
+
await page.getByRole('button', { name: 'Create order', exact: true }).click();
|
|
561
|
+
const checkout = (await (await checkoutResponse).json()).data;
|
|
562
|
+
const paymentResponse = waitForApiResponse(
|
|
563
|
+
page,
|
|
564
|
+
'POST',
|
|
565
|
+
new RegExp(`/api/commerce/trade/payment/${checkout.paymentAttemptId}/outcome$`),
|
|
566
|
+
);
|
|
567
|
+
await page.getByRole('button', { name: 'Payment succeeded', exact: true }).click();
|
|
568
|
+
expect((await paymentResponse).ok()).toBeTruthy();
|
|
569
|
+
await expect(page.getByText('paid · $45.99')).toBeVisible();
|
|
570
|
+
|
|
571
|
+
const requestResponse = waitForApiResponse(
|
|
572
|
+
page,
|
|
573
|
+
'POST',
|
|
574
|
+
`/api/commerce/trade/order/${checkout.orderId}/requestRefund`,
|
|
575
|
+
);
|
|
576
|
+
await page.getByPlaceholder('Reason for refund').fill('E2E refund request');
|
|
577
|
+
await page.getByRole('button', { name: 'Request refund', exact: true }).click();
|
|
578
|
+
expect((await requestResponse).ok()).toBeTruthy();
|
|
579
|
+
await expect(page.getByText('refund_requested · $45.99')).toBeVisible();
|
|
580
|
+
|
|
581
|
+
const adminContext = await browser.newContext();
|
|
582
|
+
const adminPage = await adminContext.newPage();
|
|
583
|
+
const adminPageErrors = collectPageErrors(adminPage);
|
|
584
|
+
try {
|
|
585
|
+
await adminPage.setViewportSize({ width: 1440, height: 900 });
|
|
586
|
+
await login(adminPage, '/commerce-admin/', 'admin', '123456', 'commerceAdmin');
|
|
587
|
+
await adminPage.goto('/commerce-admin/rest/resource/commerce-trade%3Aorder', {
|
|
588
|
+
waitUntil: 'load',
|
|
589
|
+
});
|
|
590
|
+
await expect(adminPage.locator('html')).toHaveAttribute(
|
|
591
|
+
'data-zova-hydrated',
|
|
592
|
+
'commerceAdmin',
|
|
593
|
+
);
|
|
594
|
+
const orderRow = adminPage
|
|
595
|
+
.getByRole('cell', { name: String(checkout.orderId), exact: true })
|
|
596
|
+
.locator('..');
|
|
597
|
+
await expect(orderRow).toBeVisible();
|
|
598
|
+
await orderRow.getByPlaceholder('Decision reason').fill('E2E approval');
|
|
599
|
+
await orderRow.getByRole('checkbox').check();
|
|
600
|
+
const approveResponse = waitForApiResponse(
|
|
601
|
+
adminPage,
|
|
602
|
+
'POST',
|
|
603
|
+
`/api/commerce/trade/order/${checkout.orderId}/approveRefund`,
|
|
604
|
+
);
|
|
605
|
+
await orderRow.getByRole('button', { name: 'Approve refund', exact: true }).click();
|
|
606
|
+
expect((await approveResponse).ok()).toBeTruthy();
|
|
607
|
+
await expect(orderRow).toContainText('refund_approved');
|
|
608
|
+
const executeResponse = waitForApiResponse(
|
|
609
|
+
adminPage,
|
|
610
|
+
'POST',
|
|
611
|
+
`/api/commerce/trade/order/${checkout.orderId}/refundOutcome`,
|
|
612
|
+
);
|
|
613
|
+
await orderRow.getByRole('button', { name: 'Execute refund', exact: true }).click();
|
|
614
|
+
expect((await executeResponse).ok()).toBeTruthy();
|
|
615
|
+
await expect(orderRow).toContainText('refunded');
|
|
616
|
+
expect(adminPageErrors).toEqual([]);
|
|
617
|
+
} finally {
|
|
618
|
+
await adminContext.close().catch(() => {});
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
const refundedDetailResponse = waitForApiResponse(
|
|
622
|
+
page,
|
|
623
|
+
'GET',
|
|
624
|
+
new RegExp(`/api/commerce/trade/order/viewMine/${checkout.orderId}$`),
|
|
625
|
+
);
|
|
626
|
+
await page.reload({ waitUntil: 'load' });
|
|
627
|
+
await refundedDetailResponse;
|
|
628
|
+
await expect(page.getByText('refunded · $45.99')).toBeVisible();
|
|
629
|
+
await expect(page.getByText('1 × $45.99 = $45.99')).toBeVisible();
|
|
630
|
+
await expect(page.getByRole('heading', { name: 'Shipment' })).toHaveCount(0);
|
|
631
|
+
await expect(page.getByRole('alert')).toHaveCount(0);
|
|
632
|
+
expect(pageErrors).toEqual([]);
|
|
633
|
+
} finally {
|
|
634
|
+
await context.close().catch(() => {});
|
|
635
|
+
}
|
|
636
|
+
},
|
|
637
|
+
);
|
|
638
|
+
|
|
528
639
|
test(
|
|
529
640
|
'ATP-ADDR-01: systemAdmin inspects Address Resource without mutation controls',
|
|
530
641
|
{ tag: ['@admin', '@flow', '@address'] },
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Local Metrics Stack
|
|
2
|
+
|
|
3
|
+
Start the local OpenTelemetry Collector, Prometheus, and Grafana stack:
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
docker compose -f observability/docker-compose.metrics.yml up
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
Enable the application metric exporter only for local development:
|
|
10
|
+
|
|
11
|
+
```text
|
|
12
|
+
METRICS_ENABLED=true
|
|
13
|
+
METRICS_OTLP_HTTP_URL=http://127.0.0.1:4318/v1/metrics
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Prometheus scrapes the Collector at its private exporter endpoint. Cabloy deliberately does not expose an application `/metrics` endpoint: Vona workers export independently, and the Collector preserves each worker stream before PromQL aggregates them.
|
|
17
|
+
|
|
18
|
+
The published ports bind to loopback for development. Production must keep the Collector OTLP receiver and Prometheus exporter on a private workload/monitoring network, inject any OTLP credentials from the deployment secret store, and never proxy those paths through Cloudflare or the public Nginx listener.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
receivers:
|
|
2
|
+
otlp:
|
|
3
|
+
protocols:
|
|
4
|
+
http:
|
|
5
|
+
endpoint: 0.0.0.0:4318
|
|
6
|
+
|
|
7
|
+
processors:
|
|
8
|
+
memory_limiter:
|
|
9
|
+
check_interval: 1s
|
|
10
|
+
limit_mib: 256
|
|
11
|
+
batch:
|
|
12
|
+
timeout: 5s
|
|
13
|
+
transform/metrics_allowlist:
|
|
14
|
+
error_mode: ignore
|
|
15
|
+
metric_statements:
|
|
16
|
+
- context: datapoint
|
|
17
|
+
statements:
|
|
18
|
+
- delete_key(attributes, "http.target") where attributes["http.target"] != nil
|
|
19
|
+
- delete_key(attributes, "url.full") where attributes["url.full"] != nil
|
|
20
|
+
- delete_key(attributes, "user.id") where attributes["user.id"] != nil
|
|
21
|
+
- delete_key(attributes, "enduser.id") where attributes["enduser.id"] != nil
|
|
22
|
+
|
|
23
|
+
exporters:
|
|
24
|
+
prometheus:
|
|
25
|
+
endpoint: 0.0.0.0:9464
|
|
26
|
+
resource_to_telemetry_conversion:
|
|
27
|
+
enabled: true
|
|
28
|
+
|
|
29
|
+
service:
|
|
30
|
+
pipelines:
|
|
31
|
+
metrics:
|
|
32
|
+
receivers: [otlp]
|
|
33
|
+
processors: [memory_limiter, transform/metrics_allowlist, batch]
|
|
34
|
+
exporters: [prometheus]
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
services:
|
|
2
|
+
collector:
|
|
3
|
+
image: otel/opentelemetry-collector-contrib:0.134.1
|
|
4
|
+
command: [--config=/etc/otelcol-contrib/config.yaml]
|
|
5
|
+
ports:
|
|
6
|
+
- '127.0.0.1:4318:4318'
|
|
7
|
+
- '127.0.0.1:9464:9464'
|
|
8
|
+
volumes:
|
|
9
|
+
- ./collector/otel-collector.yaml:/etc/otelcol-contrib/config.yaml:ro
|
|
10
|
+
|
|
11
|
+
prometheus:
|
|
12
|
+
image: prom/prometheus:v3.5.0
|
|
13
|
+
depends_on: [collector]
|
|
14
|
+
ports:
|
|
15
|
+
- '127.0.0.1:9090:9090'
|
|
16
|
+
volumes:
|
|
17
|
+
- ./prometheus/prometheus.yaml:/etc/prometheus/prometheus.yml:ro
|
|
18
|
+
|
|
19
|
+
grafana:
|
|
20
|
+
image: grafana/grafana:12.1.0
|
|
21
|
+
depends_on: [prometheus]
|
|
22
|
+
ports:
|
|
23
|
+
- '127.0.0.1:3000:3000'
|
|
24
|
+
volumes:
|
|
25
|
+
- ./grafana/provisioning:/etc/grafana/provisioning:ro
|
package/package.json
CHANGED
package/scripts/upgrade.ts
CHANGED
package/vona/env/.env
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vona",
|
|
3
|
-
"version": "5.1.
|
|
3
|
+
"version": "5.1.82",
|
|
4
4
|
"gitHead": "a79189b882c17af5911573896a781bbb0046d37d",
|
|
5
5
|
"description": "Vona is an intuitive, elegant and powerful Node.js framework for rapidly developing enterprise applications of any size",
|
|
6
6
|
"keywords": [
|
|
@@ -21,7 +21,7 @@ async function _closeInner() {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
export function handleProcessWork() {
|
|
24
|
-
['SIGINT', 'SIGUSR2'].forEach(signal => {
|
|
24
|
+
['SIGINT', 'SIGTERM', 'SIGUSR2'].forEach(signal => {
|
|
25
25
|
process.on(signal, async () => {
|
|
26
26
|
if (__closing) return;
|
|
27
27
|
__closing = true;
|
|
@@ -4,6 +4,8 @@ import type { VonaApplication } from 'vona-core';
|
|
|
4
4
|
import { parseModuleInfo, ParseModuleNameLevelInit } from '@cabloy/module-info-pro';
|
|
5
5
|
import { useApp } from 'vona-core';
|
|
6
6
|
|
|
7
|
+
export { acquireTestLock } from './lib/testLock.ts';
|
|
8
|
+
|
|
7
9
|
const ParseModuleNameLevel = ParseModuleNameLevelInit + 2;
|
|
8
10
|
|
|
9
11
|
export const app: VonaApplication = useApp();
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
const pendingScenes = new Map<string, Promise<void>>();
|
|
2
|
+
|
|
3
|
+
export async function acquireTestLock(scene: string): Promise<() => void> {
|
|
4
|
+
const previous = pendingScenes.get(scene) ?? Promise.resolve();
|
|
5
|
+
let resolve: () => void;
|
|
6
|
+
const pending = new Promise<void>(pendingResolve => {
|
|
7
|
+
resolve = pendingResolve;
|
|
8
|
+
});
|
|
9
|
+
pendingScenes.set(scene, pending);
|
|
10
|
+
await previous;
|
|
11
|
+
|
|
12
|
+
let released = false;
|
|
13
|
+
return () => {
|
|
14
|
+
if (released) return;
|
|
15
|
+
released = true;
|
|
16
|
+
resolve!();
|
|
17
|
+
if (pendingScenes.get(scene) === pending) {
|
|
18
|
+
pendingScenes.delete(scene);
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
}
|