Доп. материал в рамках интенсива по тестированию API

Практика чтения gRPC-контракта

Изучите контракт аналитического сервиса, который считает агрегированные показатели по формированию привычек. Затем ответьте на вопросы по схеме.

Бизнес-контекст

В основной системе трекере пользователь создаёт привычки, отмечает прогресс и постепенно формирует устойчивые привычки. Обычное API приложения работает с индивидуальными данными пользователя: его привычками, его отметками прогресса и его текущим состоянием.

Для внутренней аналитики команда хочет вынести отдельный gRPC-сервис. Он должен считать агрегированные показатели: процент сформированных привычек пользователя, средний процент по платформе, место пользователя в рейтинге и общую статистику по всем пользователям.

Такой сервис решает задачи по сбору данных из разных частей системы и возвращению аналитических результатов, которые нужны для административной панели, продуктовой аналитики или внутренних отчётов.

Методы сервиса

Сервис предоставляет четыре RPC-метода. Каждый метод решает отдельную бизнес-задачу и показывает, как разные части системы могут использовать аналитические данные.

1. GetUserFormationAnalytics — аналитика по одному пользователю

Метод возвращает аналитику формирования привычек по конкретному пользователю: количество привычек пользователя, количество сформированных привычек, процент формирования, средний процент по платформе, сегмент пользователя, период аналитики и timestamp расчёта.

Сегмент пользователя может принимать значения NO_FORMED_HABITS, LOW, MEDIUM или HIGH. По нему можно быстро понять, к какой группе относится пользователь с точки зрения успешности формирования привычек.

Поле platform_average_formation_rate_percent возвращается в этом же ответе. Для получения среднего процента по платформе не нужен отдельный запрос: сервис сразу отдаёт индивидуальный показатель пользователя и средний показатель платформы, чтобы их можно было сопоставить.

2. CompareUserWithPlatform — сравнение пользователя с платформой

Метод возвращает процент формирования привычек пользователя, средний процент по платформе, разницу между ними, место пользователя в рейтинге и общее количество участников рейтинга.

Поле difference_from_platform_average_percent может быть положительным, отрицательным или равным нулю. Если пользователь выше среднего значения по платформе — разница будет положительной. Если ниже среднего — отрицательной. Если показатели совпадают — нулевой.

Рейтинг считается только за выбранный период, который передаётся в поле period. Поэтому один и тот же пользователь может иметь разные позиции в рейтинге за последние 7 дней, 30 дней, 90 дней или за всё время.

3. ListTopUsersByFormationRate — рейтинг пользователей

Метод возвращает рейтинг пользователей по проценту сформированных привычек. В ответе приходит список элементов рейтинга: ID пользователя, количество сформированных привычек, общее количество привычек, процент формирования и место в рейтинге.

Так как пользователей может быть много, в ответе используется список repeated и токен следующей страницы next_page_token. По нему клиент может запросить следующую страницу рейтинга.

Также в ответе есть время расчёта. Это важно для аналитики, потому что рейтинг может строиться не в реальном времени, а после пересчёта данных.

4. WatchPlatformAnalytics — поток обновлений платформы

Метод возвращает поток событий. Это значит, что сервер может отправить одно или несколько сообщений PlatformAnalyticsUpdatedEvent в рамках одного соединения.

В событии передаётся общая статистика платформы: количество пользователей, общее количество привычек, количество сформированных привычек, средний процент формирования по платформе и timestamp события.

Запрос может содержать optional-поле segment. Если оно не передано, клиент подписывается на обновления по всем сегментам. Если сегмент указан, сервер должен отправлять только события по конкретному сегменту пользователей.

Контракт analytics.proto

Сначала прочитайте контракт целиком. После этого переходите к вопросам.

syntax = "proto3";

package trackhabits.analytics.v1;

option go_package = "trackhabits/gen/analytics/v1;analyticsv1";

import "google/protobuf/timestamp.proto";

service HabitAnalyticsService {
  rpc GetUserFormationAnalytics(GetUserFormationAnalyticsRequest) returns (UserFormationAnalyticsResponse);

  rpc CompareUserWithPlatform(CompareUserWithPlatformRequest) returns (UserPlatformComparisonResponse);

  rpc ListTopUsersByFormationRate(ListTopUsersByFormationRateRequest) returns (ListTopUsersByFormationRateResponse);

  rpc WatchPlatformAnalytics(WatchPlatformAnalyticsRequest) returns (stream PlatformAnalyticsUpdatedEvent);
}

message GetUserFormationAnalyticsRequest {
  string user_id = 1;
  AnalyticsPeriod period = 2;
}

