diff --git a/config/legacy-url-mappings.yml b/config/legacy-url-mappings.yml index 5de72d257..260102550 100644 --- a/config/legacy-url-mappings.yml +++ b/config/legacy-url-mappings.yml @@ -1,82 +1,228 @@ ############################################# # This file defines the legacy URL mappings for the documentation site. # It maps current documentation pages to older versions to ensure users can find the content they need. -# TODO: Refactor the model, for now we just use the first element as current version +# Upon creation of version selection dropdowns, this data is combined with the current version of its product. ############################################# -stack: &stack [ '9.0+', '8.19', '8.18', '8.17', '8.16', '8.15', '8.14', '8.13', '8.12', '8.11', '8.10', '8.9', '8.8', '8.7', '8.6', '8.5', '8.4', '8.3', '8.2', '8.1', '8.0', '7.17' ] +stack: &stack [ '8.19', '8.18', '8.17', '8.16', '8.15', '8.14', '8.13', '8.12', '8.11', '8.10', '8.9', '8.8', '8.7', '8.6', '8.5', '8.4', '8.3', '8.2', '8.1', '8.0', '7.17' ] mappings: - en/apm/agent/android/: [ '1.2.0' , '0.x' ] - en/apm/agent/dotnet/: [ '1.34.0' ] - en/apm/agent/go/: [ '2.7.1', '1.x', '0.5' ] - en/apm/agent/java/: ['1.54.0', '0.7', '0.6'] - en/apm/agent/nodejs/: [ '4.x', '3.x', '2.x', '1.x' ] - en/apm/agent/php/: [ '1.15.1', '1.x' ] - en/apm/agent/python/: [ '6.24.0', '5.x', '4.x', '3.x', '2.x', '1.x' ] - en/apm/agent/ruby/: [ '4.8.0', '3.x', '2.x', '1.x' ] - en/apm/agent/rum-js/: [ '5.17.0', '4.x', '3.x', '2.x', '1.x', '0.x' ] - en/apm/agent/swift/: [ '1.2.1', '0.x' ] - en/apm/attacher/: [ '1.1.3' ] - en/apm/lambda/: [ '1.5.8' ] - en/beats/auditbeat/: *stack - en/beats/devguide/: *stack - en/beats/filebeat/: *stack - en/beats/functionbeat/: *stack - en/beats/heartbeat/: *stack - en/beats/journalbeat/: *stack - en/beats/libbeat/: *stack - en/beats/loggingplugin/: *stack - en/beats/metricbeat/: *stack - en/beats/packetbeat/: *stack - en/beats/topbeat/: *stack - en/beats/winlogbeat/: *stack - en/cloud-heroku/: [] - en/cloud-on-k8s/: [ '3.0+', '2.16', '2.15', '2.14', '2.13', '2.12', '2.11', '2.10', '2.9', '2.8', '2.7', '2.6', '2.5', '2.4', '2.3', '2.2', '2.1', '2.0', '1.9', '1.8', '1.7', '1.6', '1.5', '1.4', '1.3', '1.2', '1.1', '1.0' ] - en/cloud/: [] - en/cloud-enterprise/: [ '4.0', '3.8', '3.7', '3.6', '3.5', '3.4', '3.3', '3.2', '3.1', '3.0', '2.13', '2.12', '2.11', '2.10', '2.9', '2.8', '2.7', '2.6', '2.5', '2.4', '2.3', '2.2', '2.1', '2.0', '1.1', '1.0' ] - en/ecctl/: [ '1.14+', '1.13', '1.12', '1.11', '1.10', '1.9', '1.8', '1.7', '1.6', '1.5', '1.4', '1.3', '1.2', '1.1', '1.0' ] - en/ecs-logging/: [] - en/ecs-logging/dotnet/: [ '8.18.1' ] - en/ecs-logging/go-logrus/: [ '1.0.0' ] - en/ecs-logging/go-zap/: [ '1.0.3' ] - en/ecs-logging/go-zerolog/: [ '0.2.0' ] - en/ecs-logging/java/: [ '1.7.0', '0.x' ] - en/ecs-logging/nodejs/: [ '1.5.3' ] - en/ecs-logging/overview/: [] - en/ecs-logging/php/: [ '2.0.0' ] - en/ecs-logging/python/: [ '2.2.0' ] - en/ecs-logging/ruby/: ['1.0.0'] - en/ecs/: [ '9.0+', '8.18', '8.17', '8.16', '8.15', '8.14', '8.13', '8.12', '8.11', '8.10', '8.9', '8.8', '8.7', '8.6', '8.5', '8.4', '8.3', '8.2', '8.1', '8.0', '1.12', '1.11', '1.10', '1.9', '1.8', '1.7', '1.6', '1.5', '1.4', '1.3', '1.2', '1.1', '1.0' ] - en/elastic-stack-glossary/: [] - en/elastic-stack/: *stack - en/elasticsearch/client/curator/: [ '8.0.21', '7.0', '6.0', '5.8', '5.7', '5.6', '5.5', '5.4', '5.3', '5.2', '5.1', '5.0', '4.3', '4.2', '4.1', '4.0', '3.5', '3.4', '3.3' ] - en/elasticsearch/client/eland/: [ '9.0.1' ] - en/elasticsearch/client/go-api/: *stack - en/elasticsearch/client/java-api-client/: *stack - en/elasticsearch/client/javascript-api/: *stack - en/elasticsearch/client/net-api/: *stack - en/elasticsearch/client/php-api/: *stack - en/elasticsearch/client/python-api/: *stack - en/elasticsearch/client/ruby-api/: *stack - en/elasticsearch/client/rust-api/: *stack - en/elasticsearch/hadoop/: *stack - en/elasticsearch/painless/: *stack - en/elasticsearch/plugins/: *stack - en/elasticsearch/reference/: *stack - en/esf/: ['1.20.1'] - en/fleet/: *stack - en/ingest-overview/: [] - en/ingest/: *stack - en/integrations-developer/: [] - en/integrations/: [] - en/kibana/: *stack - en/logstash-versioned-plugins/: [] - en/logstash/: *stack - en/machine-learning/: *stack - en/observability/: *stack - en/reference-architectures/: [] - en/search-ui/: ['1.24.0'] - en/security/: *stack - en/serverless/: [] - en/starting-with-the-elasticsearch-platform-and-its-solutions/: *stack + en/apm/agent/android/: + product: apm-agent-android + legacy_versions: [ '0.x' ] + en/apm/agent/dotnet/: + product: apm-agent-dotnet + legacy_versions: [ '1.33.0' ] + en/apm/agent/go/: + product: apm-agent-go + legacy_versions: [ '1.x', '0.5' ] + en/apm/agent/java/: + product: apm-agent-java + legacy_versions: [ '1.54.0', '0.7', '0.6'] + en/apm/agent/nodejs/: + product: apm-agent-node + legacy_versions: [ '4.x', '3.x', '2.x', '1.x' ] + en/apm/agent/php/: + product: apm-agent-php + legacy_versions: [ '1.x' ] + en/apm/agent/python/: + product: apm-agent-python + legacy_versions: [ '5.x', '4.x', '3.x', '2.x', '1.x' ] + en/apm/agent/ruby/: + product: apm-agent-ruby + legacy_versions: [ '3.x', '2.x', '1.x' ] + en/apm/agent/rum-js/: + product: apm-agent-rum-js + legacy_versions: [ '4.x', '3.x', '2.x', '1.x', '0.x' ] + en/apm/agent/swift/: + product: apm-agent-ios + legacy_versions: [ '1.2.1', '0.x' ] + en/apm/attacher/: + product: apm-k8s-attacher + legacy_versions: [] + en/apm/lambda/: + product: apm-aws-lambda + legacy_versions: [] + en/beats/auditbeat/: + product: auditbeat + legacy_versions: *stack + en/beats/devguide/: + product: beats + legacy_versions: *stack + en/beats/filebeat/: + product: filebeat + legacy_versions: *stack + en/beats/functionbeat/: + product: beats + legacy_versions: *stack + en/beats/heartbeat/: + product: heartbeat + legacy_versions: *stack + en/beats/journalbeat/: + product: beats + legacy_versions: *stack + en/beats/libbeat/: + product: beats + legacy_versions: *stack + en/beats/loggingplugin/: + product: beats + legacy_versions: *stack + en/beats/metricbeat/: + product: metricbeat + legacy_versions: *stack + en/beats/packetbeat/: + product: packetbeat + legacy_versions: *stack + en/beats/topbeat/: + product: beats + legacy_versions: *stack + en/beats/winlogbeat/: + product: winlogbeat + legacy_versions: *stack + en/cloud-heroku/: + product: cloud-hosted + legacy_versions: [] + en/cloud-on-k8s/: + product: cloud-kubernetes + legacy_versions: [ '2.16', '2.15', '2.14', '2.13', '2.12', '2.11', '2.10', '2.9', '2.8', '2.7', '2.6', '2.5', '2.4', '2.3', '2.2', '2.1', '2.0', '1.9', '1.8', '1.7', '1.6', '1.5', '1.4', '1.3', '1.2', '1.1', '1.0' ] + en/cloud/: + product: cloud-hosted + legacy_versions: [] + en/cloud-enterprise/: + product: cloud-enterprise + legacy_versions: [ '3.8', '3.7', '3.6', '3.5', '3.4', '3.3', '3.2', '3.1', '3.0', '2.13', '2.12', '2.11', '2.10', '2.9', '2.8', '2.7', '2.6', '2.5', '2.4', '2.3', '2.2', '2.1', '2.0', '1.1', '1.0' ] + en/ecctl/: + product: cloud-control-ecctl + legacy_versions: [ '1.13', '1.12', '1.11', '1.10', '1.9', '1.8', '1.7', '1.6', '1.5', '1.4', '1.3', '1.2', '1.1', '1.0' ] + en/ecs-logging/: + product: ecs-logging + legacy_versions: [] + en/ecs-logging/dotnet/: + product: ecs-dotnet + legacy_versions: [] + en/ecs-logging/go-logrus/: + product: ecs-logging-go-logrus + legacy_versions: [] + en/ecs-logging/go-zap/: + product: ecs-logging-go-zap + legacy_versions: [] + en/ecs-logging/go-zerolog/: + product: ecs-logging-go-zerolog + legacy_versions: [] + en/ecs-logging/java/: + product: ecs-logging-java + legacy_versions: ['0.x'] + en/ecs-logging/nodejs/: + product: ecs-logging-nodejs + legacy_versions: [] + en/ecs-logging/overview/: + product: ecs-logging + legacy_versions: [] + en/ecs-logging/php/: + product: ecs-logging-php + legacy_versions: [] + en/ecs-logging/python/: + product: ecs-logging-python + legacy_versions: [ '2.2.0' ] + en/ecs-logging/ruby/: + product: ecs-logging-ruby + legacy_versions: [] + en/ecs/: + product: ecs + legacy_versions: [ '8.18', '8.17', '8.16', '8.15', '8.14', '8.13', '8.12', '8.11', '8.10', '8.9', '8.8', '8.7', '8.6', '8.5', '8.4', '8.3', '8.2', '8.1', '8.0', '1.12', '1.11', '1.10', '1.9', '1.8', '1.7', '1.6', '1.5', '1.4', '1.3', '1.2', '1.1', '1.0' ] + en/elastic-stack-glossary/: + product: elastic-stack + legacy_versions: [] + en/elastic-stack/: + product: elastic-stack + legacy_versions: *stack + en/elasticsearch/client/curator/: + product: curator + legacy_versions: [ '7.0', '6.0', '5.8', '5.7', '5.6', '5.5', '5.4', '5.3', '5.2', '5.1', '5.0', '4.3', '4.2', '4.1', '4.0', '3.5', '3.4', '3.3' ] + en/elasticsearch/client/eland/: + product: eland + legacy_versions: [] + en/elasticsearch/client/go-api/: + product: elasticsearch + legacy_versions: *stack + en/elasticsearch/client/java-api-client/: + product: elasticsearch + legacy_versions: *stack + en/elasticsearch/client/javascript-api/: + product: elasticsearch + legacy_versions: *stack + en/elasticsearch/client/net-api/: + product: elasticsearch + legacy_versions: *stack + en/elasticsearch/client/php-api/: + product: elasticsearch + legacy_versions: *stack + en/elasticsearch/client/python-api/: + product: elasticsearch + legacy_versions: *stack + en/elasticsearch/client/ruby-api/: + product: elasticsearch + legacy_versions: *stack + en/elasticsearch/client/rust-api/: + product: elasticsearch + legacy_versions: *stack + en/elasticsearch/hadoop/: + product: elasticsearch + legacy_versions: *stack + en/elasticsearch/painless/: + product: elasticsearch + legacy_versions: *stack + en/elasticsearch/plugins/: + product: elasticsearch + legacy_versions: *stack + en/elasticsearch/reference/: + product: elasticsearch + legacy_versions: *stack + en/esf/: + product: elastic-serverless-forwarder + legacy_versions: [] + en/fleet/: + product: elastic-agent + legacy_versions: *stack + en/ingest-overview/: + product: elastic-stack + legacy_versions: [] + en/ingest/: + product: elastic-stack + legacy_versions: *stack + en/integrations-developer/: + product: integrations + legacy_versions: [] + en/integrations/: + product: integrations + legacy_versions: [] + en/kibana/: + product: kibana + legacy_versions: *stack + en/logstash-versioned-plugins/: + product: logstash + legacy_versions: [] + en/logstash/: + product: logstash + legacy_versions: *stack + en/machine-learning/: + product: elasticsearch + legacy_versions: *stack + en/observability/: + product: observability + legacy_versions: *stack + en/reference-architectures/: + product: elasticsearch + legacy_versions: [] + en/search-ui/: + product: search-ui + legacy_versions: ['1.24.1', '1.24.0'] + en/security/: + product: security + legacy_versions: *stack + en/serverless/: + product: cloud-serverless + legacy_versions: [] + en/starting-with-the-elasticsearch-platform-and-its-solutions/: + product: elastic-stack + legacy_versions: *stack diff --git a/config/products.yml b/config/products.yml new file mode 100644 index 000000000..803329251 --- /dev/null +++ b/config/products.yml @@ -0,0 +1,214 @@ +products: + apm: + display: 'APM' + versioning: 'stack' + apm-agent: + display: 'APM Agent' + versioning: 'stack' + apm-agent-android: + display: 'APM Agent for Android' + versioning: 'apm_agent_android' + apm-agent-dotnet: + display: 'APM .NET Agent' + versioning: 'apm_agent_dotnet' + apm-agent-go: + display: 'APM Go Agent' + versioning: 'apm_agent_go' + apm-agent-ios: + display: 'APM Agent for iOS' + versioning: 'apm_agent_ios' + apm-agent-java: + display: 'APM Java Agent' + versioning: 'apm_agent_java' + apm-agent-node: + display: 'APM Node.js Agent' + versioning: 'apm_agent_node' + apm-agent-php: + display: 'APM PHP Agent' + versioning: 'apm_agent_php' + apm-agent-python: + display: 'APM Python Agent' + versioning: 'apm_agent_python' + apm-agent-ruby: + display: 'APM Ruby Agent' + versioning: 'apm_agent_ruby' + apm-agent-rum-js: + display: 'APM RUM JavaScript Agent' + versioning: 'apm_agent_rum' + apm-k8s-attacher: + display: 'APM Attacher for Kubernetes' + versioning: 'apm_attacher' + apm-aws-lambda: + display: 'APM AWS Lambda extension' + versioning: 'apm_lambda' + apm-server: + display: 'APM Server' + versioning: 'stack' + auditbeat: + display: 'Auditbeat' + versioning: 'stack' + beats: + display: 'Beats' + versioning: 'stack' + cloud-control-ecctl: + display: 'Elastic Cloud Control' + versioning: 'ecctl' + cloud-enterprise: + display: 'Elastic Cloud Enterprise' + versioning: 'ece' + cloud-hosted: + display: 'Elastic Cloud Hosted' + versioning: 'ech' + cloud-kubernetes: + display: 'Elastic Cloud on Kubernetes' + versioning: 'eck' + cloud-serverless: + display: 'Elastic Cloud Serverless' + versioning: 'serverless' + cloud-terraform: + display: 'Elastic Cloud Terraform Provider' + versioning: 'cloud_terraform' + curator: + display: 'Elasticsearch Curator' + versioning: 'curator' + ecs: + display: 'Elastic Common Schema (ECS)' + versioning: 'stack' + ecs-logging: + display: 'ECS Logging' + versioning: 'stack' + ecs-dotnet: + display: 'Elastic Common Schema (ECS) support for .NET' + versioning: 'ecs_logging_dotnet' + ecs-logging-go-logrus: + display: 'Elastic Common Schema (ECS) support for Logrus' + versioning: 'ecs_logging_go_logrus' + ecs-logging-go-zap: + display: 'Elastic Common Schema (ECS) support for uber_go/zap logger' + versioning: 'ecs_logging_go_zap' + ecs-logging-go-zerolog: + display: 'Elastic Common Schema (ECS) support for zerolog' + versioning: 'ecs_logging_go_zerolog' + ecs-logging-java: + display: 'Elastic Common Schema (ECS) support for Java applications' + versioning: 'ecs_logging_java' + ecs-logging-nodejs: + display: 'Elastic Common Schema (ECS) support for Node.js' + versioning: 'ecs_logging_nodejs' + ecs-logging-php: + display: 'Elastic Common Schema (ECS) support for PHP' + versioning: 'ecs_logging_php' + ecs-logging-python: + display: 'Elastic Common Schema (ECS) support for Python' + versioning: 'ecs_logging_python' + ecs-logging-ruby: + display: 'Elastic Common Schema (ECS) support for Ruby' + versioning: 'ecs_logging_ruby' + edot-cf: + display: 'EDOT Cloud Forwarder' + versioning: 'stack' + edot-sdk: + display: 'Elastic Distribution of OpenTelemetry SDK' + versioning: 'stack' + edot-collector: + display: 'Elastic Distribution of OpenTelemetry Collector' + versioning: 'stack' + edot-ios: + display: 'Elastic Distribution of OpenTelemetry iOS' + versioning: 'edot_ios' + edot-android: + display: 'Elastic Distribution of OpenTelemetry Android' + versioning: 'edot_android' + edot-dotnet: + display: 'Elastic Distribution of OpenTelemetry .NET' + versioning: 'edot_dotnet' + edot-java: + display: 'Elastic Distribution of OpenTelemetry Java' + versioning: 'edot_java' + edot-node: + display: 'Elastic Distribution of OpenTelemetry Node' + versioning: 'edot_node' + edot-php: + display: 'Elastic Distribution of OpenTelemetry PHP' + versioning: 'edot_php' + edot-python: + display: 'Elastic Distribution of OpenTelemetry Python' + versioning: 'edot_python' + edot-cf-aws: + display: 'EDOT Cloud Forwarder for AWS' + versioning: 'edot_cf_aws' + eland: + display: 'Eland' + versioning: 'stack' + elastic-agent: + display: 'Elastic Agent' + versioning: 'stack' + elastic-serverless-forwarder: + display: 'Elastic Serverless Forwarder' + versioning: 'esf' + elastic-stack: + display: 'Elastic Stack' + versioning: 'stack' + elasticsearch: + display: 'Elasticsearch' + versioning: 'stack' + elasticsearch-client: + display: 'Elasticsearch Client' + versioning: 'stack' + ess: + display: 'Elastic Cloud Hosted' + versioning: 'all' + filebeat: + display: 'Filebeat' + versioning: 'stack' + fleet: + display: 'Fleet' + versioning: 'stack' + heartbeat: + display: 'Heartbeat' + versioning: 'stack' + integrations: + display: 'Elastic integrations' + versioning: 'stack' + kibana: + display: 'Kibana' + versioning: 'stack' + logstash: + display: 'Logstash' + versioning: 'stack' + machine-learning: + display: 'Machine Learning' + versioning: 'stack' + metricbeat: + display: 'Metricbeat' + versioning: 'stack' + observability: + display: 'Elastic Observability' + versioning: 'stack' + packetbeat: + display: 'Packetbeat' + versioning: 'stack' + painless: + display: 'Painless' + versioning: 'stack' + search-ui: + display: 'Search UI' + versioning: 'stack' + security: + display: 'Elastic Security' + versioning: 'stack' + self: + display: 'Self-managed Elastic' + versioning: 'stack' + serverless-elasticsearch: + display: 'Elasticsearch Serverless' + versioning: 'all' + serverless-observability: + display: 'Elastic Observability Serverless' + versioning: 'all' + serverless-security: + display: 'Elastic Security Serverless' + versioning: 'all' + winlogbeat: + display: 'Winlogbeat' + versioning: 'stack' \ No newline at end of file diff --git a/config/versions.yml b/config/versions.yml index e6e11392d..e17bf9f52 100644 --- a/config/versions.yml +++ b/config/versions.yml @@ -36,15 +36,18 @@ versioning_systems: security: *all # APM agents - # apm_agent_android: - # base: 1.0 - # current: 1.0.0 + apm_agent_android: + base: 1.0 + current: 1.2.0 apm_agent_dotnet: base: 1.0 current: 1.34.1 apm_agent_go: base: 2.0 current: 2.7.1 + apm_agent_ios: + base: 1.0 + current: 1.3.0 apm_agent_java: base: 1.0 current: 1.55.0 @@ -63,6 +66,12 @@ versioning_systems: apm_agent_rum: base: 5.0 current: 5.17.0 + apm_attacher: + base: 1.0 + current: 1.1.3 + apm_lambda: + base: 1.0 + current: 1.5.8 # EDOTs edot_collector: @@ -92,3 +101,43 @@ versioning_systems: edot_cf_aws: base: 0.1 current: 0.2.0 + + # Logging + ecs_logging_dotnet: + base: 8.0 + current: 8.18.1 + ecs_logging_go_logrus: + base: 1.0 + current: 1.0.0 + ecs_logging_go_zap: + base: 1.0 + current: 1.0.3 + ecs_logging_go_zerolog: + base: 0.2 + current: 0.2.0 + ecs_logging_java: + base: 1.0 + current: 1.7.0 + ecs_logging_nodejs: + base: 1.0 + current: 1.5.3 + ecs_logging_php: + base: 2.0 + current: 2.0.0 + ecs_logging_python: + base: 2.0 + current: 2.2.0 + ecs_logging_ruby: + base: 1.0 + current: 1.0.0 + esf: + base: 1.0 + current: 1.21.0 + search_ui: + base: 1.0 + current: 1.24.2 + +# Other + cloud_terraform: + base: 0.11 + current: 0.11.17 diff --git a/docs-builder.sln b/docs-builder.sln index 7af8a0bd8..0824dc491 100644 --- a/docs-builder.sln +++ b/docs-builder.sln @@ -121,6 +121,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{6FAB56 config\assembler.yml = config\assembler.yml config\legacy-url-mappings.yml = config\legacy-url-mappings.yml config\navigation.yml = config\navigation.yml + config\products.yml = config\products.yml EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests-integration", "tests-integration", "{BCAD38D5-6C83-46E2-8398-4BE463931098}" diff --git a/docs/_docset.yml b/docs/_docset.yml index bfbd5018a..1a942c22d 100644 --- a/docs/_docset.yml +++ b/docs/_docset.yml @@ -76,6 +76,7 @@ toc: - file: navigation.md - file: versions.md - file: legacy-url-mappings.md + - file: products.md - folder: content-set children: - file: index.md diff --git a/docs/configure/site/index.md b/docs/configure/site/index.md index 1728bad3e..e255e46a2 100644 --- a/docs/configure/site/index.md +++ b/docs/configure/site/index.md @@ -10,6 +10,7 @@ Configure the documentation site in these files: - [](./navigation.md) - global navigation index - [](./versions.md) - global versioning schemes - [](./legacy-url-mappings.md) - supported legacy version +- [](./products.md) - company product metadata ## Redirects diff --git a/docs/configure/site/legacy-url-mappings.md b/docs/configure/site/legacy-url-mappings.md index 11ab0a221..0d72bb433 100644 --- a/docs/configure/site/legacy-url-mappings.md +++ b/docs/configure/site/legacy-url-mappings.md @@ -5,7 +5,9 @@ This [`legacy-url-mappings.yml`](https://github.com/elastic/docs-builder/blob/ma This example maps documentation that references `elastic.co/guide/en/elasticsearch/reference/ to Elastic Stack versioned URL paths: ```yml -en/elasticsearch/reference/: *stack +en/elasticsearch/reference/: + product: elastic-stack + legacy_versions: *stack ``` ## Structure @@ -14,11 +16,7 @@ en/elasticsearch/reference/: *stack : Defines a reusable list of version strings for "stack" projects, e.g., [ '9.0+', '8.18', ... ]. `mappings` -: A YAML mapping where each key is a legacy documentation URL path (like `en/apm/agent/java/`) and the value is a list of asciidoc versions that exist for that path. - -:::{important} -The first version in the `mappings` list is treated as the "current" version in documentation version dropdown. -::: - -## Example entry +: A YAML mapping where each key is a legacy documentation URL path (like `en/apm/agent/java/`), and each value is a mapping with: +* `product`: Specifies the product or project type (e.g., `elastic-stack`, `eck`, `ece`, `self-managed`, etc.). Products must be defined in [`products.yml`](https://github.com/elastic/docs-builder/blob/main/config/products.yml). See [products.yml](./products.md) for more information. +* `legacy_versions`: A list of version strings that correspond to the available asciidoc pages. diff --git a/docs/configure/site/products.md b/docs/configure/site/products.md new file mode 100644 index 000000000..f7cef3f73 --- /dev/null +++ b/docs/configure/site/products.md @@ -0,0 +1,45 @@ +# `products.yml` + +The [`products.yml`](https://github.com/elastic/docs-builder/blob/main/config/products.yml) file specifies metadata regarding the projects in the organization that use the V3 docs system. + +```yml +products: + apm_agent_dotnet: + display: 'APM .NET Agent' + versioning: 'apm_agent_dotnet' + edot_collector: + display: 'Elastic Distribution of OpenTelemetry Collector' + versioning: 'stack' +#... +``` + +## Structure + +`products` +: A YAML mapping where each key is an Elastic product. +* `display`: A friendly name for the product. +* `versioning`: The versioning system used by the project. The value for this field must match one of the versioning systems defined in [`versions.yml`](https://github.com/elastic/docs-builder/blob/main/config/versions.yml) + + + +## Substitutions + +Writing `{{ product. }}` renders the friendly name of the product in the documentation. For example: + +| Substitution | Result | +|---------------------------------|---| +| `{{ product.apm_agent_dotnet }}` |{{ product.apm_agent_dotnet }} | +| `{{ product.edot_collector }}` | {{ product.edot_collector }} | + +You can also use the shorthand notation `{{ . }}`. For example: + +| Substitution | Result | +|---------------------------------|---| +| `{{ .apm_agent_dotnet }}` |{{ .apm_agent_dotnet }} | +| `{{ .edot_collector }}` | {{ .edot_collector }} | + + +## See also + +[](./versions.md) +[](./legacy-url-mappings.md) diff --git a/docs/syntax/substitutions.md b/docs/syntax/substitutions.md index 3e7624e69..45f5315f5 100644 --- a/docs/syntax/substitutions.md +++ b/docs/syntax/substitutions.md @@ -36,11 +36,13 @@ To use the variables in your files, surround them in curly brackets (`{{variable Here are some variable substitutions: -| Variable | Defined in | -|-----------------------|--------------| -| {{frontmatter_key}} | Front Matter | -| {{a-key-with-dashes}} | Front Matter | -| {{a-global-variable}} | `docset.yml` | +| Variable | Defined in | +|-----------------------|----------------| +| {{frontmatter_key}} | Front Matter | +| {{a-key-with-dashes}} | Front Matter | +| {{a-global-variable}} | `docset.yml` | +| {{product.kibana}} | `products.yml` | +| {{.kibana}} | `products.yml` | ## Mutations @@ -217,3 +219,20 @@ With mutations: {subs=true}`version {{version.stack | M.M}}` :::{note} Regular inline code (without the `{subs}` role) will not process substitutions and will display the variable placeholders as-is. ::: + +## Products + +Product substitutions use `products.yml` to determine what will be displayed. Use the product's key to get its display name. + +```yaml +# products.yml + +id: + display: {value} + ... +# example: + apm-agent-dotnet: + display: 'APM .NET Agent' +``` + +A shorthand format is also available. Using `{{.id}}` is equivalent to `{{product.id}}`. diff --git a/docs/syntax/version-variables.md b/docs/syntax/version-variables.md index 280c57b18..cb71c0f6d 100644 --- a/docs/syntax/version-variables.md +++ b/docs/syntax/version-variables.md @@ -82,6 +82,8 @@ This is dictated by the [`versions.yml`](https://github.com/elastic/docs-builder * `ech` * `eck` * `ess` +* `esf` +* `search_ui` * `self` * `ecctl` * `curator` @@ -105,6 +107,17 @@ This is dictated by the [`versions.yml`](https://github.com/elastic/docs-builder * `edot_python` * `edot_cf_aws` * `edot_collector` +* `apm_attacher` +* `apm_lambda` +* `ecs_logging_dotnet` +* `ecs_logging_go_logrus` +* `ecs_logging_go_zap` +* `ecs_logging_go_zerolog` +* `ecs_logging_java` +* `ecs_logging_nodejs` +* `ecs_logging_php` +* `ecs_logging_python` +* `ecs_logging_ruby` The following are available but should not be used. These map to serverless projects and have a fixed high version number. diff --git a/src/Elastic.Documentation.Configuration/Assembler/AssemblyConfiguration.cs b/src/Elastic.Documentation.Configuration/Assembler/AssemblyConfiguration.cs index 240aa9ed3..a98685e16 100644 --- a/src/Elastic.Documentation.Configuration/Assembler/AssemblyConfiguration.cs +++ b/src/Elastic.Documentation.Configuration/Assembler/AssemblyConfiguration.cs @@ -18,13 +18,9 @@ public static AssemblyConfiguration Deserialize(string yaml, bool skipPrivateRep { var input = new StringReader(yaml); - var deserializer = new StaticDeserializerBuilder(new YamlStaticContext()) - .IgnoreUnmatchedProperties() - .Build(); - try { - var config = deserializer.Deserialize(input); + var config = ConfigurationFileProvider.Deserializer.Deserialize(input); foreach (var (name, r) in config.ReferenceRepositories) { var repository = RepositoryDefaults(r, name); diff --git a/src/Elastic.Documentation.Configuration/BuildContext.cs b/src/Elastic.Documentation.Configuration/BuildContext.cs index 9e78846fe..6b3b383c3 100644 --- a/src/Elastic.Documentation.Configuration/BuildContext.cs +++ b/src/Elastic.Documentation.Configuration/BuildContext.cs @@ -6,6 +6,8 @@ using System.Reflection; using Elastic.Documentation.Configuration.Assembler; using Elastic.Documentation.Configuration.Builder; +using Elastic.Documentation.Configuration.LegacyUrlMappings; +using Elastic.Documentation.Configuration.Products; using Elastic.Documentation.Configuration.Versions; using Elastic.Documentation.Diagnostics; @@ -30,6 +32,9 @@ public record BuildContext : IDocumentationSetContext, IDocumentationConfigurati public ConfigurationFileProvider ConfigurationFileProvider { get; } public DocumentationEndpoints Endpoints { get; } + public ProductsConfiguration ProductsConfiguration { get; } + public LegacyUrlMappingConfiguration LegacyUrlMappings { get; } + public IFileInfo ConfigurationPath { get; } public GitCheckoutInformation Git { get; } @@ -82,6 +87,8 @@ public BuildContext( AvailableExporters = availableExporters; VersionsConfiguration = configurationContext.VersionsConfiguration; ConfigurationFileProvider = configurationContext.ConfigurationFileProvider; + ProductsConfiguration = configurationContext.ProductsConfiguration; + LegacyUrlMappings = configurationContext.LegacyUrlMappings; Endpoints = configurationContext.Endpoints; var rootFolder = !string.IsNullOrWhiteSpace(source) @@ -100,7 +107,7 @@ public BuildContext( DocumentationSourceDirectory = ConfigurationPath.Directory!; Git = gitCheckoutInformation ?? GitCheckoutInformation.Create(DocumentationCheckoutDirectory, ReadFileSystem); - Configuration = new ConfigurationFile(this, VersionsConfiguration); + Configuration = new ConfigurationFile(this, VersionsConfiguration, ProductsConfiguration); GoogleTagManager = new GoogleTagManagerConfiguration { Enabled = false diff --git a/src/Elastic.Documentation.Configuration/Builder/ConfigurationFile.cs b/src/Elastic.Documentation.Configuration/Builder/ConfigurationFile.cs index 3529782a1..25a7af3bd 100644 --- a/src/Elastic.Documentation.Configuration/Builder/ConfigurationFile.cs +++ b/src/Elastic.Documentation.Configuration/Builder/ConfigurationFile.cs @@ -4,6 +4,7 @@ using System.IO.Abstractions; using DotNet.Globbing; +using Elastic.Documentation.Configuration.Products; using Elastic.Documentation.Configuration.Suggestions; using Elastic.Documentation.Configuration.TableOfContents; using Elastic.Documentation.Configuration.Versions; @@ -36,7 +37,7 @@ public record ConfigurationFile : ITableOfContentsScope public Dictionary? Redirects { get; } - public HashSet Products { get; } = new(StringComparer.Ordinal); + public HashSet Products { get; } = []; public HashSet ImplicitFolders { get; } = new(StringComparer.OrdinalIgnoreCase); @@ -62,7 +63,7 @@ public record ConfigurationFile : ITableOfContentsScope Project is not null && Project.Equals("Elastic documentation", StringComparison.OrdinalIgnoreCase); - public ConfigurationFile(IDocumentationSetContext context, VersionsConfiguration versionsConfig) + public ConfigurationFile(IDocumentationSetContext context, VersionsConfiguration versionsConfig, ProductsConfiguration productsConfig) { _context = context; ScopeDirectory = context.ConfigurationPath.Directory!; @@ -133,6 +134,7 @@ public ConfigurationFile(IDocumentationSetContext context, VersionsConfiguration foreach (var node in sequence.Children.OfType()) { YamlScalarNode? productId = null; + foreach (var child in node.Children) { if (child is { Key: YamlScalarNode { Value: "id" }, Value: YamlScalarNode scalarNode }) @@ -147,10 +149,10 @@ public ConfigurationFile(IDocumentationSetContext context, VersionsConfiguration break; } - if (!Builder.Products.AllById.ContainsKey(productId.Value)) - reader.EmitError($"Product \"{productId.Value}\" not found in the product list. {new Suggestion(Builder.Products.All.Select(p => p.Id).ToHashSet(), productId.Value).GetSuggestionQuestion()}", node); + if (!productsConfig.Products.TryGetValue(productId.Value.Replace('_', '-'), out var productToAdd)) + reader.EmitError($"Product \"{productId.Value}\" not found in the product list. {new Suggestion(productsConfig.Products.Select(p => p.Value.Id).ToHashSet(), productId.Value).GetSuggestionQuestion()}", node); else - _ = Products.Add(productId.Value); + _ = Products.Add(productToAdd); } break; case "features": @@ -175,6 +177,15 @@ public ConfigurationFile(IDocumentationSetContext context, VersionsConfiguration _substitutions[key] = system.Base; } + foreach (var product in productsConfig.Products.Values) + { + var alternativeProductId = product.Id.Replace('-', '_'); + _substitutions[$"product.{product.Id}"] = product.DisplayName; + _substitutions[$".{product.Id}"] = product.DisplayName; + _substitutions[$"product.{alternativeProductId}"] = product.DisplayName; + _substitutions[$".{alternativeProductId}"] = product.DisplayName; + } + var toc = new TableOfContentsConfiguration(this, sourceFile, ScopeDirectory, _context, 0, ""); TableOfContents = toc.TableOfContents; Files = toc.Files; diff --git a/src/Elastic.Documentation.Configuration/Builder/Products.cs b/src/Elastic.Documentation.Configuration/Builder/Products.cs deleted file mode 100644 index 86208364c..000000000 --- a/src/Elastic.Documentation.Configuration/Builder/Products.cs +++ /dev/null @@ -1,51 +0,0 @@ -// Licensed to Elasticsearch B.V under one or more agreements. -// Elasticsearch B.V licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information - -using System.Collections.Frozen; - -namespace Elastic.Documentation.Configuration.Builder; - -public record Product(string Id, string DisplayName); - -public static class Products -{ - public static FrozenSet All { get; } = [ - new("apm", "APM"), - new("apm-agent", "APM Agent"), - new("auditbeat", "Auditbeat"), - new("beats", "Beats"), - new("cloud-control-ecctl", "Elastic Cloud Control ECCTL"), - new("cloud-enterprise", "Elastic Cloud Enterprise"), - new("cloud-hosted", "Elastic Cloud Hosted"), - new("cloud-kubernetes", "Elastic Cloud Kubernetes"), - new("cloud-serverless", "Elastic Cloud Serverless"), - new("cloud-terraform", "Elastic Cloud Terraform"), - new("ecs", "Elastic Common Schema (ECS)"), - new("ecs-logging", "ECS Logging"), - new("edot-cf", "EDOT Cloud Forwarder"), - new("edot-sdk", "Elastic Distribution of OpenTelemetry SDK"), - new("edot-collector", "Elastic Distribution of OpenTelemetry Collector"), - new("elastic-agent", "Elastic Agent"), - new("elastic-serverless-forwarder", "Elastic Serverless Forwarder"), - new("elastic-stack", "Elastic Stack"), - new("elasticsearch", "Elasticsearch"), - new("elasticsearch-client", "Elasticsearch Client"), - new("filebeat", "Filebeat"), - new("fleet", "Fleet"), - new("heartbeat", "Heartbeat"), - new("integrations", "Integrations"), - new("kibana", "Kibana"), - new("logstash", "Logstash"), - new("machine-learning", "Machine Learning"), - new("metricbeat", "Metricbeat"), - new("observability", "Elastic Observability"), - new("packetbeat", "Packetbeat"), - new("painless", "Elasticsearch Painless scripting language"), - new("search-ui", "Search UI"), - new("security", "Elastic Security"), - new("winlogbeat", "Winlogbeat"), - ]; - - public static FrozenDictionary AllById { get; } = All.ToDictionary(p => p.Id, StringComparer.Ordinal).ToFrozenDictionary(); -} diff --git a/src/Elastic.Documentation.Configuration/ConfigurationFileProvider.cs b/src/Elastic.Documentation.Configuration/ConfigurationFileProvider.cs index 0b94a0784..656682e5b 100644 --- a/src/Elastic.Documentation.Configuration/ConfigurationFileProvider.cs +++ b/src/Elastic.Documentation.Configuration/ConfigurationFileProvider.cs @@ -5,8 +5,11 @@ using System.IO.Abstractions; using System.Text.RegularExpressions; using Elastic.Documentation.Configuration.Assembler; +using Elastic.Documentation.Configuration.Serialization; using Microsoft.Extensions.DependencyInjection; using NetEscapades.EnumGenerators; +using YamlDotNet.Serialization; +using YamlDotNet.Serialization.NamingConventions; namespace Elastic.Documentation.Configuration; @@ -23,6 +26,10 @@ public partial class ConfigurationFileProvider private readonly IFileSystem _fileSystem; private readonly string _assemblyName; + internal static IDeserializer Deserializer { get; } = new StaticDeserializerBuilder(new YamlStaticContext()) + .WithNamingConvention(UnderscoredNamingConvention.Instance) + .Build(); + public ConfigurationSource ConfigurationSource { get; private set; } = ConfigurationSource.Embedded; public string? GitReference { get; } @@ -34,6 +41,7 @@ public ConfigurationFileProvider(IFileSystem fileSystem, bool skipPrivateReposit TemporaryDirectory = fileSystem.Directory.CreateTempSubdirectory("docs-builder-config"); VersionFile = CreateTemporaryConfigurationFile("versions.yml"); + ProductsFile = CreateTemporaryConfigurationFile("products.yml"); AssemblerFile = CreateTemporaryConfigurationFile("assembler.yml"); NavigationFile = CreateTemporaryConfigurationFile("navigation.yml"); LegacyUrlMappingsFile = CreateTemporaryConfigurationFile("legacy-url-mappings.yml"); @@ -50,6 +58,8 @@ public ConfigurationFileProvider(IFileSystem fileSystem, bool skipPrivateReposit public IFileInfo VersionFile { get; } + public IFileInfo ProductsFile { get; } + public IFileInfo AssemblerFile { get; } public IFileInfo LegacyUrlMappingsFile { get; } diff --git a/src/Elastic.Documentation.Configuration/Elastic.Documentation.Configuration.csproj b/src/Elastic.Documentation.Configuration/Elastic.Documentation.Configuration.csproj index 551c2af0a..c7defd0a3 100644 --- a/src/Elastic.Documentation.Configuration/Elastic.Documentation.Configuration.csproj +++ b/src/Elastic.Documentation.Configuration/Elastic.Documentation.Configuration.csproj @@ -20,6 +20,7 @@ + diff --git a/src/Elastic.Documentation.Configuration/IDocumentationConfigurationContext.cs b/src/Elastic.Documentation.Configuration/IDocumentationConfigurationContext.cs index da15a7635..4a44881cc 100644 --- a/src/Elastic.Documentation.Configuration/IDocumentationConfigurationContext.cs +++ b/src/Elastic.Documentation.Configuration/IDocumentationConfigurationContext.cs @@ -2,6 +2,8 @@ // Elasticsearch B.V licenses this file to you under the Apache 2.0 License. // See the LICENSE file in the project root for more information +using Elastic.Documentation.Configuration.LegacyUrlMappings; +using Elastic.Documentation.Configuration.Products; using Elastic.Documentation.Configuration.Versions; namespace Elastic.Documentation.Configuration; @@ -11,6 +13,8 @@ public interface IConfigurationContext VersionsConfiguration VersionsConfiguration { get; } ConfigurationFileProvider ConfigurationFileProvider { get; } DocumentationEndpoints Endpoints { get; } + ProductsConfiguration ProductsConfiguration { get; } + LegacyUrlMappingConfiguration LegacyUrlMappings { get; } } /// Used only to seed in DI, you primarily want to depend on @@ -24,6 +28,13 @@ public class ConfigurationContext : IConfigurationContext /// public required DocumentationEndpoints Endpoints { get; init; } + + /// + public required ProductsConfiguration ProductsConfiguration { get; init; } + + /// + public required LegacyUrlMappingConfiguration LegacyUrlMappings { get; init; } + } public interface IDocumentationConfigurationContext : IDocumentationContext, IConfigurationContext; diff --git a/src/Elastic.Documentation/Legacy/ILegacyUrlMapper.cs b/src/Elastic.Documentation.Configuration/LegacyUrlMappings/LegacyUrlMapping.cs similarity index 52% rename from src/Elastic.Documentation/Legacy/ILegacyUrlMapper.cs rename to src/Elastic.Documentation.Configuration/LegacyUrlMappings/LegacyUrlMapping.cs index 390d7d3eb..dd815df6f 100644 --- a/src/Elastic.Documentation/Legacy/ILegacyUrlMapper.cs +++ b/src/Elastic.Documentation.Configuration/LegacyUrlMappings/LegacyUrlMapping.cs @@ -2,12 +2,25 @@ // Elasticsearch B.V licenses this file to you under the Apache 2.0 License. // See the LICENSE file in the project root for more information -namespace Elastic.Documentation.Legacy; +using Elastic.Documentation.Configuration.Products; -public record LegacyPageMapping(string RawUrl, string Version, bool Exists) +namespace Elastic.Documentation.Configuration.LegacyUrlMappings; + +public record LegacyUrlMappingConfiguration +{ + public required IReadOnlyCollection Mappings { get; init; } +} +public record LegacyUrlMapping +{ + public required string BaseUrl { get; init; } + public required Product Product { get; init; } + public required IReadOnlyCollection LegacyVersions { get; init; } +} + +public record LegacyPageMapping(Product Product, string RawUrl, string Version, bool Exists) { public override string ToString() => RawUrl.Replace("/current/", $"/{Version}/"); -}; +} public interface ILegacyUrlMapper { diff --git a/src/Elastic.Documentation.Configuration/LegacyUrlMappings/LegacyUrlMappingExtensions.cs b/src/Elastic.Documentation.Configuration/LegacyUrlMappings/LegacyUrlMappingExtensions.cs new file mode 100644 index 000000000..07760d8ed --- /dev/null +++ b/src/Elastic.Documentation.Configuration/LegacyUrlMappings/LegacyUrlMappingExtensions.cs @@ -0,0 +1,42 @@ +// Licensed to Elasticsearch B.V under one or more agreements. +// Elasticsearch B.V licenses this file to you under the Apache 2.0 License. +// See the LICENSE file in the project root for more information + +using System.Collections.Immutable; +using Elastic.Documentation.Configuration.Products; + +namespace Elastic.Documentation.Configuration.LegacyUrlMappings; + +public static class LegacyUrlMappingExtensions +{ + public static LegacyUrlMappingConfiguration CreateLegacyUrlMappings(this ConfigurationFileProvider provider, ProductsConfiguration products) + { + var legacyUrlMappingsFilePath = provider.LegacyUrlMappingsFile; + + var legacyUrlMappingsDto = ConfigurationFileProvider.Deserializer.Deserialize(legacyUrlMappingsFilePath.OpenText()); + + var legacyUrlMappings = legacyUrlMappingsDto.Mappings.Select(kvp => + new LegacyUrlMapping + { + BaseUrl = kvp.Key, + Product = products.Products[kvp.Value.Product], + LegacyVersions = kvp.Value.LegacyVersions.ToImmutableList() + }); + + return new LegacyUrlMappingConfiguration { Mappings = legacyUrlMappings.ToImmutableList() }; + } +} + +// Private DTOs for deserialization. These match the YAML structure directly. + +internal sealed record LegacyUrlMappingConfigDto +{ + public IEnumerable Stack { get; set; } = []; + public Dictionary Mappings { get; set; } = []; +} + +internal sealed record LegacyUrlMappingDto +{ + public string Product { get; set; } = string.Empty; + public IEnumerable LegacyVersions { get; set; } = []; +} diff --git a/src/Elastic.Documentation.Configuration/Products/Product.cs b/src/Elastic.Documentation.Configuration/Products/Product.cs new file mode 100644 index 000000000..9dabeb1eb --- /dev/null +++ b/src/Elastic.Documentation.Configuration/Products/Product.cs @@ -0,0 +1,23 @@ +// Licensed to Elasticsearch B.V under one or more agreements. +// Elasticsearch B.V licenses this file to you under the Apache 2.0 License. +// See the LICENSE file in the project root for more information + +using System.Collections.Frozen; +using Elastic.Documentation.Configuration.Versions; +using YamlDotNet.Serialization; + +namespace Elastic.Documentation.Configuration.Products; + +public record ProductsConfiguration +{ + public required FrozenDictionary Products { get; init; } +} + +[YamlSerializable] +public record Product +{ + public required string Id { get; init; } + public required string DisplayName { get; init; } + public VersioningSystem? VersioningSystem { get; init; } +} + diff --git a/src/Elastic.Documentation.Configuration/Products/ProductExtensions.cs b/src/Elastic.Documentation.Configuration/Products/ProductExtensions.cs new file mode 100644 index 000000000..96cd8d5ae --- /dev/null +++ b/src/Elastic.Documentation.Configuration/Products/ProductExtensions.cs @@ -0,0 +1,44 @@ +// Licensed to Elasticsearch B.V under one or more agreements. +// Elasticsearch B.V licenses this file to you under the Apache 2.0 License. +// See the LICENSE file in the project root for more information + +using System.Collections.Frozen; +using Elastic.Documentation.Configuration.Versions; + +namespace Elastic.Documentation.Configuration.Products; + +public static class ProductExtensions +{ + public static ProductsConfiguration CreateProducts(this ConfigurationFileProvider provider, VersionsConfiguration versionsConfiguration) + { + var productsFilePath = provider.ProductsFile; + + var productsDto = ConfigurationFileProvider.Deserializer.Deserialize(productsFilePath.OpenText()); + + var products = productsDto.Products.ToDictionary( + kvp => kvp.Key, + kvp => new Product + { + Id = kvp.Key, + DisplayName = kvp.Value.Display, + VersioningSystem = versionsConfiguration.GetVersioningSystem(VersionsConfigurationExtensions.ToVersioningSystemId(kvp.Value.Versioning)) + }); + + return new ProductsConfiguration + { + Products = products.ToFrozenDictionary() + }; + } +} + +// Private DTOs for deserialization. These match the YAML structure directly. + +internal sealed record ProductConfigDto +{ + public Dictionary Products { get; set; } = []; +} +internal sealed record ProductDto +{ + public string Display { get; set; } = string.Empty; + public string Versioning { get; set; } = string.Empty; +} diff --git a/src/Elastic.Documentation.Configuration/Serialization/YamlStaticContext.cs b/src/Elastic.Documentation.Configuration/Serialization/YamlStaticContext.cs index 3bc8769c0..5a9c6ef8a 100644 --- a/src/Elastic.Documentation.Configuration/Serialization/YamlStaticContext.cs +++ b/src/Elastic.Documentation.Configuration/Serialization/YamlStaticContext.cs @@ -3,6 +3,8 @@ // See the LICENSE file in the project root for more information using Elastic.Documentation.Configuration.Assembler; +using Elastic.Documentation.Configuration.LegacyUrlMappings; +using Elastic.Documentation.Configuration.Products; using Elastic.Documentation.Configuration.Versions; using YamlDotNet.Serialization; @@ -16,5 +18,9 @@ namespace Elastic.Documentation.Configuration.Serialization; [YamlSerializable(typeof(GoogleTagManager))] [YamlSerializable(typeof(ContentSource))] [YamlSerializable(typeof(VersionsConfigDto))] +[YamlSerializable(typeof(ProductConfigDto))] [YamlSerializable(typeof(VersioningSystemDto))] +[YamlSerializable(typeof(ProductDto))] +[YamlSerializable(typeof(LegacyUrlMappingDto))] +[YamlSerializable(typeof(LegacyUrlMappingConfigDto))] public partial class YamlStaticContext; diff --git a/src/Elastic.Documentation.Configuration/Versions/VersionConfiguration.cs b/src/Elastic.Documentation.Configuration/Versions/VersionConfiguration.cs index a1f6525c7..f6a6dd046 100644 --- a/src/Elastic.Documentation.Configuration/Versions/VersionConfiguration.cs +++ b/src/Elastic.Documentation.Configuration/Versions/VersionConfiguration.cs @@ -69,6 +69,30 @@ public enum VersioningSystemId ApmAgentRuby, [Display(Name = "apm_agent_rum")] ApmAgentRum, + [Display(Name = "apm_attacher")] + ApmAttacher, + [Display(Name = "apm_lambda")] + ApmLambda, + [Display(Name = "ecs_logging_dotnet")] + EcsLoggingDotnet, + [Display(Name = "ecs_logging_go_logrus")] + EcsLoggingGoLogrus, + [Display(Name = "ecs_logging_go_zap")] + EcsLoggingGoZap, + [Display(Name = "ecs_logging_go_zerolog")] + EcsLoggingGoZerolog, + [Display(Name = "ecs_logging_java")] + EcsLoggingJava, + [Display(Name = "ecs_logging_nodejs")] + EcsLoggingNodeJs, + [Display(Name = "ecs_logging_php")] + EcsLoggingPhp, + [Display(Name = "ecs_logging_python")] + EcsLoggingPython, + [Display(Name = "ecs_logging_ruby")] + EcsLoggingRuby, + [Display(Name = "esf")] + Esf, [Display(Name = "edot_ios")] EdotIos, [Display(Name = "edot_android")] @@ -86,7 +110,11 @@ public enum VersioningSystemId [Display(Name = "edot_cf_aws")] EdotCfAws, [Display(Name = "edot_collector")] - EdotCollector + EdotCollector, + [Display(Name = "search_ui")] + SearchUI, + [Display(Name = "cloud_terraform")] + CloudTerraform, } [YamlSerializable] diff --git a/src/Elastic.Documentation.Configuration/Versions/VersionsConfigurationExtensions.cs b/src/Elastic.Documentation.Configuration/Versions/VersionsConfigurationExtensions.cs index 25c0f2f0f..00ae91b88 100644 --- a/src/Elastic.Documentation.Configuration/Versions/VersionsConfigurationExtensions.cs +++ b/src/Elastic.Documentation.Configuration/Versions/VersionsConfigurationExtensions.cs @@ -2,25 +2,17 @@ // Elasticsearch B.V licenses this file to you under the Apache 2.0 License. // See the LICENSE file in the project root for more information -using Elastic.Documentation.Configuration.Serialization; -using YamlDotNet.Serialization; -using YamlDotNet.Serialization.NamingConventions; - namespace Elastic.Documentation.Configuration.Versions; public static class VersionsConfigurationExtensions { public static VersionsConfiguration CreateVersionConfiguration(this ConfigurationFileProvider provider) { - var path = provider.VersionFile; - - var deserializer = new StaticDeserializerBuilder(new YamlStaticContext()) - .WithNamingConvention(UnderscoredNamingConvention.Instance) - .Build(); + var versionFilePath = provider.VersionFile; - var dto = deserializer.Deserialize(path.OpenText()); + var versionsDto = ConfigurationFileProvider.Deserializer.Deserialize(versionFilePath.OpenText()); - var versions = dto.VersioningSystems.ToDictionary( + var versions = versionsDto.VersioningSystems.ToDictionary( kvp => ToVersioningSystemId(kvp.Key), kvp => new VersioningSystem { @@ -29,10 +21,11 @@ public static VersionsConfiguration CreateVersionConfiguration(this Configuratio Current = ToSemVersion(kvp.Value.Current) }); var config = new VersionsConfiguration { VersioningSystems = versions }; + return config; } - private static VersioningSystemId ToVersioningSystemId(string id) + internal static VersioningSystemId ToVersioningSystemId(string id) { if (!VersioningSystemIdExtensions.TryParse(id, out var versioningSystemId, true, true)) throw new InvalidOperationException($"Could not parse versioning system id {id}"); @@ -66,3 +59,4 @@ internal sealed record VersioningSystemDto public string Base { get; set; } = string.Empty; public string Current { get; set; } = string.Empty; } + diff --git a/src/Elastic.Documentation.ServiceDefaults/AppDefaultsExtensions.cs b/src/Elastic.Documentation.ServiceDefaults/AppDefaultsExtensions.cs index bcfda4fd1..706859967 100644 --- a/src/Elastic.Documentation.ServiceDefaults/AppDefaultsExtensions.cs +++ b/src/Elastic.Documentation.ServiceDefaults/AppDefaultsExtensions.cs @@ -3,6 +3,8 @@ // See the LICENSE file in the project root for more information using Elastic.Documentation.Configuration; +using Elastic.Documentation.Configuration.LegacyUrlMappings; +using Elastic.Documentation.Configuration.Products; using Elastic.Documentation.Configuration.Versions; using Elastic.Documentation.ServiceDefaults.Logging; using Microsoft.Extensions.DependencyInjection; @@ -32,7 +34,11 @@ public static TBuilder AddDocumentationServiceDefaults(this TBuilder b _ = services .AddConfigurationFileProvider(skipPrivateRepositories, (s, p) => { - _ = s.AddSingleton(p.CreateVersionConfiguration()); + var versionConfiguration = p.CreateVersionConfiguration(); + var products = p.CreateProducts(versionConfiguration); + _ = s.AddSingleton(p.CreateLegacyUrlMappings(products)); + _ = s.AddSingleton(products); + _ = s.AddSingleton(versionConfiguration); configure?.Invoke(s, p); }); _ = builder.Services.AddElasticDocumentationLogging(logLevel); diff --git a/src/Elastic.Documentation/AppliesTo/ApplicableToYamlConverter.cs b/src/Elastic.Documentation/AppliesTo/ApplicableToYamlConverter.cs index e736e1bb9..0cfb1a505 100644 --- a/src/Elastic.Documentation/AppliesTo/ApplicableToYamlConverter.cs +++ b/src/Elastic.Documentation/AppliesTo/ApplicableToYamlConverter.cs @@ -10,16 +10,14 @@ namespace Elastic.Documentation.AppliesTo; -public class ApplicableToYamlConverter : IYamlTypeConverter +public class ApplicableToYamlConverter(IReadOnlyCollection productKeys) : IYamlTypeConverter { - private static readonly string[] KnownKeys = + private readonly string[] _knownKeys = [ - "stack", "deployment", "serverless", "product", - "ece", "eck", "ess", "self", - "elasticsearch", "observability", "security", - "ecctl", "curator", - "apm_agent_android","apm_agent_dotnet", "apm_agent_go", "apm_agent_ios", "apm_agent_java", "apm_agent_node", "apm_agent_php", "apm_agent_python", "apm_agent_ruby", "apm_agent_rum", - "edot_ios", "edot_android", "edot_dotnet", "edot_java", "edot_node", "edot_php", "edot_python", "edot_cf_aws" + "stack", "deployment", "serverless", "product", // Applicability categories + "ece", "eck", "ess", "self", // Deployment options + "elasticsearch", "observability", "security", // Serverless flavors + .. productKeys ]; public bool Accepts(Type type) => type == typeof(ApplicableTo); @@ -45,11 +43,11 @@ public class ApplicableToYamlConverter : IYamlTypeConverter if (deserialized is not Dictionary { Count: > 0 } dictionary) return null; - var keys = dictionary.Keys.OfType().ToArray(); + var keys = dictionary.Keys.OfType().Select(x => x.Replace('_', '-')).ToArray(); var oldStyleKeys = keys.Where(k => k.StartsWith(':')).ToList(); if (oldStyleKeys.Count > 0) diagnostics.Add((Severity.Warning, $"Applies block does not use valid yaml keys: {string.Join(", ", oldStyleKeys)}")); - var unknownKeys = keys.Except(KnownKeys).Except(oldStyleKeys).ToList(); + var unknownKeys = keys.Except(_knownKeys).Except(oldStyleKeys).ToList(); if (unknownKeys.Count > 0) diagnostics.Add((Severity.Warning, $"Applies block does not support the following keys: {string.Join(", ", unknownKeys)}")); diff --git a/src/Elastic.Markdown/DocumentationGenerator.cs b/src/Elastic.Markdown/DocumentationGenerator.cs index 200d19d78..5929bfd4c 100644 --- a/src/Elastic.Markdown/DocumentationGenerator.cs +++ b/src/Elastic.Markdown/DocumentationGenerator.cs @@ -6,7 +6,7 @@ using System.Text.Json; using Elastic.Documentation; using Elastic.Documentation.Configuration; -using Elastic.Documentation.Legacy; +using Elastic.Documentation.Configuration.LegacyUrlMappings; using Elastic.Documentation.Links; using Elastic.Documentation.Serialization; using Elastic.Documentation.Site.FileProviders; diff --git a/src/Elastic.Markdown/Exporters/LlmMarkdownExporter.cs b/src/Elastic.Markdown/Exporters/LlmMarkdownExporter.cs index ea54b9520..1ed4b7207 100644 --- a/src/Elastic.Markdown/Exporters/LlmMarkdownExporter.cs +++ b/src/Elastic.Markdown/Exporters/LlmMarkdownExporter.cs @@ -6,7 +6,6 @@ using System.IO.Compression; using System.Text; using Elastic.Documentation.Configuration; -using Elastic.Documentation.Configuration.Builder; using Elastic.Markdown.Helpers; using Markdig.Syntax; @@ -114,11 +113,11 @@ private string CreateLlmContentWithMetadata(MarkdownExportFileContext context, s if (!string.IsNullOrEmpty(sourceFile.Url)) _ = metadata.AppendLine($"url: {context.BuildContext.CanonicalBaseUrl?.Scheme}://{context.BuildContext.CanonicalBaseUrl?.Host}{sourceFile.Url}"); - var configProducts = context.BuildContext.Configuration.Products.Select(p => + var configProducts = context.BuildContext.ProductsConfiguration.Products.Select(p => { - if (Products.AllById.TryGetValue(p, out var product)) + if (context.BuildContext.ProductsConfiguration.Products.TryGetValue(p.Value.Id, out var product)) return product; - throw new ArgumentException($"Invalid product id: {p}"); + throw new ArgumentException($"Invalid product id: {p.Value.Id}"); }); var frontMatterProducts = sourceFile.YamlFrontMatter?.Products ?? []; var allProducts = frontMatterProducts @@ -128,8 +127,8 @@ private string CreateLlmContentWithMetadata(MarkdownExportFileContext context, s if (allProducts.Count > 0) { _ = metadata.AppendLine("products:"); - foreach (var product in allProducts.Select(p => p.DisplayName).Order()) - _ = metadata.AppendLine($" - {product}"); + foreach (var item in allProducts.Select(p => p.DisplayName).Order()) + _ = metadata.AppendLine($" - {item}"); } _ = metadata.AppendLine("---"); diff --git a/src/Elastic.Markdown/HtmlWriter.cs b/src/Elastic.Markdown/HtmlWriter.cs index 6b99f731f..3486d2864 100644 --- a/src/Elastic.Markdown/HtmlWriter.cs +++ b/src/Elastic.Markdown/HtmlWriter.cs @@ -5,8 +5,8 @@ using System.IO.Abstractions; using System.Text.Json; using Elastic.Documentation; -using Elastic.Documentation.Configuration.Builder; -using Elastic.Documentation.Legacy; +using Elastic.Documentation.Configuration.LegacyUrlMappings; +using Elastic.Documentation.Configuration.Versions; using Elastic.Documentation.Site.FileProviders; using Elastic.Documentation.Site.Navigation; using Elastic.Markdown.Extensions.DetectionRules; @@ -84,14 +84,13 @@ private async Task RenderLayout(MarkdownFile markdown, MarkdownDoc var reportUrl = $"https://github.com/elastic/docs-content/issues/new?template=issue-report.yaml&link={reportLinkParameter}&labels=source:web"; var siteName = DocumentationSet.Tree.Index.Title ?? "Elastic Documentation"; - var legacyPages = LegacyUrlMapper.MapLegacyUrl(markdown.YamlFrontMatter?.MappedPages); - var configProducts = DocumentationSet.Configuration.Products.Select(p => + var configProducts = DocumentationSet.Context.ProductsConfiguration.Products.Select(p => { - if (Products.AllById.TryGetValue(p, out var product)) + if (DocumentationSet.Context.ProductsConfiguration.Products.TryGetValue(p.Value.Id, out var product)) return product; - throw new ArgumentException($"Invalid product id: {p}"); + throw new ArgumentException($"Invalid product id: {p.Value.Id}"); }); var frontMatterProducts = markdown.YamlFrontMatter?.Products ?? []; @@ -114,6 +113,9 @@ private async Task RenderLayout(MarkdownFile markdown, MarkdownDoc fullNavigationRenderResult ); + var currentBaseVersion = legacyPages is { Count: > 0 } + ? $"{legacyPages.ElementAt(0).Product.VersioningSystem?.Base.Major}.{legacyPages.ElementAt(0).Product.VersioningSystem?.Base.Minor}+" + : $"{DocumentationSet.Context.VersionsConfiguration.VersioningSystems[VersioningSystemId.Stack].Base.Major}.{DocumentationSet.Context.VersionsConfiguration.VersioningSystems[VersioningSystemId.Stack].Base.Minor}+"; //TODO should we even distinctby var breadcrumbs = parents.Reverse().DistinctBy(p => p.Url).ToArray(); var breadcrumbsList = CreateStructuredBreadcrumbsData(markdown, breadcrumbs); @@ -147,10 +149,10 @@ private async Task RenderLayout(MarkdownFile markdown, MarkdownDoc Features = DocumentationSet.Configuration.Features, StaticFileContentHashProvider = StaticFileContentHashProvider, ReportIssueUrl = reportUrl, - CurrentVersion = legacyPages?.Count > 0 ? legacyPages.ElementAt(0).Version : "9.0+", + CurrentVersion = currentBaseVersion, AllVersionsUrl = allVersionsUrl, LegacyPages = legacyPages?.Skip(1).ToArray(), - VersionDropdownItems = VersionDrownDownItemViewModel.FromLegacyPageMappings(legacyPages?.Skip(1).ToArray()), + VersionDropdownItems = VersionDropDownItemViewModel.FromLegacyPageMappings(legacyPages?.Skip(1).ToArray()), Products = allProducts, VersionsConfig = DocumentationSet.Context.VersionsConfiguration, StructuredBreadcrumbsJson = structuredBreadcrumbsJsonString diff --git a/src/Elastic.Markdown/IO/MarkdownFile.cs b/src/Elastic.Markdown/IO/MarkdownFile.cs index b288d025f..a7ae84d99 100644 --- a/src/Elastic.Markdown/IO/MarkdownFile.cs +++ b/src/Elastic.Markdown/IO/MarkdownFile.cs @@ -379,7 +379,7 @@ private YamlFrontMatter ReadYamlFrontMatter(string raw) { try { - return YamlSerialization.Deserialize(raw); + return YamlSerialization.Deserialize(raw, _set.Context.ProductsConfiguration); } catch (InvalidProductException e) { diff --git a/src/Elastic.Markdown/MarkdownLayoutViewModel.cs b/src/Elastic.Markdown/MarkdownLayoutViewModel.cs index f1034ffaf..7b947637b 100644 --- a/src/Elastic.Markdown/MarkdownLayoutViewModel.cs +++ b/src/Elastic.Markdown/MarkdownLayoutViewModel.cs @@ -2,7 +2,7 @@ // Elasticsearch B.V licenses this file to you under the Apache 2.0 License. // See the LICENSE file in the project root for more information -using Elastic.Documentation.Legacy; +using Elastic.Documentation.Configuration.LegacyUrlMappings; using Elastic.Documentation.Site; using Elastic.Documentation.Site.Navigation; diff --git a/src/Elastic.Markdown/Myst/CodeBlocks/EnhancedCodeBlockParser.cs b/src/Elastic.Markdown/Myst/CodeBlocks/EnhancedCodeBlockParser.cs index 8b34aca0f..de2e0c4b6 100644 --- a/src/Elastic.Markdown/Myst/CodeBlocks/EnhancedCodeBlockParser.cs +++ b/src/Elastic.Markdown/Myst/CodeBlocks/EnhancedCodeBlockParser.cs @@ -124,7 +124,7 @@ private static void ProcessAppliesToDirective(AppliesToDirective appliesToDirect try { - var applicableTo = YamlSerialization.Deserialize(yaml); + var applicableTo = YamlSerialization.Deserialize(yaml, appliesToDirective.Build.ProductsConfiguration); appliesToDirective.AppliesTo = applicableTo; if (appliesToDirective.AppliesTo.Diagnostics is null) return; diff --git a/src/Elastic.Markdown/Myst/Directives/DirectiveHtmlRenderer.cs b/src/Elastic.Markdown/Myst/Directives/DirectiveHtmlRenderer.cs index 1e890eb6b..6beee7e63 100644 --- a/src/Elastic.Markdown/Myst/Directives/DirectiveHtmlRenderer.cs +++ b/src/Elastic.Markdown/Myst/Directives/DirectiveHtmlRenderer.cs @@ -316,7 +316,7 @@ private static void WriteSettingsBlock(HtmlRenderer renderer, SettingsBlock bloc try { var yaml = file.FileSystem.File.ReadAllText(file.FullName); - settings = YamlSerialization.Deserialize(yaml); + settings = YamlSerialization.Deserialize(yaml, block.Context.Build.ProductsConfiguration); } catch (YamlException e) { diff --git a/src/Elastic.Markdown/Myst/FrontMatter/FrontMatterParser.cs b/src/Elastic.Markdown/Myst/FrontMatter/FrontMatterParser.cs index f58ba115d..8692adf92 100644 --- a/src/Elastic.Markdown/Myst/FrontMatter/FrontMatterParser.cs +++ b/src/Elastic.Markdown/Myst/FrontMatter/FrontMatterParser.cs @@ -3,7 +3,8 @@ // See the LICENSE file in the project root for more information using Elastic.Documentation.AppliesTo; -using Elastic.Documentation.Configuration.Builder; +using Elastic.Documentation.Configuration.Products; +using Elastic.Documentation.Configuration.Versions; using YamlDotNet.Serialization; namespace Elastic.Markdown.Myst.FrontMatter; diff --git a/src/Elastic.Markdown/Myst/FrontMatter/Products.cs b/src/Elastic.Markdown/Myst/FrontMatter/Products.cs index 40335b5e4..cb2cf064b 100644 --- a/src/Elastic.Markdown/Myst/FrontMatter/Products.cs +++ b/src/Elastic.Markdown/Myst/FrontMatter/Products.cs @@ -2,8 +2,7 @@ // Elasticsearch B.V licenses this file to you under the Apache 2.0 License. // See the LICENSE file in the project root for more information -using System.Collections.Frozen; -using Elastic.Documentation.Configuration.Builder; +using Elastic.Documentation.Configuration.Products; using Elastic.Documentation.Configuration.Suggestions; using YamlDotNet.Core; using YamlDotNet.Core.Events; @@ -11,7 +10,7 @@ namespace Elastic.Markdown.Myst.FrontMatter; -public class ProductConverter : IYamlTypeConverter +public class ProductConverter(ProductsConfiguration products) : IYamlTypeConverter { public bool Accepts(Type type) => type == typeof(Product); @@ -20,7 +19,7 @@ public object ReadYaml(IParser parser, Type type, ObjectDeserializer rootDeseria if (parser.Current is Scalar) { var value = parser.Consume().Value; - throw new InvalidProductException($"Invalid YAML format. Products must be specified as a mapping with an 'id' field. Found scalar value: '{value}'. Example format:\nproducts:\n - id: apm"); + throw new InvalidProductException($"Invalid YAML format. Products must be specified as a mapping with an 'id' field. Found scalar value: '{value}'. Example format:\nproducts:\n - id: apm", products); } _ = parser.Consume(); @@ -38,25 +37,19 @@ public object ReadYaml(IParser parser, Type type, ObjectDeserializer rootDeseria _ = parser.Consume(); if (string.IsNullOrWhiteSpace(productId)) - throw new InvalidProductException("Product 'id' field is required. Example format:\nproducts:\n - id: apm"); + throw new InvalidProductException("Product 'id' field is required. Example format:\nproducts:\n - id: apm", products); - if (Products.AllById.TryGetValue(productId, out var product)) + if (products.Products.TryGetValue(productId.Replace('_', '-'), out var product)) return product; - throw new InvalidProductException(productId); + throw new InvalidProductException(productId, products); } public void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer) => serializer.Invoke(value, type); } -public class InvalidProductException(string invalidValue) +public class InvalidProductException(string invalidValue, ProductsConfiguration products) : Exception( $"Invalid products frontmatter value: \"{invalidValue}\"." + - (!string.IsNullOrWhiteSpace(invalidValue) ? " " + new Suggestion(ProductExtensions.GetProductIds(), invalidValue).GetSuggestionQuestion() : "") + + (!string.IsNullOrWhiteSpace(invalidValue) ? " " + new Suggestion(products.Products.Select(p => p.Value.Id).ToHashSet(), invalidValue).GetSuggestionQuestion() : "") + "\nYou can find the full list at https://docs-v3-preview.elastic.dev/elastic/docs-builder/tree/main/syntax/frontmatter#products."); - -public static class ProductExtensions -{ - public static IReadOnlySet GetProductIds() => - Products.All.Select(p => p.Id).ToFrozenSet(); -} diff --git a/src/Elastic.Markdown/Myst/Roles/AppliesTo/AppliesToRole.cs b/src/Elastic.Markdown/Myst/Roles/AppliesTo/AppliesToRole.cs index ad2e13627..d65d9dd06 100644 --- a/src/Elastic.Markdown/Myst/Roles/AppliesTo/AppliesToRole.cs +++ b/src/Elastic.Markdown/Myst/Roles/AppliesTo/AppliesToRole.cs @@ -20,8 +20,8 @@ public class AppliesToRole : RoleLeaf, IApplicableToElement { public AppliesToRole(string role, string content, InlineProcessor parserContext) : base(role, content) { - AppliesTo = ParseApplicableTo(content, parserContext); BuildContext = parserContext.GetContext().Build; + AppliesTo = ParseApplicableTo(content, parserContext); } public ApplicableTo? AppliesTo { get; } @@ -32,7 +32,7 @@ public AppliesToRole(string role, string content, InlineProcessor parserContext) { try { - var applicableTo = YamlSerialization.Deserialize(yaml); + var applicableTo = YamlSerialization.Deserialize(yaml, BuildContext.ProductsConfiguration); if (applicableTo.Diagnostics is null) return applicableTo; foreach (var (severity, message) in applicableTo.Diagnostics) diff --git a/src/Elastic.Markdown/Myst/YamlSerialization.cs b/src/Elastic.Markdown/Myst/YamlSerialization.cs index 857439a3c..669833fa3 100644 --- a/src/Elastic.Markdown/Myst/YamlSerialization.cs +++ b/src/Elastic.Markdown/Myst/YamlSerialization.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information using Elastic.Documentation.AppliesTo; +using Elastic.Documentation.Configuration.Products; using Elastic.Markdown.Myst.Directives.Settings; using Elastic.Markdown.Myst.FrontMatter; using YamlDotNet.Serialization; @@ -12,7 +13,7 @@ namespace Elastic.Markdown.Myst; public static class YamlSerialization { - public static T Deserialize(string yaml) + public static T Deserialize(string yaml, ProductsConfiguration products) { var input = new StringReader(yaml); @@ -20,21 +21,18 @@ public static T Deserialize(string yaml) .IgnoreUnmatchedProperties() .WithEnumNamingConvention(HyphenatedNamingConvention.Instance) .WithTypeConverter(new SemVersionConverter()) - .WithTypeConverter(new ProductConverter()) - .WithTypeConverter(new ApplicableToYamlConverter()) + .WithTypeConverter(new ProductConverter(products)) + .WithTypeConverter(new ApplicableToYamlConverter(products.Products.Keys)) .Build(); var frontMatter = deserializer.Deserialize(input); return frontMatter; - } } [YamlStaticContext] [YamlSerializable(typeof(YamlSettings))] [YamlSerializable(typeof(SettingsGrouping))] -[YamlSerializable(typeof(YamlSettings))] -[YamlSerializable(typeof(SettingsGrouping))] [YamlSerializable(typeof(Setting))] [YamlSerializable(typeof(AllowedValue))] [YamlSerializable(typeof(SettingMutability))] diff --git a/src/Elastic.Markdown/Page/Index.cshtml b/src/Elastic.Markdown/Page/Index.cshtml index 58c6b47c6..9cb7fa702 100644 --- a/src/Elastic.Markdown/Page/Index.cshtml +++ b/src/Elastic.Markdown/Page/Index.cshtml @@ -40,7 +40,7 @@ CurrentVersion = Model.CurrentDocument.YamlFrontMatter?.Layout == MarkdownPageLayout.LandingPage ? Model.VersionsConfig.VersioningSystems[0].Current : Model.CurrentVersion, AllVersionsUrl = Model.AllVersionsUrl, VersionDropdownSerializedModel = JsonSerializer.Serialize(Model.VersionDropdownItems, - ViewModelSerializerContext.Default.VersionDrownDownItemViewModelArray), + ViewModelSerializerContext.Default.VersionDropDownItemViewModelArray), }; protected override Task ExecuteSectionAsync(string name) { diff --git a/src/Elastic.Markdown/Page/IndexViewModel.cs b/src/Elastic.Markdown/Page/IndexViewModel.cs index b6a9ba096..7c3ce4761 100644 --- a/src/Elastic.Markdown/Page/IndexViewModel.cs +++ b/src/Elastic.Markdown/Page/IndexViewModel.cs @@ -6,8 +6,9 @@ using Elastic.Documentation.AppliesTo; using Elastic.Documentation.Configuration.Assembler; using Elastic.Documentation.Configuration.Builder; +using Elastic.Documentation.Configuration.LegacyUrlMappings; +using Elastic.Documentation.Configuration.Products; using Elastic.Documentation.Configuration.Versions; -using Elastic.Documentation.Legacy; using Elastic.Documentation.Site.FileProviders; using Elastic.Documentation.Site.Navigation; using Elastic.Markdown.IO; @@ -26,7 +27,6 @@ public class IndexViewModel public required string MarkdownHtml { get; init; } public required IReadOnlyCollection PageTocItems { get; init; } public required MarkdownFile CurrentDocument { get; init; } - public required INavigationItem CurrentNavigationItem { get; init; } public required INavigationItem? PreviousDocument { get; init; } public required INavigationItem? NextDocument { get; init; } @@ -39,7 +39,7 @@ public class IndexViewModel public required string? AllVersionsUrl { get; init; } public required LegacyPageMapping[]? LegacyPages { get; init; } - public required VersionDrownDownItemViewModel[]? VersionDropdownItems { get; init; } + public required VersionDropDownItemViewModel[]? VersionDropdownItems { get; init; } public required string? UrlPathPrefix { get; init; } public required string? GithubEditUrl { get; init; } public required string MarkdownUrl { get; init; } @@ -61,7 +61,7 @@ public class IndexViewModel public required string StructuredBreadcrumbsJson { get; init; } } -public class VersionDrownDownItemViewModel +public class VersionDropDownItemViewModel { [JsonPropertyName("name")] public required string Name { get; init; } @@ -73,45 +73,49 @@ public class VersionDrownDownItemViewModel public required bool IsDisabled { get; init; } [JsonPropertyName("children")] - public required VersionDrownDownItemViewModel[]? Children { get; init; } + public required VersionDropDownItemViewModel[]? Children { get; init; } // This logic currently only handles one level of children. Although the model supports multiple levels, it is not currently used. - public static VersionDrownDownItemViewModel[]? FromLegacyPageMappings(LegacyPageMapping[]? legacyPageMappings) + public static VersionDropDownItemViewModel[]? FromLegacyPageMappings(LegacyPageMapping[]? legacyPageMappings) { - if (legacyPageMappings is null) + if (legacyPageMappings is null || legacyPageMappings.Length == 0) return null; var groupedVersions = GroupByMajorVersion(legacyPageMappings); - return groupedVersions.Select(m => + + List versions = []; + foreach (var versionGroup in groupedVersions) { - // If there is more than one version, we need to create a dropdown - if (m.Value.Count != 1) + if (versionGroup.Value.Count != 1) { - return new VersionDrownDownItemViewModel + versions.Add(new VersionDropDownItemViewModel { - Name = m.Key, + Name = versionGroup.Key, Href = null, IsDisabled = false, - Children = m.Value.Select(v => new VersionDrownDownItemViewModel + Children = versionGroup.Value.Select(v => new VersionDropDownItemViewModel { Name = v, Href = legacyPageMappings.First(x => x.Version == v).ToString(), IsDisabled = !legacyPageMappings.First(x => x.Version == v).Exists, Children = null }).ToArray() - }; + }); } + else + { + var legacyPageMapping = legacyPageMappings.First(x => x.Version == versionGroup.Value.First()); - var legacyPageMapping = legacyPageMappings.First(x => x.Version == m.Value.First()); + versions.Add(new VersionDropDownItemViewModel + { + Name = legacyPageMapping.Version, + Href = legacyPageMapping.ToString(), + IsDisabled = !legacyPageMapping.Exists, + Children = null + }); + } + } - // If there is only one version, we don't need to create a dropdown - return new VersionDrownDownItemViewModel - { - Name = legacyPageMapping.Version, - Href = legacyPageMapping.ToString(), - IsDisabled = !legacyPageMapping.Exists, - Children = null - }; - }).ToArray(); + return versions.ToArray(); } // The legacy page mappings provide a list of versions. @@ -132,5 +136,5 @@ private static Dictionary> GroupByMajorVersion(LegacyPageMa }); } -[JsonSerializable(typeof(VersionDrownDownItemViewModel[]))] +[JsonSerializable(typeof(VersionDropDownItemViewModel[]))] public partial class ViewModelSerializerContext : JsonSerializerContext; diff --git a/src/tooling/Elastic.Documentation.Tooling/DocumentationTooling.cs b/src/tooling/Elastic.Documentation.Tooling/DocumentationTooling.cs index 478a7ced1..58b881234 100644 --- a/src/tooling/Elastic.Documentation.Tooling/DocumentationTooling.cs +++ b/src/tooling/Elastic.Documentation.Tooling/DocumentationTooling.cs @@ -6,6 +6,8 @@ using System.Net.Sockets; using Actions.Core.Extensions; using Elastic.Documentation.Configuration; +using Elastic.Documentation.Configuration.LegacyUrlMappings; +using Elastic.Documentation.Configuration.Products; using Elastic.Documentation.Configuration.Versions; using Elastic.Documentation.Diagnostics; using Microsoft.Extensions.DependencyInjection; @@ -60,11 +62,15 @@ public static TBuilder AddDocumentationToolingDefaults(this TBuilder b var endpoints = sp.GetRequiredService(); var configurationFileProvider = sp.GetRequiredService(); var versionsConfiguration = sp.GetRequiredService(); + var products = sp.GetRequiredService(); + var legacyUrlMappings = sp.GetRequiredService(); return new ConfigurationContext { ConfigurationFileProvider = configurationFileProvider, VersionsConfiguration = versionsConfiguration, - Endpoints = endpoints + Endpoints = endpoints, + ProductsConfiguration = products, + LegacyUrlMappings = legacyUrlMappings }; }); diff --git a/src/tooling/Elastic.Documentation.Tooling/Exporters/ConfigurationExporter.cs b/src/tooling/Elastic.Documentation.Tooling/Exporters/ConfigurationExporter.cs index 4cf0926cc..7d59b76d1 100644 --- a/src/tooling/Elastic.Documentation.Tooling/Exporters/ConfigurationExporter.cs +++ b/src/tooling/Elastic.Documentation.Tooling/Exporters/ConfigurationExporter.cs @@ -54,6 +54,10 @@ public ValueTask FinishExportAsync(IDirectoryInfo outputFolder, Cancellati _logger.LogInformation("Exporting {Name} to {ConfigFolder}", versionsConfig.Name, configFolder.FullName); fs.File.Copy(versionsConfig.FullName, Path.Combine(configFolder.FullName, versionsConfig.Name), true); + var productsConfig = configurationFileProvider.ProductsFile; + _logger.LogInformation("Exporting {Name} to {ConfigFolder}", productsConfig.Name, configFolder.FullName); + fs.File.Copy(productsConfig.FullName, Path.Combine(configFolder.FullName, productsConfig.Name), true); + return default; } } diff --git a/src/tooling/docs-assembler/AssembleContext.cs b/src/tooling/docs-assembler/AssembleContext.cs index 131299530..cdef3e212 100644 --- a/src/tooling/docs-assembler/AssembleContext.cs +++ b/src/tooling/docs-assembler/AssembleContext.cs @@ -5,6 +5,8 @@ using System.IO.Abstractions; using Elastic.Documentation.Configuration; using Elastic.Documentation.Configuration.Assembler; +using Elastic.Documentation.Configuration.LegacyUrlMappings; +using Elastic.Documentation.Configuration.Products; using Elastic.Documentation.Configuration.Versions; using Elastic.Documentation.Diagnostics; @@ -27,6 +29,9 @@ public class AssembleContext : IDocumentationConfigurationContext /// public DocumentationEndpoints Endpoints { get; } + public ProductsConfiguration ProductsConfiguration { get; } + public LegacyUrlMappingConfiguration LegacyUrlMappings { get; } + public IDirectoryInfo CheckoutDirectory { get; } public IDirectoryInfo OutputDirectory { get; } @@ -52,6 +57,8 @@ public AssembleContext( ConfigurationFileProvider = configurationContext.ConfigurationFileProvider; VersionsConfiguration = configurationContext.VersionsConfiguration; Endpoints = configurationContext.Endpoints; + ProductsConfiguration = configurationContext.ProductsConfiguration; + LegacyUrlMappings = configurationContext.LegacyUrlMappings; if (!Configuration.Environments.TryGetValue(environment, out var env)) throw new Exception($"Could not find environment {environment}"); diff --git a/src/tooling/docs-assembler/AssembleSources.cs b/src/tooling/docs-assembler/AssembleSources.cs index 835cc9415..ca8848b6e 100644 --- a/src/tooling/docs-assembler/AssembleSources.cs +++ b/src/tooling/docs-assembler/AssembleSources.cs @@ -11,6 +11,7 @@ using Elastic.Documentation.Configuration; using Elastic.Documentation.Configuration.Assembler; using Elastic.Documentation.Configuration.Builder; +using Elastic.Documentation.Configuration.LegacyUrlMappings; using Elastic.Documentation.LinkIndex; using Elastic.Markdown.IO.Navigation; using Elastic.Markdown.Links.CrossLinks; @@ -42,7 +43,7 @@ public class AssembleSources public FrozenDictionary NavigationTocMappings { get; } - public FrozenDictionary> LegacyUrlMappings { get; } + public LegacyUrlMappingConfiguration LegacyUrlMappings { get; } public FrozenDictionary TocConfigurationMapping { get; } @@ -61,7 +62,6 @@ Cancel ctx { var linkIndexProvider = Aws3LinkIndexReader.CreateAnonymous(); var navigationTocMappings = GetTocMappings(context); - var legacyUrlMappings = GetLegacyUrlMappings(context); var uriResolver = new PublishEnvironmentUriResolver(navigationTocMappings, context.Environment); var crossLinkFetcher = new AssemblerCrossLinkFetcher(logFactory, context.Configuration, context.Environment, linkIndexProvider); @@ -74,7 +74,7 @@ Cancel ctx checkouts, configurationContext, navigationTocMappings, - legacyUrlMappings, + configurationContext.LegacyUrlMappings, uriResolver, crossLinkResolver, availableExporters @@ -90,7 +90,7 @@ private AssembleSources( Checkout[] checkouts, IConfigurationContext configurationContext, FrozenDictionary navigationTocMappings, - FrozenDictionary> legacyUrlMappings, + LegacyUrlMappingConfiguration legacyUrlMappings, PublishEnvironmentUriResolver uriResolver, ICrossLinkResolver crossLinkResolver, IReadOnlySet availableExporters @@ -145,44 +145,6 @@ IReadOnlySet availableExporters .ToFrozenDictionary(); } - private static FrozenDictionary> GetLegacyUrlMappings(AssembleContext context) - { - var dictionary = new Dictionary>(); - var reader = new YamlStreamReader(context.ConfigurationFileProvider.LegacyUrlMappingsFile, context.Collector); - foreach (var entry in reader.Read()) - { - switch (entry.Key) - { - case "mappings": - ReadHistoryMappings(dictionary, reader, entry); - break; - } - } - - return dictionary.OrderByDescending(x => x.Key.Length).ToFrozenDictionary(); - - static void ReadHistoryMappings(IDictionary> dictionary, YamlStreamReader reader, YamlToplevelKey entry) - { - if (entry.Entry.Value is not YamlMappingNode mappings) - { - reader.EmitWarning($"It wasn't possible to read the mappings"); - return; - } - - foreach (var mapping in mappings) - { - var mappingKey = $"{((YamlScalarNode)mapping.Key).Value}"; - var mappingValues = ((YamlSequenceNode)mapping.Value).Children.OfType().Where(x => x.Value is not null).Select(x => x.Value!) - .ToList(); - if (dictionary.TryGetValue(mappingKey, out _)) - reader.EmitWarning($"'{mappingKey}' is already mapped to '{mappingValues}'"); - else - dictionary[mappingKey] = mappingValues; - } - } - } - - public static FrozenDictionary GetTocMappings(AssembleContext context) { var dictionary = new Dictionary(); diff --git a/src/tooling/docs-assembler/Building/AssemblerBuilder.cs b/src/tooling/docs-assembler/Building/AssemblerBuilder.cs index deb0d6a06..776a5e8d5 100644 --- a/src/tooling/docs-assembler/Building/AssemblerBuilder.cs +++ b/src/tooling/docs-assembler/Building/AssemblerBuilder.cs @@ -7,7 +7,7 @@ using Documentation.Assembler.Navigation; using Elastic.Documentation; using Elastic.Documentation.Configuration.Assembler; -using Elastic.Documentation.Legacy; +using Elastic.Documentation.Configuration.LegacyUrlMappings; using Elastic.Documentation.Links; using Elastic.Documentation.Serialization; using Elastic.Documentation.Tooling.Arguments; diff --git a/src/tooling/docs-assembler/Cli/RepositoryCommands.cs b/src/tooling/docs-assembler/Cli/RepositoryCommands.cs index 31f85c2d7..0d1ddc632 100644 --- a/src/tooling/docs-assembler/Cli/RepositoryCommands.cs +++ b/src/tooling/docs-assembler/Cli/RepositoryCommands.cs @@ -16,6 +16,7 @@ using Elastic.Documentation; using Elastic.Documentation.Configuration; using Elastic.Documentation.Configuration.Assembler; +using Elastic.Documentation.Configuration.Builder; using Elastic.Documentation.LegacyDocs; using Elastic.Documentation.Tooling.Arguments; using Elastic.Documentation.Tooling.Diagnostics.Console; @@ -181,7 +182,8 @@ public async Task BuildAll( var pathProvider = new GlobalNavigationPathProvider(navigationFile, assembleSources, assembleContext); var htmlWriter = new GlobalNavigationHtmlWriter(logFactory, navigation, collector); var legacyPageChecker = new LegacyPageChecker(); - var historyMapper = new PageLegacyUrlMapper(legacyPageChecker, assembleSources.LegacyUrlMappings); + + var historyMapper = new PageLegacyUrlMapper(legacyPageChecker, assembleContext.VersionsConfiguration, configurationContext.LegacyUrlMappings); var builder = new AssemblerBuilder(logFactory, assembleContext, navigation, htmlWriter, pathProvider, historyMapper); diff --git a/src/tooling/docs-assembler/Legacy/PageLegacyUrlMapper.cs b/src/tooling/docs-assembler/Legacy/PageLegacyUrlMapper.cs index 7f2228495..08b883bc4 100644 --- a/src/tooling/docs-assembler/Legacy/PageLegacyUrlMapper.cs +++ b/src/tooling/docs-assembler/Legacy/PageLegacyUrlMapper.cs @@ -2,47 +2,47 @@ // Elasticsearch B.V licenses this file to you under the Apache 2.0 License. // See the LICENSE file in the project root for more information -using Elastic.Documentation.Legacy; +using Elastic.Documentation.Configuration.LegacyUrlMappings; +using Elastic.Documentation.Configuration.Versions; using Elastic.Documentation.LegacyDocs; namespace Documentation.Assembler.Legacy; public record PageLegacyUrlMapper : ILegacyUrlMapper { - private IReadOnlyDictionary> PreviousUrls { get; } private LegacyPageChecker LegacyPageChecker { get; } - public PageLegacyUrlMapper(LegacyPageChecker legacyPageChecker, IReadOnlyDictionary> previousUrls) + private string DefaultVersion { get; } + private LegacyUrlMappingConfiguration LegacyUrlMappings { get; } + public PageLegacyUrlMapper(LegacyPageChecker legacyPageChecker, VersionsConfiguration versions, LegacyUrlMappingConfiguration legacyUrlMappings) { - PreviousUrls = previousUrls; LegacyPageChecker = legacyPageChecker; + DefaultVersion = $"{versions.VersioningSystems[VersioningSystemId.Stack].Base.Major}.{versions.VersioningSystems[VersioningSystemId.Stack].Base.Minor}"; + LegacyUrlMappings = legacyUrlMappings; } + public IReadOnlyCollection? MapLegacyUrl(IReadOnlyCollection? mappedPages) { - if (mappedPages is null) + if (mappedPages is null || mappedPages.Count == 0) return null; - if (mappedPages.Count == 0) - return [new LegacyPageMapping(mappedPages.FirstOrDefault() ?? string.Empty, string.Empty, false)]; - var mappedPage = mappedPages.First(); - var versions = PreviousUrls.FirstOrDefault(kv => + if (LegacyUrlMappings.Mappings.FirstOrDefault(x => mappedPage.Contains(x.BaseUrl, StringComparison.OrdinalIgnoreCase)) is not { } legacyMappingMatch) + { + return [new LegacyPageMapping(LegacyUrlMappings.Mappings.First(x => x.Product.Id.Equals("elastic-stack", StringComparison.InvariantCultureIgnoreCase)).Product, mappedPages.FirstOrDefault() ?? string.Empty, DefaultVersion, false)]; + } + + var allVersions = new List(); + + allVersions.AddRange(legacyMappingMatch.LegacyVersions.Select(x => { - var (key, _) = kv; - return mappedPage.Contains(key, StringComparison.OrdinalIgnoreCase); - }); - - if (versions.Value is null) - return [new LegacyPageMapping(mappedPages.FirstOrDefault() ?? string.Empty, string.Empty, false)]; - return versions.Value - .Select(v => - { - var legacyPageMapping = new LegacyPageMapping(mappedPage, v, true); - var path = Uri.TryCreate(legacyPageMapping.ToString(), UriKind.Absolute, out var uri) ? uri : null; - var exists = LegacyPageChecker.PathExists(path?.AbsolutePath!); - return legacyPageMapping with { Exists = exists }; - } - ).ToArray(); + var mapping = new LegacyPageMapping(legacyMappingMatch.Product, mappedPage, x, false); + var path = Uri.TryCreate(mapping.ToString(), UriKind.Absolute, out var uri) ? uri : null; + var exists = path is not null && LegacyPageChecker.PathExists(path.AbsolutePath); + return mapping with { Exists = exists }; + })); + + return allVersions; } } diff --git a/tests/Elastic.ApiExplorer.Tests/TestHelpers.cs b/tests/Elastic.ApiExplorer.Tests/TestHelpers.cs index 732366b6c..6c62afbf9 100644 --- a/tests/Elastic.ApiExplorer.Tests/TestHelpers.cs +++ b/tests/Elastic.ApiExplorer.Tests/TestHelpers.cs @@ -2,16 +2,19 @@ // Elasticsearch B.V licenses this file to you under the Apache 2.0 License. // See the LICENSE file in the project root for more information +using System.Collections.Frozen; using System.IO.Abstractions; using Elastic.Documentation; using Elastic.Documentation.Configuration; +using Elastic.Documentation.Configuration.LegacyUrlMappings; +using Elastic.Documentation.Configuration.Products; using Elastic.Documentation.Configuration.Versions; namespace Elastic.ApiExplorer.Tests; public static class TestHelpers { - public static IConfigurationContext CreateConfigurationContext(IFileSystem fileSystem, VersionsConfiguration? versionsConfiguration = null) + public static IConfigurationContext CreateConfigurationContext(IFileSystem fileSystem, VersionsConfiguration? versionsConfiguration = null, ProductsConfiguration? productsConfiguration = null) { versionsConfiguration ??= new VersionsConfiguration { @@ -25,7 +28,21 @@ public static IConfigurationContext CreateConfigurationContext(IFileSystem fileS Base = new SemVersion(8, 0, 0) } } - } + }, + }; + productsConfiguration ??= new ProductsConfiguration + { + Products = new Dictionary() + { + { + "elasticsearch", new Product + { + Id = "elasticsearch", + DisplayName = "Elasticsearch", + VersioningSystem = versionsConfiguration.GetVersioningSystem(VersioningSystemId.Stack) + } + } + }.ToFrozenDictionary() }; return new ConfigurationContext { @@ -34,7 +51,9 @@ public static IConfigurationContext CreateConfigurationContext(IFileSystem fileS Elasticsearch = ElasticsearchEndpoint.Default, }, ConfigurationFileProvider = new ConfigurationFileProvider(fileSystem), - VersionsConfiguration = versionsConfiguration + VersionsConfiguration = versionsConfiguration, + ProductsConfiguration = productsConfiguration, + LegacyUrlMappings = new LegacyUrlMappingConfiguration { Mappings = [] }, }; } } diff --git a/tests/Elastic.Markdown.Tests/TestHelpers.cs b/tests/Elastic.Markdown.Tests/TestHelpers.cs index 1d40f4318..3b1a95e1e 100644 --- a/tests/Elastic.Markdown.Tests/TestHelpers.cs +++ b/tests/Elastic.Markdown.Tests/TestHelpers.cs @@ -2,16 +2,19 @@ // Elasticsearch B.V licenses this file to you under the Apache 2.0 License. // See the LICENSE file in the project root for more information +using System.Collections.Frozen; using System.IO.Abstractions; using Elastic.Documentation; using Elastic.Documentation.Configuration; +using Elastic.Documentation.Configuration.LegacyUrlMappings; +using Elastic.Documentation.Configuration.Products; using Elastic.Documentation.Configuration.Versions; namespace Elastic.Markdown.Tests; public static class TestHelpers { - public static IConfigurationContext CreateConfigurationContext(IFileSystem fileSystem, VersionsConfiguration? versionsConfiguration = null) + public static IConfigurationContext CreateConfigurationContext(IFileSystem fileSystem, VersionsConfiguration? versionsConfiguration = null, ProductsConfiguration? productsConfiguration = null) { versionsConfiguration ??= new VersionsConfiguration { @@ -25,7 +28,37 @@ public static IConfigurationContext CreateConfigurationContext(IFileSystem fileS Base = new SemVersion(8, 0, 0) } } - } + }, + }; + productsConfiguration ??= new ProductsConfiguration + { + Products = new Dictionary + { + { + "elasticsearch", new Product + { + Id = "elasticsearch", + DisplayName = "Elasticsearch", + VersioningSystem = versionsConfiguration.GetVersioningSystem(VersioningSystemId.Stack) + } + }, + { + "apm", new Product + { + Id = "apm", + DisplayName = "APM", + VersioningSystem = versionsConfiguration.GetVersioningSystem(VersioningSystemId.Stack) + } + }, + { + "apm-agent", new Product + { + Id = "apm-agent", + DisplayName = "APM Agent", + VersioningSystem = versionsConfiguration.GetVersioningSystem(VersioningSystemId.Stack) + } + } + }.ToFrozenDictionary() }; return new ConfigurationContext { @@ -34,7 +67,9 @@ public static IConfigurationContext CreateConfigurationContext(IFileSystem fileS Elasticsearch = ElasticsearchEndpoint.Default, }, ConfigurationFileProvider = new ConfigurationFileProvider(fileSystem), - VersionsConfiguration = versionsConfiguration + VersionsConfiguration = versionsConfiguration, + ProductsConfiguration = productsConfiguration, + LegacyUrlMappings = new LegacyUrlMappingConfiguration { Mappings = [] }, }; } } diff --git a/tests/authoring/Framework/Setup.fs b/tests/authoring/Framework/Setup.fs index a88e0a499..ae06276b0 100644 --- a/tests/authoring/Framework/Setup.fs +++ b/tests/authoring/Framework/Setup.fs @@ -6,13 +6,16 @@ namespace authoring open System +open System.Collections.Frozen open System.Collections.Generic open System.IO open System.IO.Abstractions.TestingHelpers open System.Threading.Tasks open Elastic.Documentation open Elastic.Documentation.Configuration +open Elastic.Documentation.Configuration.LegacyUrlMappings open Elastic.Documentation.Configuration.Versions +open Elastic.Documentation.Configuration.Products open Elastic.Markdown open Elastic.Markdown.IO open JetBrains.Annotations @@ -260,11 +263,24 @@ type Setup = ) let versionConfig = VersionsConfiguration(VersioningSystems = versioningSystems) + let productDict = Dictionary() + productDict.Add("elasticsearch", Product(Id = "elasticsearch", + DisplayName = "Elasticsearch", + VersioningSystem = versionConfig.VersioningSystems[VersioningSystemId.ElasticsearchProject])) + productDict.Add("apm_agent_dotnet", Product(Id = "apm_agent_dotnet", + DisplayName = "APM Agent for .NET", + VersioningSystem = versionConfig.VersioningSystems[VersioningSystemId.ApmAgentDotnet])) + productDict.Add("ecctl", Product(Id = "ecctl", + DisplayName = "Elastic Cloud Control ECCTL", + VersioningSystem = versionConfig.VersioningSystems[VersioningSystemId.Ecctl])) + let configurationFileProvider = ConfigurationFileProvider(fileSystem) let configurationContext = ConfigurationContext( VersionsConfiguration = versionConfig, ConfigurationFileProvider = configurationFileProvider, - Endpoints=DocumentationEndpoints(Elasticsearch = ElasticsearchEndpoint.Default) + Endpoints=DocumentationEndpoints(Elasticsearch = ElasticsearchEndpoint.Default), + ProductsConfiguration = ProductsConfiguration(Products = productDict.ToFrozenDictionary()), + LegacyUrlMappings = LegacyUrlMappingConfiguration(Mappings = []) ) let context = BuildContext( collector, diff --git a/tests/docs-assembler.Tests/src/docs-assembler.Tests/AssemblerConfigurationTests.cs b/tests/docs-assembler.Tests/src/docs-assembler.Tests/AssemblerConfigurationTests.cs index 9cc16ea4f..732ef142f 100644 --- a/tests/docs-assembler.Tests/src/docs-assembler.Tests/AssemblerConfigurationTests.cs +++ b/tests/docs-assembler.Tests/src/docs-assembler.Tests/AssemblerConfigurationTests.cs @@ -65,6 +65,7 @@ public AssemblerConfigurationTests() public void ReadsConfigurationFiles() { Context.ConfigurationFileProvider.VersionFile.Name.Should().Be("versions.yml"); + Context.ConfigurationFileProvider.ProductsFile.Name.Should().Be("products.yml"); Context.ConfigurationFileProvider.NavigationFile.Name.Should().Be("navigation.yml"); Context.ConfigurationFileProvider.AssemblerFile.Name.Should().Be("assembler.yml"); Context.ConfigurationFileProvider.LegacyUrlMappingsFile.Name.Should().Be("legacy-url-mappings.yml"); diff --git a/tests/docs-assembler.Tests/src/docs-assembler.Tests/TestHelpers.cs b/tests/docs-assembler.Tests/src/docs-assembler.Tests/TestHelpers.cs index d6445c297..ace2d9f5c 100644 --- a/tests/docs-assembler.Tests/src/docs-assembler.Tests/TestHelpers.cs +++ b/tests/docs-assembler.Tests/src/docs-assembler.Tests/TestHelpers.cs @@ -2,9 +2,12 @@ // Elasticsearch B.V licenses this file to you under the Apache 2.0 License. // See the LICENSE file in the project root for more information +using System.Collections.Frozen; using System.IO.Abstractions; using Elastic.Documentation; using Elastic.Documentation.Configuration; +using Elastic.Documentation.Configuration.LegacyUrlMappings; +using Elastic.Documentation.Configuration.Products; using Elastic.Documentation.Configuration.Versions; namespace Documentation.Assembler.Tests; @@ -14,7 +17,8 @@ public static class TestHelpers public static IConfigurationContext CreateConfigurationContext( IFileSystem fileSystem, VersionsConfiguration? versionsConfiguration = null, - ConfigurationFileProvider? configurationFileProvider = null + ConfigurationFileProvider? configurationFileProvider = null, + ProductsConfiguration? productsConfiguration = null ) { configurationFileProvider ??= new ConfigurationFileProvider(fileSystem); @@ -30,7 +34,21 @@ public static IConfigurationContext CreateConfigurationContext( Base = new SemVersion(8, 0, 0) } } - } + }, + }; + productsConfiguration ??= new ProductsConfiguration + { + Products = new Dictionary + { + { + "elasticsearch", new Product + { + Id = "elasticsearch", + DisplayName = "Elasticsearch", + VersioningSystem = versionsConfiguration.GetVersioningSystem(VersioningSystemId.Stack) + } + } + }.ToFrozenDictionary() }; return new ConfigurationContext { @@ -39,7 +57,9 @@ public static IConfigurationContext CreateConfigurationContext( Elasticsearch = ElasticsearchEndpoint.Default, }, ConfigurationFileProvider = configurationFileProvider, - VersionsConfiguration = versionsConfiguration + VersionsConfiguration = versionsConfiguration, + ProductsConfiguration = productsConfiguration, + LegacyUrlMappings = new LegacyUrlMappingConfiguration { Mappings = [] }, }; } }