Crate reporting_api

Source
Expand description

This crate provides some useful Rust code for working with the Reporting API and Network Error Logging W3C draft specifications.

§Overview

The core of the Reporting API is pretty simple: reports are uploaded via a POST to a URL of your choosing. The payload of the POST request is a JSON-encoded array of reports, and the report schema is defined by the spec.

The Reporting API can be used to upload many different kinds of reports. For instance, Reporting itself defines crash reports, deprecations, and interventions, all of which come from the JavaScript environment running in the browser. Other report types are complex enough that they need to be defined in their own specs, such as Network Error Logging and Content Security Policy. Regardless of where they’re defined, each report type defines some fields specific to that type (the body), and the Reporting API defines some fields that are common to all types.

This library provides a definition of all of these schemas as regular Rust types, along with the ability to use serde to serialize and deserialize them. We’ve carefully defined everything so that serde_json will automatically do The Right Thing and use a JSON serialization that lines up with the various specifications. We also provide way to define body schemas for new report types, and have them seamlessly fit in with the rest of the serialization logic.

§Collecting reports

The simplest way to use this library is if you just want to receive reports from somewhere (you’re implementing a collector, for instance, and we’ve already defined Rust types for all of the report types that you care about).

To do that, you just need to use serde_json to deserialize the content of the JSON string that you’ve received:

let reports: Vec<BareReport> = serde_json::from_str(payload).unwrap();

That’s it! The elements of the vector will represent each of the reports in this upload batch. Each one is a “bare” report, which means that we haven’t tried to figure out what type of report this is, or which Rust type corresponds with that report type. Instead, the raw body of the report is available (in the body field) as a serde_json Value.

If you know which particular kind of report you want to process, you can use the bare report’s parse method to convert it into a “parsed” report. For instance, if you know you only care about Network Error Logging reports:

// Ignore both kinds of failure, returning a Vec<Report<NEL>>.
let nel_reports = reports
    .into_iter()
    .filter_map(BareReport::parse::<NEL>)
    .filter_map(Result::ok)
    .collect::<Vec<Report<NEL>>>();

Note that parse’s return value is wrapped in both Option and Result. The outer Option tells you whether or not the report is of the expected type. If it is, the inner Result tells you whether we were able to parse the reports body field according to that type’s expected schema. In this example, we therefore need two filter_map calls to strip away any mismatches and errors, leaving us with a vector of Report<NEL> instances.

§Creating a new report type

This should be a relatively rare occurrence, but consider a new report type that uses the Reporting API but that isn’t covered here. For instance, let’s say there’s a new lint report type whose body content looks like:

{
    "source_file": "foo.js",
    "line": 10,
    "column": 12,
    "finding": "Indentation doesn't match the rest of the file"
}

First you’ll define a Rust type to hold the body content:

#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
pub struct Lint {
    pub source_file: String,
    pub line: u32,
    pub column: u32,
    pub finding: String,
}

Lastly, you must implement the ReportType trait for your new type, which defines the value of the type field in the report payload that corresponds to this new report type.

impl ReportType for Lint {
    fn report_type() -> &'static str {
        "lint"
    }
}

And that’s it! The parse method will now work with your new report type.

Modules§

  • A serde parsing module that can be used to parse durations expressed as an integer number of milliseconds.
  • A serde parsing module that can be used to parse optional durations expressed as an integer number of milliseconds.

Structs§

  • Represents a single report uploaded via the Reporting API, whose body is still a JSON object and has not yet been parsed into a more specific Rust type.
  • The body of a single Network Error Logging report.
  • Represents a single report, after having parsed the body into the Rust type specific to this type of report.

Traits§

  • A trait that maps each Rust report type to the corresponding type value that appears in a JSON report payload.