message UserFormationAnalyticsResponse {
  string user_id = 1;
  AnalyticsPeriod period = 2;
  int32 user_total_habits = 3;
  int32 user_formed_habits = 4;
  double user_formation_rate_percent = 5;
  double platform_average_formation_rate_percent = 6;
  UserFormationSegment segment = 7;
  google.protobuf.Timestamp calculated_at = 8;
}

message CompareUserWithPlatformRequest {
  string user_id = 1;
  AnalyticsPeriod period = 2;
}

message UserPlatformComparisonResponse {
  string user_id = 1;
  AnalyticsPeriod period = 2;
  double user_formation_rate_percent = 3;
  double platform_average_formation_rate_percent = 4;
  double difference_from_platform_average_percent = 5;
  int32 user_rank = 6;
  int32 total_users_in_rating = 7;
  google.protobuf.Timestamp calculated_at = 8;
}

message ListTopUsersByFormationRateRequest {
  AnalyticsPeriod period = 1;
  int32 page_size = 2;
  string page_token = 3;
}

message ListTopUsersByFormationRateResponse {
  repeated TopUserFormationRateItem items = 1;
  string next_page_token = 2;
  google.protobuf.Timestamp calculated_at = 3;
}

message TopUserFormationRateItem {
  string user_id = 1;
  int32 formed_habits = 2;
  int32 total_habits = 3;
  double formation_rate_percent = 4;
  int32 rank = 5;
}

message WatchPlatformAnalyticsRequest {
  AnalyticsPeriod period = 1;
  optional UserFormationSegment segment = 2;
}

message PlatformAnalyticsUpdatedEvent {
  AnalyticsPeriod period = 1;
  int32 total_users = 2;
  int32 total_habits = 3;
  int32 total_formed_habits = 4;
  double platform_average_formation_rate_percent = 5;
  google.protobuf.Timestamp occurred_at = 6;
}

enum AnalyticsPeriod {
  ANALYTICS_PERIOD_UNSPECIFIED = 0;
  ANALYTICS_PERIOD_LAST_7_DAYS = 1;
  ANALYTICS_PERIOD_LAST_30_DAYS = 2;
  ANALYTICS_PERIOD_LAST_90_DAYS = 3;
  ANALYTICS_PERIOD_ALL_TIME = 4;
}

enum UserFormationSegment {
  USER_FORMATION_SEGMENT_UNSPECIFIED = 0;
  USER_FORMATION_SEGMENT_NO_FORMED_HABITS = 1;
  USER_FORMATION_SEGMENT_LOW = 2;
  USER_FORMATION_SEGMENT_MEDIUM = 3;
  USER_FORMATION_SEGMENT_HIGH = 4;
}

Памятка по чтению контракта

service

Описывает gRPC-сервис и список методов, которые можно вызвать. В REST API аналогом был бы список endpoint-ов.

rpc

Описывает конкретный метод: какое сообщение он принимает и какое сообщение возвращает.

message

Описывает структуру запроса, ответа или вложенного объекта. Внутри message перечислены поля, их типы и номера.

enum

Задаёт ограниченный набор допустимых значений.

repeated

Означает список значений одного типа. В JSON это было бы похоже на массив.

stream

Означает поток сообщений. Сервер может вернуть не один ответ, а последовательность событий.

Квиз

Выберите ответы и нажмите «Проверить». После проверки появятся пояснения.

1. Какой сервис описан в контракте?

Сервис объявлен строкой service HabitAnalyticsService.

2. Какой метод возвращает поток событий?

У метода WatchPlatformAnalytics перед типом ответа указано ключевое слово stream.

3. Какие данные нужны для запроса аналитики по формированию привычек пользователя?

GetUserFormationAnalyticsRequest содержит поля user_id и period.

4. Какое поле показывает процент сформированных привычек пользователя?

Поле user_formation_rate_percent хранит процент сформированных привычек конкретного пользователя.

5. Какое поле показывает отличие пользователя от среднего значения по платформе?

Поле difference_from_platform_average_percent показывает разницу между показателем пользователя и средним значением по платформе.

6. Что означает repeated TopUserFormationRateItem items = 1?

Ключевое слово repeated означает список значений одного типа.

7. Где описаны допустимые периоды аналитики?

Периоды аналитики перечислены в enum AnalyticsPeriod.

8. Какое поле в WatchPlatformAnalyticsRequest является optional?

В запросе WatchPlatformAnalyticsRequest поле segment объявлено как optional.

9. Какой метод лучше всего подходит для получения рейтинга пользователей по проценту сформированных привычек?

Метод ListTopUsersByFormationRate возвращает список элементов рейтинга.

10. Как правильно интерпретировать значение ANALYTICS_PERIOD_UNSPECIFIED = 0 в enum AnalyticsPeriod?

В enum-е значение с номером 0 часто используют как техническое значение по умолчанию. В данном контракте UNSPECIFIED означает, что период явно не выбран.