public constructor()

in src/backend-dashboard.ts [30:310]


  public constructor(scope: Construct, id: string, props: BackendDashboardProps) {
    super(scope, id);

    const reports: IWidget[][] = [[
      new PackageVersionsTableWidget(this, 'UninstallablePackages', {
        title: 'Package Versions Report | Uninstallable',
        description: [
          "These packages could not be installed. Note that currently they will also appear in the 'missing' documentation reports.",
          '',
          "The specific error can be found in the package directory inside a file named 'uninstallable'",
        ].join('\n'),
        bucket: props.packageData,
        key: UNINSTALLABLE_PACKAGES_REPORT,
        height: 6,
        width: 24,
      }),
    ]];

    for (const language of DocumentationLanguage.ALL) {
      for (const report of this.perLanguageReports(language, props.packageData)) {
        // put every report in a new line
        reports.push([report]);
      }
    }

    const dashboardName = props.dashboardName ?? 'ConstructHubBackend';

    new Dashboard(this, 'Reports', {
      dashboardName: `${dashboardName}-reports`,
      periodOverride: PeriodOverride.AUTO,
      start: '-P1W', // Show 1 week by default
      widgets: reports,
    });

    new Dashboard(this, 'Graphs', {
      dashboardName: `${dashboardName}-graphs`,
      periodOverride: PeriodOverride.AUTO,
      start: '-P1W', // Show 1 week by default
      widgets: [
        [
          new TextWidget({
            height: 2,
            width: 24,
            markdown: [
              '# Catalog Overview',
              '',
              `[button:primary:Package Data](${s3ObjectUrl(props.packageData)})`,
              `[button:Catalog Builder](${lambdaFunctionUrl(props.orchestration.catalogBuilder.function)})`,
              `[button:Inventory Canary](${lambdaFunctionUrl(props.inventory.function)})`,
              `[button:Search Canary Log Group](${lambdaSearchLogGroupUrl(props.inventory.function)})`,
            ].join('\n'),
          }),
        ],
        [
          new GraphWidget({
            height: 6,
            width: 12,
            title: 'Catalog Size',
            left: [
              props.inventory.metricSubmoduleCount({ label: 'Submodules' }),
              props.inventory.metricPackageVersionCount({ label: 'Package Versions' }),
              props.inventory.metricPackageMajorCount({ label: 'Package Majors' }),
              props.inventory.metricPackageCount({ label: 'Packages' }),
            ],
            leftYAxis: { min: 0 },
          }),
          new GraphWidget({
            height: 6,
            width: 12,
            title: 'Catalog Issues',
            left: [
              props.inventory.metricUnknownObjectCount({ label: 'Unknown' }),
              props.inventory.metricMissingAssemblyCount({ label: 'Missing Assembly' }),
              props.inventory.metricMissingPackageMetadataCount({ label: 'Missing Metadata' }),
              props.inventory.metricMissingPackageTarballCount({ label: 'Missing Tarball' }),
              props.inventory.metricUninstallablePackageCount({ label: 'Uninstallable Package' }),
            ],
            leftYAxis: { min: 0 },
            right: [
              props.orchestration.catalogBuilder.metricMissingConstructFrameworkCount({ label: 'No Construct Framework' }),
              props.orchestration.catalogBuilder.metricMissingConstructFrameworkVersionCount({ label: 'No Construct Framework Version' }),
            ],
            rightYAxis: { min: 0 },
          }),
        ],
        ...this.catalogOverviewLanguageSections(props),
        ...renderPackageSourcesWidgets(props.packageSources),
        [
          new TextWidget({
            height: 2,
            width: 24,
            markdown: [
              '# Ingestion Function',
              '',
              `[button:Ingestion Function](${lambdaFunctionUrl(props.ingestion.function)})`,
              `[button:primary:Search Log Group](${lambdaSearchLogGroupUrl(props.ingestion.function)})`,
              `[button:DLQ](${sqsQueueUrl(props.ingestion.deadLetterQueue)})`,
            ].join('\n'),
          }),
        ],
        [
          new GraphWidget({
            height: 6,
            width: 12,
            title: 'Function Health',
            left: [
              fillMetric(props.ingestion.function.metricInvocations({ label: 'Invocations' })),
              fillMetric(props.ingestion.function.metricErrors({ label: 'Errors' })),
            ],
            leftYAxis: { min: 0 },
            period: Duration.minutes(1),
          }),
          new GraphWidget({
            height: 6,
            width: 12,
            title: 'Input Queue',
            left: [
              props.ingestion.queue.metricApproximateNumberOfMessagesVisible({ label: 'Visible Messages', period: Duration.minutes(1) }),
              props.ingestion.queue.metricApproximateNumberOfMessagesNotVisible({ label: 'Hidden Messages', period: Duration.minutes(1) }),
            ],
            leftYAxis: { min: 0 },
            right: [
              props.ingestion.queue.metricApproximateAgeOfOldestMessage({ label: 'Oldest Message Age', period: Duration.minutes(1) }),
            ],
            rightAnnotations: [{
              color: '#ffa500',
              label: '10 Minutes',
              value: Duration.minutes(10).toSeconds(),
            }],
            rightYAxis: { min: 0 },
            period: Duration.minutes(1),
          }),
        ],
        [
          new GraphWidget({
            height: 6,
            width: 12,
            title: 'Input Quality',
            left: [
              fillMetric(props.ingestion.metricInvalidAssembly({ label: 'Invalid Assemblies' })),
              fillMetric(props.ingestion.metricInvalidTarball({ label: 'Invalid Tarball' })),
              fillMetric(props.ingestion.metricIneligibleLicense({ label: 'Ineligible License' })),
              fillMetric(props.ingestion.metricMismatchedIdentityRejections({ label: 'Mismatched Identity' })),
              fillMetric(props.ingestion.metricFoundLicenseFile({ label: 'Found License file' })),
            ],
            leftYAxis: { label: 'Count', min: 0, showUnits: false },
            stacked: true,
          }),
          new GraphWidget({
            height: 6,
            width: 12,
            title: 'Dead Letters',
            left: [
              props.ingestion.deadLetterQueue.metricApproximateNumberOfMessagesVisible({ label: 'Visible Messages' }),
              props.ingestion.deadLetterQueue.metricApproximateNumberOfMessagesNotVisible({ label: 'Invisible Messages' }),
            ],
            leftYAxis: { min: 0 },
            right: [
              props.ingestion.deadLetterQueue.metricApproximateAgeOfOldestMessage({ label: 'Oldest Message Age' }),
            ],
            rightAnnotations: [{
              color: '#ff7f0e',
              label: '10 days',
              value: Duration.days(10).toSeconds(),
            }, {
              color: '#ff0000',
              label: '14 days (DLQ Retention)',
              value: Duration.days(14).toSeconds(),
            }],
            rightYAxis: { min: 0 },
            period: Duration.minutes(1),
          }),
        ],
        [
          new TextWidget({
            height: 2,
            width: 24,
            markdown:
              [
                '# Orchestration',
                '',
                `[button:primary:State Machine](${stateMachineUrl(props.orchestration.stateMachine)})`,
                `[button:DLQ](${sqsQueueUrl(props.orchestration.deadLetterQueue)})`,
                `[button:Redrive DLQ](${lambdaFunctionUrl(props.orchestration.redriveFunction)})`,
                `[button:Regenerate All Documentation](${stateMachineUrl(props.orchestration.regenerateAllDocumentation)})`,
              ].join('\n'),
          }),
        ],
        [
          new GraphWidget({
            height: 6,
            width: 12,
            title: 'State Machine Executions',
            left: [
              fillMetric(props.orchestration.stateMachine.metricStarted({ label: 'Started' })),
              fillMetric(props.orchestration.stateMachine.metricSucceeded({ label: 'Succeeded' })),
              fillMetric(props.orchestration.stateMachine.metricAborted({ label: 'Aborted' })),
              fillMetric(props.orchestration.stateMachine.metricFailed({ label: 'Failed' })),
              fillMetric(props.orchestration.stateMachine.metricThrottled({ label: 'Throttled' })),
              fillMetric(props.orchestration.stateMachine.metricTimedOut({ label: 'Timed Out' })),
            ],
            leftYAxis: { min: 0 },
            right: [
              props.orchestration.stateMachine.metricTime({ label: 'Duration' }),
            ],
            rightYAxis: { min: 0 },
          }),
          new GraphWidget({
            height: 6,
            width: 12,
            title: 'Dead Letter Queue',
            left: [
              props.orchestration.deadLetterQueue.metricApproximateNumberOfMessagesVisible({ label: 'Visible Messages' }),
              props.orchestration.deadLetterQueue.metricApproximateNumberOfMessagesNotVisible({ label: 'Invisible Messages' }),
            ],
            leftYAxis: { min: 0 },
            right: [
              props.orchestration.deadLetterQueue.metricApproximateAgeOfOldestMessage({ label: 'Oldest Message Age' }),
            ],
            rightAnnotations: [{
              color: '#ff7f0e',
              label: '10 days',
              value: Duration.days(10).toSeconds(),
            }, {
              color: '#ff0000',
              label: '14 days (DLQ Retention)',
              value: Duration.days(14).toSeconds(),
            }],
            rightYAxis: { min: 0 },
            period: Duration.minutes(1),
          }),
        ],

        // deny list
        // ----------------------------------------------
        [
          new TextWidget({
            height: 2,
            width: 24,
            markdown:
              [
                '# Deny List',
                '',
                `[button:primary:Deny List Object](${s3ObjectUrl(props.denyList.bucket, props.denyList.objectKey)})`,
                `[button:Prune Function](${lambdaFunctionUrl(props.denyList.prune.pruneHandler)})`,
                `[button:Prune Logs](${lambdaSearchLogGroupUrl(props.denyList.prune.pruneHandler)})`,
                `[button:Delete Queue](${sqsQueueUrl(props.denyList.prune.queue)})`,
                `[button:Delete Logs](${lambdaSearchLogGroupUrl(props.denyList.prune.deleteHandler)})`,
              ].join('\n'),
          }),
        ],
        [
          new GraphWidget({
            height: 6,
            width: 12,
            title: 'Deny List',
            left: [
              fillMetric(props.denyList.metricDenyListRules({ label: 'Rules' }), 'REPEAT'),
              props.denyList.prune.queue.metricNumberOfMessagesDeleted({ label: 'Deleted Files' }),
            ],
            leftYAxis: { min: 0 },
            period: Duration.minutes(5),
          }),
          new GraphWidget({
            height: 6,
            width: 12,
            title: 'Prune Function Health',
            left: [
              fillMetric(props.denyList.prune.pruneHandler.metricInvocations({ label: 'Invocations' })),
              fillMetric(props.denyList.prune.pruneHandler.metricErrors({ label: 'Errors' })),
            ],
            leftYAxis: { min: 0 },
            period: Duration.minutes(5),
          }),
        ],

        ...(props.packageStats ? renderPackageStatsWidgets(props.packageStats) : []),
        ...renderVersionTrackerWidgets(props.versionTracker),
      ],
    });
  }