diff --git a/client/utilities/rfc-index-txt.ts b/client/utilities/rfc-index-txt.ts index f525973..c265081 100644 --- a/client/utilities/rfc-index-txt.ts +++ b/client/utilities/rfc-index-txt.ts @@ -2,6 +2,8 @@ import { DateTime } from 'luxon' import { padStart } from 'lodash-es' import { SPACE } from './strings' import type { ApiClient, RfcMetadata } from '~/generated/red-client' +import type { ExtraFieldsNeeded } from './rfc.mocks' +import { getRFCWithExtraFields } from './rfc.mocks' // Note: this file is intentionally named rfc-index-txt.ts not rfc-index.txt.ts // because vitest can't import that later filename @@ -162,110 +164,6 @@ const stringifyAuthor = (author: RfcMetadata['authors'][number]): string => { return author.affiliation === 'Editor' ? `${name}, Ed.` : name } -type ExtraFieldNeededFormat = - | { - type: 'TXT' - } - | { type: 'HTML' } - | { type: 'HTMLized' } - | { type: 'PDF' } - | { type: 'PS' } - -type ExtraFieldNeededObsolete = { - number: number -} - -type ExtraFieldNeededUpdate = { - number: number -} - -type ExtraFieldNeededIsAlso = { - number: number -} - -type ExtraFieldNeededSeeAlso = { - number: number -} - -type ExtraFieldsNeeded = { - formats: ExtraFieldNeededFormat[] - obsoletes: ExtraFieldNeededObsolete[] - updates: ExtraFieldNeededUpdate[] - is_also: ExtraFieldNeededIsAlso[] - see_also: ExtraFieldNeededSeeAlso[] -} - -const missingData: Record> = { - 1: { - authors: [ - { - name: 'Steve Crocker', - person: 0 - } - ], - formats: [{ type: 'TXT' }, { type: 'HTML' }] - }, - 2: { - authors: [{ name: 'Bill Duvall', person: 0 }], - formats: [{ type: 'TXT' }, { type: 'PDF' }, { type: 'HTML' }] - }, - 3: { - authors: [{ name: 'Steve D. Crocker', person: 0 }], - formats: [{ type: 'TXT' }, { type: 'HTML' }] - }, - 4: { - authors: [{ name: 'E. B. Shapiro', person: 0 }], - formats: [{ type: 'TXT' }, { type: 'HTML' }] - }, - 5: { - authors: [{ name: 'J. Rulifson', person: 0 }], - formats: [{ type: 'TXT' }, { type: 'HTML' }] - }, - 6: { - authors: [{ name: 'S.D. Crocker.', person: 0 }], - formats: [{ type: 'TXT' }, { type: 'HTML' }] - }, - 7: { - authors: [{ name: 'G. Deloche', person: 0 }], - formats: [{ type: 'TXT' }, { type: 'HTML' }] - }, - 8: { - authors: [{ name: 'G. Deloche', person: 0 }], - formats: [{ type: 'PDF' }] - }, - 9: { - authors: [{ name: 'G. Deloche', person: 0 }], - formats: [{ type: 'PDF' }] - }, - 10: { - authors: [{ name: 'S.D. Crocker.', person: 0 }], - formats: [{ type: 'TXT' }, { type: 'HTML' }], - obsoletes: [{ number: 3 }], - updated_by: [ - { id: 24, number: 24, title: '?' }, - { id: 27, number: 27, title: '?' }, - { id: 30, number: 30, title: '?' } - ] - }, - 11: { - authors: [{ name: 'G. Deloche', person: 0 }], - formats: [{ type: 'TXT' }, { type: 'PDF' }, { type: 'HTML' }] - }, - 12: { - authors: [{ name: 'M. Wingfield', person: 0 }], - formats: [ - { type: 'TXT' }, - { type: 'PS' }, - { type: 'PDF' }, - { type: 'HTML' } - ] - }, - 13: { - authors: [{ name: 'Vincent Cerf', person: 0 }], - formats: [{ type: 'TXT' }, { type: 'HTML' }] - } -} - const formatRfcNumber = (number: number): string => { return `RFC${number.toString()}` } @@ -284,13 +182,7 @@ const stringifyRFC = ( let also = '' let doi = '' - const missingRfc = missingData[rfcMetadata.number] - - const rfc: RfcMetadata & Partial = { - ...missingRfc, - ...rfcMetadata, - authors: [...toArray(missingRfc?.authors), ...toArray(rfcMetadata?.authors)] - } + const rfc = getRFCWithExtraFields(rfcMetadata) if (rfc.title === 'Not Issued') { return 'Not Issued.' diff --git a/client/utilities/rfc-index-xml.ts b/client/utilities/rfc-index-xml.ts index 988e3c3..77193af 100644 --- a/client/utilities/rfc-index-xml.ts +++ b/client/utilities/rfc-index-xml.ts @@ -1,6 +1,7 @@ import { DateTime } from 'luxon' import { XMLBuilder } from 'fast-xml-parser' import type { ApiClient } from '~/generated/red-client' +import { getRFCWithExtraFields } from './rfc.mocks' type DocListArg = Parameters[0] @@ -78,23 +79,25 @@ const renderRFCs = async ({ const response = await redApi.red.docList(docListArg) response.results.forEach((rfcMetadata) => { + const rfc = getRFCWithExtraFields(rfcMetadata) + const [month, year] = DateTime.fromISO(rfcMetadata.published) .toFormat('LLLL yyyy') .split(' ') - const abstractParagraphs = (rfcMetadata.abstract ?? '').split('\n') + const abstractParagraphs = (rfc.abstract ?? '').split('\n') // FIXME: replace with RFC formats when the API has them const formats = ['HTML', 'TEXT', 'PDF', 'XML'] - const rfc = { + const rfcForXml = { 'rfc-entry': { - 'doc-id': `RFC${rfcMetadata.number}`, - title: rfcMetadata.title, - ...(rfcMetadata.authors.length > 0 ? + 'doc-id': `rfc${rfc.number}`, + title: rfc.title, + ...(rfc.authors.length > 0 ? { author: { - name: rfcMetadata.authors + name: rfc.authors } } : {}), @@ -102,10 +105,10 @@ const renderRFCs = async ({ format: { 'file-format': formats }, - 'page-count': rfcMetadata.pages, + 'page-count': rfc.pages, keywords: { // @ts-expect-error update when client provides that - kw: rfcMetadata.keywords + kw: rfc.keywords }, ...(abstractParagraphs.length > 0 ? { @@ -117,19 +120,18 @@ const renderRFCs = async ({ } : {}), draft: 'draft-ietf-lamps-cms-cek-hkdf-sha256-05', - 'current-status': rfcMetadata.status.slug, - 'publication-status': rfcMetadata.status.slug, - stream: rfcMetadata.stream.name, - area: rfcMetadata.area, - wg_acronym: rfcMetadata.group.acronym, + 'current-status': rfc.status.slug, + 'publication-status': rfc.status.slug, + stream: rfc.stream.name, + area: rfc.area, + wg_acronym: rfc.group.acronym, doi: - rfcMetadata.identifiers?.find( - (identifier) => identifier.type === 'doi' - )?.value ?? undefined + rfc.identifiers?.find((identifier) => identifier.type === 'doi') + ?.value ?? undefined } } - const xml = builder.build(rfc) + const xml = builder.build(rfcForXml) push(xml) }) diff --git a/client/utilities/rfc-index.xml.test.ts b/client/utilities/rfc-index.xml.test.ts index ea28337..d7cff57 100644 --- a/client/utilities/rfc-index.xml.test.ts +++ b/client/utilities/rfc-index.xml.test.ts @@ -2,6 +2,7 @@ import fs from 'node:fs' import path from 'node:path' import { z } from 'zod' +import { zip } from 'lodash' import { vi, describe, beforeEach, afterEach, test, expect } from 'vitest' import { XMLParser } from 'fast-xml-parser' @@ -11,6 +12,7 @@ import { ApiClient, type PaginatedRfcMetadataList } from '~/generated/red-client' +import { parseRFCId } from './rfc' const originalXMLString = fs .readFileSync(path.join(import.meta.dirname, 'rfc-index.xml'), 'utf-8') @@ -28,11 +30,14 @@ type DocListResponse = Awaited> * The following Zod schema is the minimal amount of structure needed to parse BCP/FYI/RFC/STD * entries with typing. * - * Why not refine the z.any() type further? That's unnecessary because we have reference XML (originalXML) + * We don't need to refine the EntrySchema value further because we have reference XML (originalXML) * to parse too which means the XMLParser should produce the same data from the same XML so we can use - * vitest to diff them. This also avoids any maintenance burden of keeping a Zod schema in sync with the - * output of XMLBuilder. + * vitest to diff them rather than validating their exact types. This also avoids any maintenance burden + * of keeping a Zod schema in sync with the output of XMLBuilder. */ +const EntryValueSchema = z + .record(z.string(), z.record(z.string(), z.any()).array()) + .array() const EntrySchema = z.record( z.enum([ 'bcp-entry', @@ -41,9 +46,11 @@ const EntrySchema = z.record( 'rfc-not-issued-entry', 'std-entry' ]), - z.any() + EntryValueSchema ) const EntriesSchema = EntrySchema.array() +type Entry = z.infer +type RFCEntry = Required> type TestHelperResponses = { oldestRfcResponse: DocListResponse @@ -93,29 +100,38 @@ const testHelper = (responses: TestHelperResponses) => })() }) -const filterByEntryTypeFactory = - (key: keyof z.infer) => - (entry: z.infer) => { - return key in entry - } +const filterByRFCEntry = (entry: Entry): entry is RFCEntry => { + return 'rfc-entry' in entry +} -const filterByRFCEntry = filterByEntryTypeFactory('rfc-entry') +// const summariseEntries = (entries: any[]) => +// entries.reduce( +// (acc, entry) => +// Object.keys(entry).reduce((acc, key, _index, arr) => { +// if (arr.length === 0) { +// throw Error(`Entry has no keys?`) +// } +// if (!acc[key]) { +// acc[key] = 0 +// } +// acc[key]++ +// return acc +// }, acc), +// {} +// ) -const summariseEntries = (entries: any[]) => - entries.reduce( - (acc, entry) => - Object.keys(entry).reduce((acc, key, _index, arr) => { - if (arr.length === 0) { - throw Error(`Entry has no keys?`) - } - if (!acc[key]) { - acc[key] = 0 - } - acc[key]++ - return acc - }, acc), - {} - ) +const getRFCNumber = (rfcEntry: RFCEntry): string => { + const docIdItem = rfcEntry['rfc-entry'].find((item) => { + return !!item['doc-id'] + }) + if (!docIdItem) throw Error(`Couldn't find doc-id in ${rfcEntry}`) + const rfcNumber = docIdItem['doc-id'][0]['#text'] + const typeofRFCNumber = typeof rfcNumber !== 'string' + if (typeofRFCNumber) { + throw Error(`Expected RFCNumber to be a string but was ${typeofRFCNumber}`) + } + return parseRFCId(rfcNumber).number +} describe('renderRfcIndexDotXml', () => { beforeEach(() => { @@ -136,14 +152,15 @@ describe('renderRfcIndexDotXml', () => { expect(originalXML[1]).toHaveProperty('rfc-index') const originalXMLEntries = originalXML[1]['rfc-index'] expect(originalXMLEntries.length).toBeGreaterThan(10) - const originalParseResult = EntriesSchema.safeParse(originalXMLEntries) - const originalParsedEntries = originalParseResult.data + const originalParsedXML = EntriesSchema.safeParse(originalXMLEntries) + const originalParsedEntries = originalParsedXML.data if (!originalParsedEntries) { - console.log(originalParseResult.error) + console.log(originalParsedXML.error) throw Error('Expected some results') } - expect(originalParseResult.success).toBeTruthy() - const originalRFCs = originalParsedEntries.filter(filterByRFCEntry) + expect(originalParsedXML.success).toBeTruthy() + const originalRFCs: RFCEntry[] = + originalParsedEntries.filter(filterByRFCEntry) expect(originalRFCs.length).toBeGreaterThan(10) const result = await testHelper({ @@ -159,21 +176,32 @@ describe('renderRfcIndexDotXml', () => { expect(resultXML[1]).toHaveProperty('rfc-index') const resultXMLEntries = resultXML[1]['rfc-index'] expect(resultXMLEntries.length).toBeGreaterThan(10) - const resultEntries = EntriesSchema.parse(resultXMLEntries) - const resultRFCs = resultEntries.filter(filterByRFCEntry) + const resultParsedXML = EntriesSchema.safeParse(resultXMLEntries) + const resultParsedEntries = resultParsedXML.data + if (!resultParsedEntries) { + console.log(resultParsedXML.error) + throw Error('Expected some results') + } + expect(resultParsedXML.success).toBeTruthy() + + const resultRFCs: RFCEntry[] = resultParsedEntries.filter(filterByRFCEntry) expect(resultRFCs.length).toBeGreaterThan(10) // Intentionally not using test.each() because that would continue running tests after one fails // whereas this will fail early and compete the tests much quicker resultRFCs.forEach((resultRFC, i) => { const originalRFC = originalRFCs[i] + const originalRFCNumber = getRFCNumber(originalRFC) + const resultRFCNumber = getRFCNumber(resultRFC) + + expect(resultRFCNumber).toEqual(originalRFCNumber) if ( // FIXME: enable checking more RFCs i < 14 ) { - console.log(`Checking RFC${i + 1}`) - expect(resultRFC).toBe(originalRFC) + console.log(`Checking ${originalRFCNumber}`, resultRFC, originalRFC) + expect(resultRFC).toEqual(originalRFC) } }) }) diff --git a/client/utilities/rfc.mocks.ts b/client/utilities/rfc.mocks.ts index 6686b5a..c3290f1 100644 --- a/client/utilities/rfc.mocks.ts +++ b/client/utilities/rfc.mocks.ts @@ -1,4 +1,4 @@ -import type { Rfc } from '../generated/red-client' +import type { Rfc, RfcMetadata } from '../generated/red-client' export const exampleRfc: Rfc = { number: 9703, @@ -72,3 +72,127 @@ export const exampleRfc: Rfc = { 'Egress Peer Engineering (EPE) is an application of Segment Routing (SR) that solves the problem of egress peer selection. The SR-based BGP-EPE solution allows a centralized controller, e.g., a Software-Defined Network (SDN) controller, to program any egress peer. The EPE solution requires the node or the SDN controller to program 1) the PeerNode Segment Identifier (SID) describing a session between two nodes, 2) the PeerAdj SID describing the link or links that are used by the sessions between peer nodes, and 3) the PeerSet SID describing any connected interface to any peer in the related group. This document provides new sub-TLVs for EPE-SIDs that are used in the Target FEC Stack TLV (Type 1) in MPLS Ping and Traceroute procedures.', text: '\n\n\n\nInternet Engineering Task Force (IETF) S. Hegde\nRequest for Comments: 9703 M. Srivastava\nCategory: Standards Track Juniper Networks Inc.\nISSN: 2070-1721 K. Arora\n Individual Contributor\n S. Ninan\n Ciena\n X. Xu\n China Mobile\n December 2024\n\n\n Label Switched Path (LSP) Ping/Traceroute for Segment Routing (SR)\nEgress Peer Engineering (EPE) Segment Identifiers (SIDs) with MPLS Data\n Plane\n\nAbstract\n\n Egress Peer Engineering (EPE) is an application of Segment Routing\n (SR) that solves the problem of egress peer selection. The SR-based\n BGP-EPE solution allows a centralized controller, e.g., a Software-\n Defined Network (SDN) controller, to program any egress peer. The\n EPE solution requires the node or the SDN controller to program 1)\n the PeerNode Segment Identifier (SID) describing a session between\n two nodes, 2) the PeerAdj SID describing the link or links that are\n used by the sessions between peer nodes, and 3) the PeerSet SID\n describing any connected interface to any peer in the related group.\n This document provides new sub-TLVs for EPE-SIDs that are used in the\n Target FEC Stack TLV (Type 1) in MPLS Ping and Traceroute procedures.\n\nStatus of This Memo\n\n This is an Internet Standards Track document.\n\n This document is a product of the Internet Engineering Task Force\n (IETF). It represents the consensus of the IETF community. It has\n received public review and has been approved for publication by the\n Internet Engineering Steering Group (IESG). Further information on\n Internet Standards is available in Section 2 of RFC 7841.\n\n Information about the current status of this document, any errata,\n and how to provide feedback on it may be obtained at\n https://www.rfc-editor.org/info/rfc9703.\n\nCopyright Notice\n\n Copyright (c) 2024 IETF Trust and the persons identified as the\n document authors. All rights reserved.\n\n This document is subject to BCP 78 and the IETF Trust\'s Legal\n Provisions Relating to IETF Documents\n (https://trustee.ietf.org/license-info) in effect on the date of\n publication of this document. Please review these documents\n carefully, as they describe your rights and restrictions with respect\n to this document. Code Components extracted from this document must\n include Revised BSD License text as described in Section 4.e of the\n Trust Legal Provisions and are provided without warranty as described\n in the Revised BSD License.\n\nTable of Contents\n\n 1. Introduction\n 2. Theory of Operation\n 3. Requirements Language\n 4. FEC Definitions\n 4.1. PeerNode SID Sub-TLV\n 4.2. PeerAdj SID Sub-TLV\n 4.3. PeerSet SID Sub-TLV\n 5. EPE-SID FEC Validation\n 5.1. EPE-SID FEC Validation Rules\n 6. IANA Considerations\n 7. Security Considerations\n 8. References\n 8.1. Normative References\n 8.2. Informative References\n Appendix A. Examples of Programmed States\n Acknowledgments\n Authors\' Addresses\n\n1. Introduction\n\n Egress Peer Engineering (EPE), as defined in [RFC9087], is an\n effective mechanism that is used to select the egress peer link based\n on different criteria. In this scenario, egress peers may belong to\n a completely different ownership. The EPE-SIDs provide the means to\n represent egress peer nodes, links, sets of links, and sets of nodes.\n Many network deployments have built their networks consisting of\n multiple Autonomous Systems (ASes) either for the ease of operations\n or as a result of network mergers and acquisitions. The inter-AS\n links connecting any two ASes could be traffic-engineered using EPE-\n SIDs in this case, where there is single ownership but different AS\n numbers. It is important to validate the control plane to forwarding\n plane synchronization for these SIDs so that any anomaly can be\n easily detected by the network operator. EPE-SIDs may also be used\n in an ingress Segment Routing (SR) policy [RFC9256] to choose exit\n points where the remote AS has a completely different ownership.\n This scenario is out of scope for this document.\n\n +---------+ +------+\n | | | |\n | H B------D G\n | | +---/| AS2 |\\ +------+\n | |/ +------+ \\ | |---L/8\n A AS1 C---+ \\| |\n | |\\\\ \\ +------+ /| AS4 |---M/8\n | | \\\\ +-E |/ +------+\n | X | \\\\ | K\n | | +===F AS3 |\n +---------+ +------+\n\n Figure 1: Reference Diagram\n\n In Figure 1, EPE-SIDs are configured on AS1 towards AS2 and AS3 and\n advertised in the Border Gateway Protocol - Link State (BGP-LS)\n [RFC9086]. In certain cases, the EPE-SIDs advertised by the control\n plane may not be in synchronization with the label programmed in the\n data plane. For example, on C, a PeerAdj SID could be advertised to\n indicate it is for the link C->D. Due to some software anomaly, the\n actual data forwarding on this PeerAdj SID could be happening over\n the C->E link. If E had relevant data paths for further forwarding\n the packet, this kind of anomaly would go unnoticed by the network\n operator. A detailed example of a correctly programmed state and an\n incorrectly programmed state along with a description of how the\n incorrect state can be detected is described in Appendix A. A\n Forwarding Equivalence Class (FEC) definition for the EPE-SIDs will\n detail the control plane association of the SID. The data plane\n validation of the SID will be done during the MPLS Traceroute\n procedure. When there is a multi-hop External BGP (EBGP) session\n between the ASBRs, a PeerNode SID is advertised, and the traffic MAY\n be load-balanced between the interfaces connecting the two nodes. In\n Figure 1, C and F could have a PeerNode SID advertised. When the\n Operations, Administration, and Maintenance (OAM) packet is received\n on F, it needs to be validated that the packet came from one of the\n two interfaces connected to C.\n\n This document provides Target Forwarding Equivalence Class (FEC)\n Stack TLV definitions for EPE-SIDs. This solution requires the node\n constructing the Target FEC Stack TLV to determine the types of SIDs\n along the path of the LSP. Other procedures for MPLS Ping and\n Traceroute, as defined in Section 7 of [RFC8287] and clarified in\n [RFC8690], are applicable for EPE-SIDs as well.\n\n2. Theory of Operation\n\n [RFC9086] provides mechanisms to advertise the EPE-SIDs in BGP-LS.\n These EPE-SIDs may be used to build SR paths and may be communicated\n using extensions described in [SR-SEGTYPES] and [SR-BGP-POLICY] or\n Path Computation Element Protocol (PCEP) extensions as defined in\n [RFC8664]. Data plane monitoring for such paths that consist of EPE-\n SIDs will use extensions defined in this document to build the Target\n FEC Stack TLV. The MPLS Ping and Traceroute procedures MAY be\n initiated by the head-end of the SR path or a centralized topology-\n aware data plane monitoring system, as described in [RFC8403]. The\n extensions in [SR-SEGTYPES], [SR-BGP-POLICY], and [RFC8664] do not\n define how to acquire and carry the details of the SID that can be\n used to construct the FEC. Such extensions are out of scope for this\n document. The node initiating the data plane monitoring may acquire\n the details of EPE-SIDs through BGP-LS advertisements, as described\n in [RFC9086]. There may be other possible mechanisms that can be\n used to learn the definition of the SID from the controller. Details\n of such mechanisms are out of scope for this document.\n\n The EPE-SIDs are advertised for inter-AS links that run EBGP\n sessions. [RFC9086] does not define the detailed procedures of how\n to operate EBGP sessions in a scenario with unnumbered interfaces.\n Therefore, these scenarios are out of scope for this document.\n Anycast and multicast addresses are not in the scope of this\n document. During the AS migration scenario, procedures described in\n [RFC7705] may be in force. In these scenarios, if the local and\n remote AS fields in the FEC (as described in Section 4) carry the\n globally configured AS Number and not the "local AS" (as defined in\n [RFC7705]), then the FEC validation procedures may fail.\n\n As described in Section 1, this document defines Target FEC Stack\n TLVs for EPE-SIDs that can be used in detecting MPLS data plane\n failures [RFC8029]. This mechanism applies to paths created across\n ASes of cooperating administrations. If the ping or traceroute\n packet enters a non-cooperating AS domain, it might be dropped by the\n routers in the non-cooperating domain. Although a complete path\n validation cannot be done across non-cooperating domains, it still\n provides useful information that the ping or traceroute packet\n entered a non-cooperating domain.\n\n3. Requirements Language\n\n The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",\n "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and\n "OPTIONAL" in this document are to be interpreted as described in BCP\n 14, [RFC2119], [RFC8174] when, and only when, they appear in all\n capitals, as shown here.\n\n4. FEC Definitions\n\n In this document, three new sub-TLVs are defined for the Target FEC\n Stack TLV (Type 1), the Reverse-Path Target FEC Stack TLV (Type 16),\n and the Reply Path TLV (Type 21); see Table 1.\n\n4.1. PeerNode SID Sub-TLV\n\n 0 1 2 3\n 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1\n +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n |Type = 39 | Length |\n +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n | Local AS Number (4 octets) |\n +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n | Remote AS Number (4 octets) |\n +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n | Local BGP Router ID (4 octets) |\n +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n | Remote BGP Router ID (4 octets) |\n +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n\n Figure 2: PeerNode SID Sub-TLV\n\n Type: 2 octets\n\n Value: 39\n\n Length: 2 octets\n\n Value: 16\n\n Local AS Number: 4 octets. The unsigned integer representing the AS\n number [RFC6793] of the AS to which the PeerNode SID advertising\n node belongs. If Confederations [RFC5065] are in use, and if the\n remote node is a member of a different Member-AS within the local\n Confederation, this is the Member-AS Number inside the\n Confederation and not the Confederation Identifier.\n\n Remote AS Number: 4 octets. The unsigned integer representing the\n AS number [RFC6793] of the AS of the remote node for which the\n PeerNode SID is advertised. If Confederations [RFC5065] are in\n use, and if the remote node is a member of a different Member-AS\n within the local Confederation, this is the Member-AS Number\n inside the Confederation and not the Confederation Identifier.\n\n Local BGP Router ID: 4 octets. The unsigned integer representing\n the BGP Identifier of the PeerNode SID advertising node as defined\n in [RFC4271] and [RFC6286].\n\n Remote BGP Router ID: 4 octets. The unsigned integer representing\n the BGP Identifier of the remote node as defined in [RFC4271] and\n [RFC6286].\n\n When there is a multi-hop EBGP session between two ASBRs, a PeerNode\n SID is advertised for this session, and traffic can be load-balanced\n across these interfaces. An EPE controller that performs bandwidth\n management for these links should be aware of the links on which the\n traffic will be load-balanced. As per [RFC8029], the node\n advertising the EPE-SIDs will send a Downstream Detailed Mapping\n (DDMAP) TLV specifying the details of the next-hop interfaces. Based\n on this information, the controller MAY choose to verify the actual\n forwarding state with the topology information that the controller\n has. On the router, the validation procedures will include the\n received DDMAP validation, as specified in [RFC8029], to verify the\n control state and the forwarding state synchronization on the two\n routers. Any discrepancies between the controller\'s state and the\n forwarding state will not be detected by the procedures described in\n this document.\n\n4.2. PeerAdj SID Sub-TLV\n\n 0 1 2 3\n 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1\n +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n |Type = 38 | Length |\n +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n | Adj type | RESERVED |\n +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n | Local AS Number (4 octets) |\n +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n | Remote AS Number (4 octets) |\n +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n | Local BGP Router ID (4 octets) |\n +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n | Remote BGP Router ID (4 octets) |\n +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n | Local Interface Address (4/16 octets) |\n +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n | Remote Interface Address (4/16 octets) |\n +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n\n Figure 3: PeerAdj SID Sub-TLV\n\n Type: 2 octets\n\n Value: 38\n\n Length: 2 octets\n\n Value: Variable based on the IPv4/IPv6 interface address. Length\n excludes the length of the Type and Length fields. For IPv4\n interface addresses, the length will be 28 octets. In the case of\n an IPv6 address, the length will be 52 octets.\n\n Adj type: 1 octet\n\n Value: Set to 1 when the Adjacency Segment is IPv4. Set to 2 when\n the Adjacency Segment is IPv6.\n\n RESERVED: 3 octets. MUST be zero when sending and ignored on\n receiving.\n\n Local AS Number: 4 octets. The unsigned integer representing the AS\n number [RFC6793] of the AS to which the PeerAdj SID advertising\n node belongs. If Confederations [RFC5065] are in use, and if the\n remote node is a member of a different Member-AS within the local\n Confederation, this is the Member-AS Number inside the\n Confederation and not the Confederation Identifier.\n\n Remote AS Number: 4 octets. The unsigned integer representing the\n AS number [RFC6793] of the remote node\'s AS for which the PeerAdj\n SID is advertised. If Confederations [RFC5065] are in use, and if\n the remote node is a member of a different Member-AS within the\n local Confederation, this is the Member-AS Number inside the\n Confederation and not the Confederation Identifier.\n\n Local BGP Router ID: 4 octets. The unsigned integer representing\n the BGP Identifier of the PeerAdj SID advertising node as defined\n in [RFC4271] and [RFC6286].\n\n Remote BGP Router ID: 4 octets. The unsigned integer representing\n the BGP Identifier of the remote node as defined in [RFC4271] and\n [RFC6286].\n\n Local Interface Address: 4 octets or 16 octets. In the case of\n PeerAdj SID, the local interface address corresponding to the\n PeerAdj SID should be specified in this field. For IPv4, this\n field is 4 octets; for IPv6, this field is 16 octets. Link-local\n IPv6 addresses are not in the scope of this document.\n\n Remote Interface Address: 4 octets or 16 octets. In the case of\n PeerAdj SID, the remote interface address corresponding to the\n PeerAdj SID should be specified in this field. For IPv4, this\n field is 4 octets; for IPv6, this field is 16 octets. Link-local\n IPv6 addresses are not in the scope of this document.\n\n [RFC9086] mandates sending a local interface ID and remote interface\n ID in the link descriptors and allows a value of 0 in the remote\n descriptors. It is useful to validate the incoming interface for an\n OAM packet, but if the remote descriptor is 0, this validation is not\n possible. Optional link descriptors of local and remote interface\n addresses are allowed as described in Section 4.2 of [RFC9086]. In\n this document, it is RECOMMENDED to send these optional descriptors\n and use them to validate incoming interfaces. When these local and\n remote interface addresses are not available, an ingress node can\n send 0 in the local and/or remote interface address field. The\n receiver SHOULD skip the validation for the incoming interface if the\n address field contains 0.\n\n4.3. PeerSet SID Sub-TLV\n\n 0 1 2 3\n 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1\n +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n |Type = 40 | Length |\n +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n | Local AS Number (4 octets) |\n +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n | Local BGP Router ID (4 octets) |\n +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n | No. of elements in set | Reserved |\n +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n | Remote AS Number (4 octets) |\n +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n | Remote BGP Router ID (4 octets) |\n ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-++\n\n\n One element in set consists of the details below\n +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n | Remote AS Number (4 octets) |\n +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n | Remote BGP Router ID (4 octets) |\n ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-++\n\n Figure 4: PeerSet SID Sub-TLV\n\n Type: 2 octets\n\n Value: 40\n\n Length: 2 octets\n\n Value: Expressed in octets and is a variable based on the number of\n elements in the set. The length field does not include the length\n of Type and Length fields.\n\n Local AS Number: 4 octets. The unsigned integer representing the AS\n number [RFC6793] of the AS to which the PeerSet SID advertising\n node belongs. If Confederations [RFC5065] are in use, and if the\n remote node is a member of a different Member-AS within the local\n Confederation, this is the Member-AS Number inside the\n Confederation and not the Confederation Identifier.\n\n Local BGP Router ID: 4 octets. The unsigned integer representing\n the BGP Identifier of the PeerSet SID advertising node, as defined\n in [RFC4271] and [RFC6286].\n\n No. of elements in set: 2 octets. The number of remote ASes over\n which the set SID performs load-balancing.\n\n Reserved: 2 octets. MUST be zero when sent and ignored when\n received.\n\n Remote AS Number: 4 octets. The unsigned integer representing the\n AS number [RFC6793] of the remote node\'s AS for which the PeerSet\n SID is advertised. If Confederations [RFC5065] are in use, and if\n the remote node is a member of a different Member-AS within the\n local Confederation, this is the Member-AS Number inside the\n Confederation and not the Confederation Identifier.\n\n Remote BGP Router ID: 4 octets. The unsigned integer representing\n the BGP Identifier of the remote node as defined in [RFC4271] and\n [RFC6286].\n\n PeerSet SID may be associated with a number of PeerNode SIDs and\n PeerAdj SIDs. The remote AS number and the Router ID of each of\n these PeerNode SIDs and PeerAdj SIDs MUST be included in the FEC.\n\n5. EPE-SID FEC Validation\n\n When a remote ASBR of the EPE-SID advertisement receives the MPLS OAM\n packet with the top FEC being the EPE-SID, it MUST perform validity\n checks on the content of the EPE-SID FEC sub-TLV. The basic length\n check should be performed on the received FEC.\n\n PeerAdj SID sub-TLV\n -----------\n If Adj type = 1, Length should be 28 octets\n If Adj type = 2, Length should be 52 octets\n\n PeerNode SID sub-TLV\n -------------\n Length = (20 + No. of IPv4 interface pairs * 8 +\n No. of IPv6 interface pairs * 32) octets\n\n PeerSet SID sub-TLV\n -----------\n Length = (9 + No. of elements in the set *\n (8 + No. of IPv4 interface pairs * 8 +\n No. of IPv6 interface pairs * 32) octets\n\n Figure 5: Length Validation\n\n If a malformed FEC sub-TLV is received, then a return code of 1,\n "Malformed echo request received", as defined in [RFC8029] MUST be\n sent. The section below is appended to the procedure given in step\n 4a of Section 7.4 of [RFC8287].\n\n5.1. EPE-SID FEC Validation Rules\n\n This is an example of Segment Routing IGP-Prefix, IGP-Adjacency SID,\n and EPE-SID validations. Note that the term "receiving node" in this\n section corresponds to the node that receives the OAM message with\n the Target FEC Stack TLV.\n\n Else, if the Label-stack-depth is 0 and the Target FEC Stack sub-TLV\n at FEC stack-depth is 38 (PeerAdj SID sub-TLV), {\n\n Set the Best-return-code to 10, "Mapping for this FEC is not\n the given label at stack-depth " [RFC8029]. Check if\n any below conditions fail:\n\n - Validate that the receiving node\'s BGP Local AS matches\n with the remote AS field in the received PeerAdj SID\n sub-TLV.\n\n - Validate that the receiving node\'s BGP Router-ID\n matches with the Remote Router ID field in the\n received PeerAdj SID sub-TLV.\n\n - Validate that there is an EBGP session with a peer\n having a local AS number and BGP Router-ID as\n specified in the local AS number and Local Router-ID\n field in the received PeerAdj SID sub-TLV.\n\n If the remote interface address is not zero, validate the\n incoming interface. Set the Best-return-code to 35,\n "Mapping for this FEC is not associated with the incoming\n interface" [RFC8287]. Check if any below conditions fail:\n\n - Validate that the incoming interface on which the\n OAM packet was received matches with the remote\n interface specified in the PeerAdj SID sub-TLV.\n\n If all above validations have passed, set the return code to 3,\n "Replying router is an egress for the FEC at stack-depth "\n [RFC8029].\n }\n\n Else, if the Target FEC Stack sub-TLV at FEC stack-depth is 39\n (PeerNode SID sub-TLV), {\n\n Set the Best-return-code to 10, "Mapping for this FEC is not\n the given label at stack-depth " [RFC8029]. Check if any\n below conditions fail:\n\n - Validate that the receiving node\'s BGP Local AS matches\n with the remote AS field in the received PeerNode SID\n FEC sub-TLV.\n\n - Validate that the receiving node\'s BGP Router-ID matches\n with the Remote Router ID field in the received\n PeerNode SID FEC.\n\n - Validate that there is an EBGP session with a peer\n having a local AS number and BGP Router-ID as\n specified in the local AS number and Local Router-ID\n field in the received PeerNode SID FEC sub-TLV.\n\n If all above validations have passed, set the return code to 3,\n "Replying router is an egress for the FEC at stack-depth "\n [RFC8029].\n }\n Else, if the Target FEC Stack sub-TLV at FEC stack-depth is 40\n (PeerSet SID sub-TLV), {\n\n Set the Best-return-code to 10, "Mapping for this FEC is not\n the given label at stack-depth " [RFC8029]. Check if any\n below conditions fail:\n\n - Validate that the receiving node\'s BGP Local AS matches\n with one of the remote AS fields in the received\n PeerSet SID FEC sub-TLV.\n\n - Validate that the receiving node\'s BGP Router-ID matches\n with one of the Remote Router ID fields in the\n received PeerSet SID FEC sub-TLV.\n\n - Validate that there is an EBGP session with a peer having\n a local AS number and BGP Router-ID as specified in the\n local AS number and Local Router-ID fields in the received\n PeerSet SID FEC sub-TLV.\n\n If all above validations have passed, set the return code to 3,\n "Replying router is an egress for the FEC at stack-depth "\n [RFC8029].\n }\n\n6. IANA Considerations\n\n IANA has allocated three new Target FEC Stack sub-TLVs in the "Sub-\n TLVs for TLV Types 1, 16, and 21" registry [MPLS-LSP-PING] within the\n "TLVs" registry of the "Multiprotocol Label Switching (MPLS) Label\n Switched Paths (LSPs) Ping Parameters" registry group.\n\n +==========+==============+\n | Sub-Type | Sub-TLV Name |\n +==========+==============+\n | 38 | PeerAdj SID |\n +----------+--------------+\n | 39 | PeerNode SID |\n +----------+--------------+\n | 40 | PeerSet SID |\n +----------+--------------+\n\n Table 1: Sub-TLVs for\n TLV Types 1, 16, and 21\n Registry\n\n7. Security Considerations\n\n The EPE-SIDs are advertised for egress links for EPE purposes or for\n inter-AS links between cooperating ASes. When cooperating domains\n are involved, they can allow the packets arriving on trusted\n interfaces to reach the control plane and be processed.\n\n When EPE-SIDs are created for egress TE links where the neighbor AS\n is an independent entity, it may not allow the packets arriving from\n the external world to reach the control plane. In such deployments,\n the MPLS OAM packets will be dropped by the neighboring AS that\n receives the MPLS OAM packet.\n\n In MPLS Traceroute applications, when the AS boundary is crossed with\n the EPE-SIDs, the Target FEC Stack TLV is changed. [RFC8287] does\n not mandate that the initiator, upon receiving an MPLS Echo Reply\n message that includes the Target FEC Stack Change TLV with one or\n more of the original segments being popped, remove the corresponding\n FEC(s) from the Target FEC Stack TLV in the next (TTL+1) traceroute\n request.\n\n If an initiator does not remove the FECs belonging to the previous AS\n that has traversed, it may expose the internal AS information to the\n following AS being traversed in the traceroute.\n\n8. References\n\n8.1. Normative References\n\n [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate\n Requirement Levels", BCP 14, RFC 2119,\n DOI 10.17487/RFC2119, March 1997,\n .\n\n [RFC6793] Vohra, Q. and E. Chen, "BGP Support for Four-Octet\n Autonomous System (AS) Number Space", RFC 6793,\n DOI 10.17487/RFC6793, December 2012,\n .\n\n [RFC8029] Kompella, K., Swallow, G., Pignataro, C., Ed., Kumar, N.,\n Aldrin, S., and M. Chen, "Detecting Multiprotocol Label\n Switched (MPLS) Data-Plane Failures", RFC 8029,\n DOI 10.17487/RFC8029, March 2017,\n .\n\n [RFC8174] Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC\n 2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174,\n May 2017, .\n\n [RFC8287] Kumar, N., Ed., Pignataro, C., Ed., Swallow, G., Akiya,\n N., Kini, S., and M. Chen, "Label Switched Path (LSP)\n Ping/Traceroute for Segment Routing (SR) IGP-Prefix and\n IGP-Adjacency Segment Identifiers (SIDs) with MPLS Data\n Planes", RFC 8287, DOI 10.17487/RFC8287, December 2017,\n .\n\n [RFC8690] Nainar, N., Pignataro, C., Iqbal, F., and A. Vainshtein,\n "Clarification of Segment ID Sub-TLV Length for RFC 8287",\n RFC 8690, DOI 10.17487/RFC8690, December 2019,\n .\n\n [RFC9086] Previdi, S., Talaulikar, K., Ed., Filsfils, C., Patel, K.,\n Ray, S., and J. Dong, "Border Gateway Protocol - Link\n State (BGP-LS) Extensions for Segment Routing BGP Egress\n Peer Engineering", RFC 9086, DOI 10.17487/RFC9086, August\n 2021, .\n\n8.2. Informative References\n\n [MPLS-LSP-PING]\n IANA, "Sub-TLVs for TLV Types 1, 16, and 21",\n .\n\n [RFC4271] Rekhter, Y., Ed., Li, T., Ed., and S. Hares, Ed., "A\n Border Gateway Protocol 4 (BGP-4)", RFC 4271,\n DOI 10.17487/RFC4271, January 2006,\n .\n\n [RFC5065] Traina, P., McPherson, D., and J. Scudder, "Autonomous\n System Confederations for BGP", RFC 5065,\n DOI 10.17487/RFC5065, August 2007,\n .\n\n [RFC6286] Chen, E. and J. Yuan, "Autonomous-System-Wide Unique BGP\n Identifier for BGP-4", RFC 6286, DOI 10.17487/RFC6286,\n June 2011, .\n\n [RFC7705] George, W. and S. Amante, "Autonomous System Migration\n Mechanisms and Their Effects on the BGP AS_PATH\n Attribute", RFC 7705, DOI 10.17487/RFC7705, November 2015,\n .\n\n [RFC8403] Geib, R., Ed., Filsfils, C., Pignataro, C., Ed., and N.\n Kumar, "A Scalable and Topology-Aware MPLS Data-Plane\n Monitoring System", RFC 8403, DOI 10.17487/RFC8403, July\n 2018, .\n\n [RFC8664] Sivabalan, S., Filsfils, C., Tantsura, J., Henderickx, W.,\n and J. Hardwick, "Path Computation Element Communication\n Protocol (PCEP) Extensions for Segment Routing", RFC 8664,\n DOI 10.17487/RFC8664, December 2019,\n .\n\n [RFC9087] Filsfils, C., Ed., Previdi, S., Dawra, G., Ed., Aries, E.,\n and D. Afanasiev, "Segment Routing Centralized BGP Egress\n Peer Engineering", RFC 9087, DOI 10.17487/RFC9087, August\n 2021, .\n\n [RFC9256] Filsfils, C., Talaulikar, K., Ed., Voyer, D., Bogdanov,\n A., and P. Mattes, "Segment Routing Policy Architecture",\n RFC 9256, DOI 10.17487/RFC9256, July 2022,\n .\n\n [SR-BGP-POLICY]\n Previdi, S., Filsfils, C., Talaulikar, K., Mattes, P., and\n D. Jain, "Advertising Segment Routing Policies in BGP",\n Work in Progress, Internet-Draft, draft-ietf-idr-sr-\n policy-safi-10, 7 November 2024,\n .\n\n [SR-SEGTYPES]\n Talaulikar, K., Filsfils, C., Previdi, S., Mattes, P., and\n D. Jain, "Segment Routing Segment Types Extensions for BGP\n SR Policy", Work in Progress, Internet-Draft, draft-ietf-\n idr-bgp-sr-segtypes-ext-06, 7 November 2024,\n .\n\nAppendix A. Examples of Programmed States\n\n This section describes examples of both a correctly and an\n incorrectly programmed state and provides details on how the new sub-\n TLVs described in this document can be used to validate the\n correctness. Consider the diagram from Figure 1.\n\n Correctly programmed state:\n\n * C assigns label 16001 and binds it to adjacency C->E\n\n * C signals that label 16001 is bound to adjacency C->E (e.g., via\n BGP-LS)\n\n * The controller/ingress programs an SR path that has SID/label\n 16001 to steer the packet on the exit point from C onto adjacency\n C->E\n\n * Using MPLS Traceroute procedures defined in this document, the\n PeerAdj SID sub-TLV is populated with entities to be validated by\n C when the OAM packet reaches it\n\n * C receives the OAM packet and validates that the top label (16001)\n is indeed corresponding to the entities populated in the PeerAdj\n SID sub-TLV\n\n Incorrectly programmed state:\n\n * C assigns label 16001 and binds it to adjacency C->D\n\n * The controller learns that PeerAdj SID label 16001 is bound to\n adjacency C->E (e.g., via BGP-LS) -- this could be a software bug\n on C or on the controller\n\n * The controller/ingress programs an SR path that has SID/label\n 16001 to steer the packet on the exit point from C onto adjacency\n C->E\n\n * Using MPLS Traceroute procedures defined in this document, the\n PeerAdj SID sub-TLV is populated with entities to be validated by\n C (including a local/remote interface address of C->E) when the\n OAM packet reaches it\n\n * C receives the OAM packet and validates that the top label (16001)\n is NOT bound to C->E as populated in the PeerAdj SID sub-TLV and\n then responds with the respective error code\n\nAcknowledgments\n\n Thanks to Loa Andersson, Dhruv Dhody, Ketan Talaulikar, Italo Busi,\n Alexander Vainshtein, and Deepti Rathi for careful reviews and\n comments. Thanks to Tarek Saad for providing the example described\n in Appendix A.\n\nAuthors\' Addresses\n\n Shraddha Hegde\n Juniper Networks Inc.\n Exora Business Park\n Bangalore 560103\n Karnataka\n India\n Email: shraddha@juniper.net\n\n\n Mukul Srivastava\n Juniper Networks Inc.\n Email: msri@juniper.net\n\n\n Kapil Arora\n Individual Contributor\n Email: kapil.it@gmail.com\n\n\n Samson Ninan\n Ciena\n Email: samson.cse@gmail.com\n\n\n Xiaohu Xu\n China Mobile\n Beijing\n China\n Email: xuxiaohu_ietf@hotmail.com\n' } + +type ExtraFieldNeededFormat = + | { + type: 'TXT' + } + | { type: 'HTML' } + | { type: 'HTMLized' } + | { type: 'PDF' } + | { type: 'PS' } + +type ExtraFieldNeededObsolete = { + number: number +} + +type ExtraFieldNeededUpdate = { + number: number +} + +type ExtraFieldNeededIsAlso = { + number: number +} + +type ExtraFieldNeededSeeAlso = { + number: number +} + +/** New fields needed in the RfcMetadata response */ +export type ExtraFieldsNeeded = { + formats: ExtraFieldNeededFormat[] + obsoletes: ExtraFieldNeededObsolete[] + updates: ExtraFieldNeededUpdate[] + is_also: ExtraFieldNeededIsAlso[] + see_also: ExtraFieldNeededSeeAlso[] +} + +const missingData: Record> = { + 1: { + authors: [ + { + name: 'Steve Crocker', + person: 0 + } + ], + formats: [{ type: 'TXT' }, { type: 'HTML' }] + }, + 2: { + authors: [{ name: 'Bill Duvall', person: 0 }], + formats: [{ type: 'TXT' }, { type: 'PDF' }, { type: 'HTML' }] + }, + 3: { + authors: [{ name: 'Steve D. Crocker', person: 0 }], + formats: [{ type: 'TXT' }, { type: 'HTML' }] + }, + 4: { + authors: [{ name: 'E. B. Shapiro', person: 0 }], + formats: [{ type: 'TXT' }, { type: 'HTML' }] + }, + 5: { + authors: [{ name: 'J. Rulifson', person: 0 }], + formats: [{ type: 'TXT' }, { type: 'HTML' }] + }, + 6: { + authors: [{ name: 'S.D. Crocker.', person: 0 }], + formats: [{ type: 'TXT' }, { type: 'HTML' }] + }, + 7: { + authors: [{ name: 'G. Deloche', person: 0 }], + formats: [{ type: 'TXT' }, { type: 'HTML' }] + }, + 8: { + authors: [{ name: 'G. Deloche', person: 0 }], + formats: [{ type: 'PDF' }] + }, + 9: { + authors: [{ name: 'G. Deloche', person: 0 }], + formats: [{ type: 'PDF' }] + }, + 10: { + authors: [{ name: 'S.D. Crocker.', person: 0 }], + formats: [{ type: 'TXT' }, { type: 'HTML' }], + obsoletes: [{ number: 3 }], + updated_by: [ + { id: 24, number: 24, title: '?' }, + { id: 27, number: 27, title: '?' }, + { id: 30, number: 30, title: '?' } + ] + }, + 11: { + authors: [{ name: 'G. Deloche', person: 0 }], + formats: [{ type: 'TXT' }, { type: 'PDF' }, { type: 'HTML' }] + }, + 12: { + authors: [{ name: 'M. Wingfield', person: 0 }], + formats: [ + { type: 'TXT' }, + { type: 'PS' }, + { type: 'PDF' }, + { type: 'HTML' } + ] + }, + 13: { + authors: [{ name: 'Vincent Cerf', person: 0 }], + formats: [{ type: 'TXT' }, { type: 'HTML' }] + } +} + +export const getRFCWithExtraFields = ( + rfcMetadata: RfcMetadata +): RfcMetadata & Partial => { + const toArray = (obj: unknown) => { + if (!obj) return [] + return Array.isArray(obj) ? obj : [obj] + } + + const missingRfc = missingData[rfcMetadata.number] + + const rfc: RfcMetadata & Partial = { + ...missingRfc, + ...rfcMetadata, + authors: [...toArray(missingRfc?.authors), ...toArray(rfcMetadata?.authors)] + } + + return rfc +}