amazon-route-53-dns-zone-file
Version:
Makes DNS Zone File easy. Parses and validates BIND zone files and can be extended for custom features. Functionality is modular. Features are made open for extension and closed for runtime mutation. Written in TypeScript.
144 lines (127 loc) • 4.32 kB
text/typescript
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<string, ExampleDomain.ApiRecord>();
/** @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<ExampleDomain.ClientRecord | ExampleDomain.ApiRecord>
): 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 };
};