What's new in 0.2.1
0.2.1 only adds: no 0.2.0 export, signature or behavior changes, and no peer range moves.
Upgrade the ten packages above to ^0.2.1 to use the following.
Span attributes from every add-on
Section titled “Span attributes from every add-on”Each add-on publishes its decision as a context.items entry and now exports a builder
that turns it into flat attributes, {} when the behavior did not run and never a key.
AttributesBehavior of @nestjs-pipeline/opentelemetry runs the builders you choose
after the chain has finished and adds the result to the attributes TraceBehavior puts on
the span:
import { buildCacheAttributes } from '@nestjs-pipeline/cache';import { buildDeadLetterAttributes } from '@nestjs-pipeline/deadletter';import { buildFeatureFlagAttributes } from '@nestjs-pipeline/feature-flags';import { buildIdempotencyAttributes } from '@nestjs-pipeline/idempotency';import { AttributesBehavior, MetricsBehavior, TraceBehavior,} from '@nestjs-pipeline/opentelemetry';import { buildRateLimitAttributes } from '@nestjs-pipeline/rate-limit';
PipelineModule.forRoot({ globalBehaviors: { before: [ TraceBehavior, MetricsBehavior, [AttributesBehavior, { factories: [ buildFeatureFlagAttributes, // feature_flag.key, .enabled, .variant, .reason, .error_code buildCacheAttributes, // cache.hit buildIdempotencyAttributes, // idempotency.replayed, .ownership_lost buildRateLimitAttributes, // rate_limit.remaining_points buildDeadLetterAttributes, // dead_letter.captured ], }], ], },});The builders are plain data, not telemetry-only. Wrap one to rename an attribute, or use
it as audit metadata, with audit() declared outside the behavior it describes:
const cacheHit = (context: IPipelineContext) => { const { 'cache.hit': hit } = buildCacheAttributes(context); return hit === undefined ? {} : { 'app.cache_hit': hit };};
@UsePipeline( audit({ action: 'order.create', metadata: buildIdempotencyAttributes }), idempotent({ keyFactory }),)A ready-made permission digest
Section titled “A ready-made permission digest”@nestjs-pipeline/casl exports abilityDigest(context?): the SHA-256 of the caller’s
effective rules, with conditions already resolved against the principal, so any change
that can change a decision changes it. Use it as the scope of a principal-scoped cache
key, and requireAbilityDigest, which throws MissingAbilityError without an ability,
as an idempotency replay scope:
import { abilityDigest, getCaslPrincipal, requireAbilityDigest } from '@nestjs-pipeline/casl';
const overviewKey = createPartitionedCacheKeyFactory({ principal: (ctx) => getCaslPrincipal(ctx)?.id, scope: abilityDigest,});
@UsePipeline(idempotent({ keyFactory, replayScopeFactory: requireAbilityDigest }))CaslAuthorizer.dependsOnEntity(action, subject) tells a handler that a conditional rule
decides on entity attributes, so it reads the entity fresh instead of from a cache:
const refresh = this.authorizer.dependsOnEntity('read', 'User');const user = await this.users.find(new GetUserQuery({ userId }, { refresh }));Tenant with the purpose first
Section titled “Tenant with the purpose first”@cqrs-ddd/core adds requireTenant(purpose, source?). It returns the tenant from
source, or from the resolver registered with setTenantResolver, and throws
MissingTenantContextError when there is none:
import { requireTenant } from '@cqrs-ddd/core/application';
requireTenant('access token issuance'); // the current tenantrequireTenant('users.create idempotency key', ctx); // ctx.tenantId, else the current onerequireTenantId(source, purpose) keeps working and is deprecated: swap the arguments and
call requireTenant.