Page feedback
The documentation site records page-level reactions and optional comments in the
page-feedback-v1-{environment} Elasticsearch index. The API uses the feedback
identifier as the document _id, so later comment submissions and reaction
changes replace the same document.
The thumbs-up or thumbs-down selection writes a document with the reaction after
a short debounce, so quickly changing the selection records only the final
choice. The follow-up questionnaire writes the same document again with one or
more structured reasons, the reason-set version, and optional details. Readers
can select several reasons at once. One comment field applies to the whole
submission. It keeps its value when the reader switches between Yes and No.
Switching the reaction clears any selected reason that does not belong to the new
reason set. The browser stores that draft in sessionStorage for the current tab
and page so an accidental refresh can restore it, and clears it after a
successful submit. Submitting the questionnaire flushes any pending reaction write first.
This keeps abandoned questionnaires useful while ensuring the richer
submission wins.
Reasons are stored in the multi-valued reasons keyword field for filtering and
aggregation. reason_set_version identifies the questionnaire revision that
presented the option. Display labels may change without changing their stored value. Add a new
enum value when an option's meaning changes, and retain retired values so older
clients and historical documents remain valid.
Reason set version 2 presents these positive reasons in display order:
solvedProblem, easyToUnderstand, accurate, helpfulExamples,
easyToFind, and anotherReason. The negative reasons are outOfDate,
hardToUnderstand, inaccurate, codeSampleErrors, missingInformation, and
anotherReason. API pages use the same stored values
and reason_set_version. They pass surface="api" so labels and descriptions
talk about the spec and examples instead of a product how-to.
PageFeedbackMapping defines the mapping with Elastic.Mapping attributes.
During API startup, PageFeedbackBootstrapService uses
Elastic.Ingest.Elasticsearch to create or update environment-specific
component and index templates. The generated context uses the API assembly
version as its mapping version. Bootstrap skips unchanged mapping hashes and
prevents an older API task from replacing templates installed by a newer task
during a rolling deployment.
The bootstrap service and runtime gateway share one ingest channel. Feedback
upserts use DirectWriteAsync and await the bulk item result before the API
responds. They do not use the channel's retry overload because the browser owns
the bounded retry UX. The first successful feedback write creates the concrete
index from the templates.
The generated mapping disables dynamic field mapping. Fields that are not part
of PageFeedbackDocument remain unindexed instead of changing the schema.
Bootstrap failures are logged and ignored in dev so the local documentation
server can run without Elasticsearch. They prevent startup in staging, edge,
and prod.
Runtime credentials require permission to manage component and index templates, create the environment's index, and write and delete its documents.
Template updates do not modify existing indices. For a mapping change, increment the schema version in the index name, deploy the new template, and migrate any documents that must be retained. Mixed task versions then write to separate versioned indices during a rolling deployment. The assembly mapping version is an additional downgrade guard for templates that retain the same schema-versioned name; it does not replace index versioning.
The web ACL can require a valid aws-waf-token cookie on /docs/_api/v1/page-feedback.
A request without one gets an HTTP 202 challenge that a plain fetch() can't solve.
When waf_integration_url is set on an environment in config/assembler.yml, the page
<head> loads the AWS WAF JavaScript SDK from that URL (Layout/_Head.cshtml). The SDK
fetches the token in the background and stores it in the cookie. Take the URL from the
AWS WAF console's Application integration page. Use the CloudFront (edge) script URL,
not the regional one. It belongs to the AWS account and is not tied to a web ACL, so
environments in the same account share it.
The URL is the jsapi.js script from the CAPTCHA integration tab. The
Intelligent threat integration tab only lists web ACLs that use the ACFP or ATP rule
groups or targeted Bot Control, and the docs web ACLs use common Bot Control. jsapi.js
loads challenge.js, which defines AwsWafIntegration a little later, so
waitForWafSdk in Assets/waf.ts waits up to 5 seconds for the global.
The <page-feedback> component sends the PUT through AwsWafIntegration.fetch. If the
SDK doesn't appear in time, the submit shows the normal error state instead of sending
an unchallenged request.
Browser telemetry waits for the token before each export to /docs/_api/v1/o/{t,l,m},
so the web ACL can challenge those paths too. If the token can't be fetched within 5
seconds, exports go out without it. The telemetry SDK still starts at page load, so
instrumentation isn't delayed.
With no URL (local dev, isolated, codex, air-gapped) no script loads, page feedback uses
plain fetch, and telemetry doesn't wait.
External callers of other /docs/_api/v1/* paths can't run the SDK and stay on IP
rate limits.