auditRegistrationBundle function

RegistrationBundleAudit auditRegistrationBundle(
  1. RegistrationBundle bundle, {
  2. bool includeUnknownShapeWarnings = false,
})

Audits bundle for duplicate types, duplicate keys, canonical drift, and missing shape data.

Implementation

RegistrationBundleAudit auditRegistrationBundle(
  RegistrationBundle bundle, {
  bool includeUnknownShapeWarnings = false,
}) {
  final issues = <RegistrationAuditIssue>[];
  final typeOwners = <ChartType, ChartRegistration>{};
  final canonicalTypeOwners = <ChartType, ChartRegistration>{};
  final keyOwners = <String, ChartRegistration>{};
  final normalizedKeyOwners = <String, _RegistrationKeyOwner>{};

  for (final reg in bundle.registrations) {
    final canonicalType = canonicalChartType(reg.type);
    if (canonicalType != reg.type) {
      issues.add(
        RegistrationAuditIssue(
          severity: RegistrationAuditSeverity.error,
          code: 'LEGACY_REGISTRATION_TYPE',
          message:
              'Registration ${reg.type.name} in ${bundle.name} should use '
              'canonical chart type ${canonicalType.name}.',
          firstType: canonicalType,
          secondType: reg.type,
        ),
      );
    }

    final existingType = typeOwners[reg.type];
    if (existingType != null) {
      issues.add(
        RegistrationAuditIssue(
          severity: RegistrationAuditSeverity.error,
          code: 'DUPLICATE_TYPE',
          message:
              'Chart type ${chartTypeToString(reg.type)} is registered more than once in ${bundle.name}.',
          firstType: existingType.type,
          secondType: reg.type,
        ),
      );
    } else {
      typeOwners[reg.type] = reg;
    }

    final existingCanonicalType = canonicalTypeOwners[canonicalType];
    if (existingCanonicalType != null &&
        existingCanonicalType.type != reg.type) {
      issues.add(
        RegistrationAuditIssue(
          severity: RegistrationAuditSeverity.error,
          code: 'DUPLICATE_CANONICAL_TYPE',
          message:
              'Canonical chart type ${chartTypeToString(canonicalType)} is '
              'owned by both ${existingCanonicalType.type.name} and '
              '${reg.type.name} in ${bundle.name}.',
          firstType: existingCanonicalType.type,
          secondType: reg.type,
        ),
      );
    } else {
      canonicalTypeOwners[canonicalType] = reg;
    }

    final typeStringKey = normalizeChartTypeKey(reg.typeString);
    final canonicalTypeStringKey = normalizeChartTypeKey(
      chartTypeToString(canonicalType),
    );
    if (typeStringKey.isEmpty) {
      issues.add(
        RegistrationAuditIssue(
          severity: RegistrationAuditSeverity.error,
          code: 'EMPTY_TYPE_STRING',
          message:
              'Chart type ${chartTypeToString(canonicalType)} in '
              '${bundle.name} has an empty registration typeString.',
          secondType: reg.type,
        ),
      );
    } else if (typeStringKey != canonicalTypeStringKey) {
      issues.add(
        RegistrationAuditIssue(
          severity: RegistrationAuditSeverity.error,
          code: 'TYPE_STRING_MISMATCH',
          message:
              'Registration typeString "${reg.typeString}" does not match '
              'canonical chart type ${chartTypeToString(canonicalType)}.',
          key: reg.typeString,
          secondType: reg.type,
        ),
      );
    }

    for (final entry in _registrationKeyEntries(reg)) {
      final key = entry.rawKey;
      final existingKeyOwner = keyOwners[key];
      if (existingKeyOwner != null) {
        final severity = existingKeyOwner.type == reg.type
            ? RegistrationAuditSeverity.error
            : RegistrationAuditSeverity.warning;
        issues.add(
          RegistrationAuditIssue(
            severity: severity,
            code: existingKeyOwner.type == reg.type
                ? 'DUPLICATE_KEY'
                : 'KEY_COLLISION',
            message:
                'Registration key "$key" is used by ${chartTypeToString(existingKeyOwner.type)} and ${chartTypeToString(reg.type)}.',
            key: key,
            firstType: existingKeyOwner.type,
            secondType: reg.type,
          ),
        );
      } else {
        keyOwners[key] = reg;
      }

      final existingNormalizedOwner = normalizedKeyOwners[entry.normalizedKey];
      if (existingNormalizedOwner != null &&
          existingNormalizedOwner.rawKey != entry.rawKey) {
        final sameType = existingNormalizedOwner.registration.type == reg.type;
        issues.add(
          RegistrationAuditIssue(
            severity: RegistrationAuditSeverity.warning,
            code: sameType
                ? 'REDUNDANT_NORMALIZED_ALIAS'
                : 'NORMALIZED_KEY_COLLISION',
            message: sameType
                ? 'Registration key "${entry.rawKey}" normalizes to the same '
                      'lookup key as "${existingNormalizedOwner.rawKey}" for '
                      '${chartTypeToString(reg.type)}.'
                : 'Registration key "${entry.rawKey}" normalizes to the same '
                      'lookup key as "${existingNormalizedOwner.rawKey}", used '
                      'by ${chartTypeToString(existingNormalizedOwner.registration.type)} '
                      'and ${chartTypeToString(reg.type)}.',
            key: entry.normalizedKey,
            firstType: existingNormalizedOwner.registration.type,
            secondType: reg.type,
          ),
        );
      } else {
        normalizedKeyOwners[entry.normalizedKey] = _RegistrationKeyOwner(
          registration: reg,
          rawKey: entry.rawKey,
        );
      }
    }

    if (includeUnknownShapeWarnings &&
        targetSeriesDataShape(reg.type) == ChartSeriesDataShape.unknown) {
      issues.add(
        RegistrationAuditIssue(
          severity: RegistrationAuditSeverity.warning,
          code: 'UNKNOWN_DATA_SHAPE',
          message:
              'Chart type ${chartTypeToString(reg.type)} has no data-shape capability mapping.',
          secondType: reg.type,
        ),
      );
    }
  }

  return RegistrationBundleAudit(
    bundleName: bundle.name,
    registrationCount: bundle.registrations.length,
    issues: issues,
  );
}