blob: 8838109ae30464598bb4779d5c5da1e8b27ef48b [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/developer/feedback/feedback_data/integrity_reporter.h"
#include "src/developer/feedback/feedback_data/errors.h"
#include "src/developer/feedback/utils/errors.h"
#include "third_party/rapidjson/include/rapidjson/document.h"
#include "third_party/rapidjson/include/rapidjson/prettywriter.h"
#include "third_party/rapidjson/include/rapidjson/stringbuffer.h"
namespace feedback {
namespace {
std::string ToString(const enum AttachmentValue::State state) {
switch (state) {
case AttachmentValue::State::kComplete:
return "complete";
case AttachmentValue::State::kPartial:
return "partial";
case AttachmentValue::State::kMissing:
return "missing";
}
}
// Create a complete list set of annotations from the collected annotations and the allowlist.
Annotations AllAnnotations(const AnnotationKeys& allowlist,
const ::fit::result<Annotations>& annotations_result) {
Annotations all_annotations;
if (annotations_result.is_ok()) {
all_annotations.insert(annotations_result.value().cbegin(), annotations_result.value().cend());
}
for (const auto& key : allowlist) {
if (all_annotations.find(key) == all_annotations.end()) {
// There is an annotation in the allowlist that was not produced by any provider. This
// indicates a logical error on the Feedback-side.
all_annotations.insert({key, AnnotationOr(Error::kLogicError)});
}
}
return all_annotations;
}
// Create a complete list set of attachments from the collected attachments and the allowlist.
Attachments AllAttachments(const AttachmentKeys& allowlist,
const ::fit::result<Attachments>& attachments_result) {
Attachments all_attachments;
if (attachments_result.is_ok()) {
// Because attachments can contain large blobs of text and we only care about the state of the
// attachment and its associated error, we don't copy the value of the attachment.
for (const auto& [k, v] : attachments_result.value()) {
switch (v.State()) {
case AttachmentValue::State::kComplete:
all_attachments.insert({k, AttachmentValue("")});
break;
case AttachmentValue::State::kPartial:
all_attachments.insert({k, AttachmentValue("", v.Error())});
break;
case AttachmentValue::State::kMissing:
all_attachments.insert({k, v});
break;
}
}
}
for (const auto& key : allowlist) {
if (all_attachments.find(key) == all_attachments.end()) {
all_attachments.insert({key, AttachmentValue(Error::kLogicError)});
}
}
return all_attachments;
}
void AddAttachments(const AttachmentKeys& attachment_allowlist,
const ::fit::result<Attachments>& attachments_result,
rapidjson::Document* integrity_report) {
if (attachment_allowlist.empty()) {
return;
}
rapidjson::Document::AllocatorType& allocator = integrity_report->GetAllocator();
for (const auto& [name, v] : AllAttachments(attachment_allowlist, attachments_result)) {
rapidjson::Value attachment(rapidjson::kObjectType);
attachment.AddMember("state", rapidjson::Value(ToString(v.State()), allocator), allocator);
if (v.HasError()) {
attachment.AddMember("reason", rapidjson::Value(ToReason(v.Error()), allocator), allocator);
}
integrity_report->AddMember(rapidjson::Value(name, allocator), attachment, allocator);
}
}
void AddAnnotationsJson(const AnnotationKeys& annotation_allowlist,
const ::fit::result<Annotations>& annotations_result,
const bool missing_non_platform_annotations,
rapidjson::Document* integrity_report) {
const Annotations all_annotations = AllAnnotations(annotation_allowlist, annotations_result);
bool has_non_platform = all_annotations.size() > annotation_allowlist.size();
if (annotation_allowlist.empty() && !(has_non_platform || missing_non_platform_annotations)) {
return;
}
rapidjson::Document::AllocatorType& allocator = integrity_report->GetAllocator();
rapidjson::Value present(rapidjson::kArrayType);
rapidjson::Value missing(rapidjson::kObjectType);
size_t num_present_platform = 0u;
size_t num_missing_platform = 0u;
for (const auto& [k, v] : all_annotations) {
if (annotation_allowlist.find(k) == annotation_allowlist.end()) {
continue;
}
rapidjson::Value key(k, allocator);
if (v.HasValue()) {
present.PushBack(key, allocator);
++num_present_platform;
} else {
missing.AddMember(key, rapidjson::Value(ToReason(v.Error()), allocator), allocator);
++num_missing_platform;
}
}
if (has_non_platform || missing_non_platform_annotations) {
if (!missing_non_platform_annotations) {
present.PushBack("non-platform annotations", allocator);
} else {
missing.AddMember("non-platform annotations", "too many non-platfrom annotations added",
allocator);
}
}
rapidjson::Value state;
if (num_present_platform == annotation_allowlist.size() && !missing_non_platform_annotations) {
state = "complete";
} else if (num_missing_platform == annotation_allowlist.size() && !has_non_platform &&
missing_non_platform_annotations) {
state = "missing";
} else {
state = "partial";
}
rapidjson::Value annotations_json(rapidjson::kObjectType);
annotations_json.AddMember("state", state, allocator);
annotations_json.AddMember("missing annotations", missing, allocator);
annotations_json.AddMember("present annotations", present, allocator);
integrity_report->AddMember("annotations.json", annotations_json, allocator);
}
} // namespace
IntegrityReporter::IntegrityReporter(const AnnotationKeys& annotation_allowlist,
const AttachmentKeys& attachment_allowlist)
: annotation_allowlist_(annotation_allowlist), attachment_allowlist_(attachment_allowlist) {}
std::optional<std::string> IntegrityReporter::MakeIntegrityReport(
const ::fit::result<Annotations>& annotations_result,
const ::fit::result<Attachments>& attachments_result,
bool missing_non_platform_annotations) const {
const bool has_non_platform_annotations =
annotations_result.is_ok() &&
annotations_result.value().size() > annotation_allowlist_.size();
if (annotation_allowlist_.empty() && attachment_allowlist_.empty() &&
!has_non_platform_annotations && !missing_non_platform_annotations) {
return std::nullopt;
}
rapidjson::Document integrity_report(rapidjson::kObjectType);
AddAttachments(attachment_allowlist_, attachments_result, &integrity_report);
AddAnnotationsJson(annotation_allowlist_, annotations_result, missing_non_platform_annotations,
&integrity_report);
rapidjson::StringBuffer buffer;
rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(buffer);
integrity_report.Accept(writer);
return buffer.GetString();
}
} // namespace feedback