chromium/google_apis/classroom/classroom_api_course_work_response_types.cc

// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "google_apis/classroom/classroom_api_course_work_response_types.h"

#include <memory>
#include <optional>
#include <string>
#include <string_view>

#include "base/json/json_value_converter.h"
#include "base/time/time.h"
#include "base/values.h"
#include "google_apis/common/parser_util.h"
#include "google_apis/common/time_util.h"
#include "url/gurl.h"

namespace google_apis::classroom {
namespace {

constexpr char kApiResponseCourseWorkKey[] = "courseWork";
constexpr char kApiResponseCourseWorkItemAlternateLinkKey[] = "alternateLink";
constexpr char kApiResponseCourseWorkItemCreationTimeKey[] = "creationTime";
constexpr char kApiResponseCourseWorkItemUpdateTimeKey[] = "updateTime";
constexpr char kApiResponseCourseWorkItemDueDateKey[] = "dueDate";
constexpr char kApiResponseCourseWorkItemDueTimeKey[] = "dueTime";
constexpr char kApiResponseCourseWorkItemStateKey[] = "state";
constexpr char kApiResponseCourseWorkItemTitleKey[] = "title";

constexpr char kDueDateYearComponent[] = "year";
constexpr char kDueDateMonthComponent[] = "month";
constexpr char kDueDateDayComponent[] = "day";

constexpr char kDueTimeHoursComponent[] = "hours";
constexpr char kDueTimeMinutesComponent[] = "minutes";
constexpr char kDueTimeSecondsComponent[] = "seconds";
constexpr char kDueTimeNanosComponent[] = "nanos";

constexpr char kPublishedCourseWorkItemState[] = "PUBLISHED";

bool ConvertCourseWorkItemState(std::string_view input,
                                CourseWorkItem::State* output) {
  *output = input == kPublishedCourseWorkItemState
                ? CourseWorkItem::State::kPublished
                : CourseWorkItem::State::kOther;
  return true;
}

bool ConvertCourseWorkItemAlternateLink(std::string_view input, GURL* output) {
  *output = GURL(input);
  return true;
}

base::TimeDelta GetCourseWorkItemDueTime(
    const base::Value::Dict& raw_course_work_item) {
  const auto* const time =
      raw_course_work_item.FindDict(kApiResponseCourseWorkItemDueTimeKey);
  if (!time) {
    return base::TimeDelta();
  }

  const auto hours = time->FindInt(kDueTimeHoursComponent);
  const auto minutes = time->FindInt(kDueTimeMinutesComponent);
  const auto seconds = time->FindInt(kDueTimeSecondsComponent);
  const auto nanos = time->FindInt(kDueTimeNanosComponent);

  return base::Hours(hours.value_or(0)) + base::Minutes(minutes.value_or(0)) +
         base::Seconds(seconds.value_or(0)) +
         base::Nanoseconds(nanos.value_or(0));
}

std::optional<CourseWorkItem::DueDateTime> GetCourseWorkItemDueDateTime(
    const base::Value::Dict& raw_course_work_item) {
  const auto* const date =
      raw_course_work_item.FindDict(kApiResponseCourseWorkItemDueDateKey);
  if (!date) {
    return std::nullopt;
  }

  const auto year = date->FindInt(kDueDateYearComponent);
  const auto month = date->FindInt(kDueDateMonthComponent);
  const auto day = date->FindInt(kDueDateDayComponent);

  if (!year.has_value() && !month.has_value() && !day.has_value()) {
    return std::nullopt;
  }

  return CourseWorkItem::DueDateTime{
      .year = year.value_or(0),
      .month = month.value_or(0),
      .day = day.value_or(0),
      .time_of_day = GetCourseWorkItemDueTime(raw_course_work_item)};
}

}  // namespace

// ----- CourseWorkItem -----

CourseWorkItem::CourseWorkItem() = default;

CourseWorkItem::~CourseWorkItem() = default;

// static
void CourseWorkItem::RegisterJSONConverter(
    base::JSONValueConverter<CourseWorkItem>* converter) {
  converter->RegisterStringField(kApiResponseIdKey, &CourseWorkItem::id_);
  converter->RegisterStringField(kApiResponseCourseWorkItemTitleKey,
                                 &CourseWorkItem::title_);
  converter->RegisterCustomField<CourseWorkItem::State>(
      kApiResponseCourseWorkItemStateKey, &CourseWorkItem::state_,
      &ConvertCourseWorkItemState);
  converter->RegisterCustomField<GURL>(
      kApiResponseCourseWorkItemAlternateLinkKey,
      &CourseWorkItem::alternate_link_, &ConvertCourseWorkItemAlternateLink);
  converter->RegisterCustomField<base::Time>(
      kApiResponseCourseWorkItemCreationTimeKey,
      &CourseWorkItem::creation_time_, &util::GetTimeFromString);
  converter->RegisterCustomField<base::Time>(
      kApiResponseCourseWorkItemUpdateTimeKey, &CourseWorkItem::last_update_,
      &util::GetTimeFromString);
}

// static
bool CourseWorkItem::ConvertCourseWorkItem(const base::Value* input,
                                           CourseWorkItem* output) {
  base::JSONValueConverter<CourseWorkItem> converter;
  const base::Value::Dict* dict = input->GetIfDict();
  if (!dict || !converter.Convert(*dict, output)) {
    return false;
  }

  output->due_date_time_ = GetCourseWorkItemDueDateTime(*dict);
  return true;
}

// ----- CourseWork -----

CourseWork::CourseWork() = default;

CourseWork::~CourseWork() = default;

// static
void CourseWork::RegisterJSONConverter(
    base::JSONValueConverter<CourseWork>* converter) {
  // TODO(crbug.com/40911919): Handle base::Value::Dict here.
  converter->RegisterRepeatedCustomValue<CourseWorkItem>(
      kApiResponseCourseWorkKey, &CourseWork::items_,
      &CourseWorkItem::ConvertCourseWorkItem);
  converter->RegisterStringField(kApiResponseNextPageTokenKey,
                                 &CourseWork::next_page_token_);
}

// static
std::unique_ptr<CourseWork> CourseWork::CreateFrom(const base::Value& value) {
  auto course_work = std::make_unique<CourseWork>();
  base::JSONValueConverter<CourseWork> converter;
  if (!converter.Convert(value, course_work.get())) {
    return nullptr;
  }
  return course_work;
}

}  // namespace google_apis::classroom