import { Validator } from '../src/validation/validator'; import { SourceMapTypings } from '../src/parser/types_parser'; import { ParsingError } from '../src/errors/parsing_error'; import { RecordType, ErrorKeyKind, WarningKeyKind, SUPPORTED_RECORD_TYPES, } from '../src/shared/constants_domain_specific'; import { trimDot } from '../src/shared/utils_generic'; import { ExampleDomain, ExampleZoneFile } from './example_types'; import { DEFAULT_RECORD } from './example_constants'; import { Parser } from '../src/parser/parser'; import { Validation } from '../src/validation/validation_types'; export const clientServiceApi = { createRecords( hz: ExampleDomain.HostedZone, records: ExampleDomain.ApiRecord[] ): any { return Promise.resolve() as any; }, }; export const getErrorText: Validation.GetErrorTextFn = ( metaEntry, i18nArgs ): string => i18nArgs ? JSON.stringify({ ...metaEntry, ...i18nArgs }, null, 2) : metaEntry.errorType!; const fromParamsToApiRecordObj = ( recordType: RecordType, recordName: string, routeTrafficToEndpoints: string[], ttl?: string | number ): ExampleDomain.ApiRecord => { return { ...DEFAULT_RECORD, recordName, recordType, routeTrafficToEndpoints, ttl: (ttl as number) ?? DEFAULT_RECORD.ttl, }; }; export const importZoneAdapter = ( parser: Parser, hz: ExampleDomain.HostedZone ): ExampleZoneFile.ParsedAndAdapted => { const records: ExampleDomain.ApiRecord[] = []; const warnings: WarningKeyKind[] = []; const reportDuplicate = (record: SourceMapTypings.Value) => { for (const [index, entry] of parser.parsingMeta) { if (entry.value === record) { parser.parsingMeta.set(index, { ...entry, errorType: ErrorKeyKind.Duplicated, }); break; } } }; /** @note only warning for SOA records */ if (!!parser.zoneObj.SOA) { warnings.push(WarningKeyKind.SoaIgnored); } const hashMap = new Map(); /** @idea map in the order given by the text input */ for (const type of SUPPORTED_RECORD_TYPES.filter(supported => Array.isArray(parser.zoneObj[supported]) )) { /** non-assertion because we filter the types above */ for (const record of parser.zoneObj[type]!) { /** * Ignoring any NS records that have the same name as the hosted zone. * https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/resource-record-sets-creating-import.html */ if (type === 'NS' && trimDot(record.name) === trimDot(hz.name)) { warnings.push(WarningKeyKind.NsIgnored); continue; } const value = record.values.join(' '); /** * 1. if we have not indexed the record => create it * 2. if we have created the record already => duplicate * 3. add the new value to our endpoints */ if (hashMap.has(record.name) === false) { const obj = fromParamsToApiRecordObj( type, record.name, [value], record.ttl ); hashMap.set(record.name, obj); } else if ( hashMap.get(record.name)!.routeTrafficToEndpoints.includes(value) ) { reportDuplicate((record as unknown) as SourceMapTypings.Value); } else { hashMap.get(record.name)!.routeTrafficToEndpoints.push(value); } } records.push(...hashMap.values()); hashMap.clear(); } return { records, warnings }; }; export const createRecordValidator = ( records: Array ): Validation.Cb => (p, errorBuilder) => { if (records.length === 0) { throw errorBuilder.create({ errorType: ErrorKeyKind.NoValidRecords }); } }; export const importAndAdaptAndValidate = ( txt: string, hz: ExampleDomain.HostedZone ): ExampleZoneFile.ParsedAndAdapted & { error?: ParsingError } => { const parser = new Parser(hz.name).setText(txt).parse(); const { records, warnings } = importZoneAdapter(parser, hz); const { errors } = new Validator({ getErrorText, postValidators: [createRecordValidator(records)], }).validate(parser); /** @note the domain may want to show more than 1 error at a time but this shows 1 */ const [error] = errors; return { warnings, error, records }; };