feat(shorebird_code_push_protocol): add unique-users metric API surface (#3814)
This commit is contained in:
@@ -35,6 +35,7 @@ export 'package:shorebird_code_push_protocol/src/messages/get_release/get_releas
|
||||
export 'package:shorebird_code_push_protocol/src/messages/get_release_artifacts/get_release_artifacts_response.dart';
|
||||
export 'package:shorebird_code_push_protocol/src/messages/get_release_patches/get_release_patches_response.dart';
|
||||
export 'package:shorebird_code_push_protocol/src/messages/get_releases/get_releases_response.dart';
|
||||
export 'package:shorebird_code_push_protocol/src/messages/get_unique_users/get_unique_users_response.dart';
|
||||
export 'package:shorebird_code_push_protocol/src/messages/get_version_distribution/get_version_distribution_response.dart';
|
||||
export 'package:shorebird_code_push_protocol/src/messages/patch_check/patch_check_request.dart';
|
||||
export 'package:shorebird_code_push_protocol/src/messages/patch_check/patch_check_response.dart';
|
||||
@@ -48,6 +49,8 @@ export 'package:shorebird_code_push_protocol/src/models/app_collaborator_role.da
|
||||
export 'package:shorebird_code_push_protocol/src/models/app_metadata.dart';
|
||||
export 'package:shorebird_code_push_protocol/src/models/channel.dart';
|
||||
export 'package:shorebird_code_push_protocol/src/models/get_patch_adoption_parameter3.dart';
|
||||
export 'package:shorebird_code_push_protocol/src/models/get_unique_users_parameter2.dart';
|
||||
export 'package:shorebird_code_push_protocol/src/models/get_unique_users_parameter3.dart';
|
||||
export 'package:shorebird_code_push_protocol/src/models/latest_release.dart';
|
||||
export 'package:shorebird_code_push_protocol/src/models/organization.dart';
|
||||
export 'package:shorebird_code_push_protocol/src/models/organization_membership.dart';
|
||||
@@ -69,6 +72,9 @@ export 'package:shorebird_code_push_protocol/src/models/release_patch.dart';
|
||||
export 'package:shorebird_code_push_protocol/src/models/release_platform.dart';
|
||||
export 'package:shorebird_code_push_protocol/src/models/release_status.dart';
|
||||
export 'package:shorebird_code_push_protocol/src/models/role.dart';
|
||||
export 'package:shorebird_code_push_protocol/src/models/unique_users_breakdown_entry.dart';
|
||||
export 'package:shorebird_code_push_protocol/src/models/unique_users_range.dart';
|
||||
export 'package:shorebird_code_push_protocol/src/models/unique_users_time_series_entry.dart';
|
||||
export 'package:shorebird_code_push_protocol/src/models/version_distribution_entry.dart';
|
||||
|
||||
/// Parsed JSON data.
|
||||
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:shorebird_code_push_protocol/model_helpers.dart';
|
||||
import 'package:shorebird_code_push_protocol/src/models/unique_users_breakdown_entry.dart';
|
||||
import 'package:shorebird_code_push_protocol/src/models/unique_users_range.dart';
|
||||
import 'package:shorebird_code_push_protocol/src/models/unique_users_time_series_entry.dart';
|
||||
|
||||
/// {@template get_unique_users_response}
|
||||
/// The response body for GET /apps/{appId}/metrics/unique-users.
|
||||
/// {@endtemplate}
|
||||
@immutable
|
||||
class GetUniqueUsersResponse {
|
||||
/// {@macro get_unique_users_response}
|
||||
const GetUniqueUsersResponse({
|
||||
required this.uniqueUsers,
|
||||
required this.granularity,
|
||||
required this.range,
|
||||
required this.asOf,
|
||||
this.timeSeries,
|
||||
this.breakdown,
|
||||
});
|
||||
|
||||
/// Converts a `Map<String, dynamic>` to a [GetUniqueUsersResponse].
|
||||
factory GetUniqueUsersResponse.fromJson(Map<String, dynamic> json) {
|
||||
return parseFromJson(
|
||||
'GetUniqueUsersResponse',
|
||||
json,
|
||||
() => GetUniqueUsersResponse(
|
||||
uniqueUsers: json['unique_users'] as int,
|
||||
granularity: checkedKey(json, 'granularity') as String?,
|
||||
range: UniqueUsersRange.fromJson(json['range'] as Map<String, dynamic>),
|
||||
asOf: DateTime.parse(json['as_of'] as String),
|
||||
timeSeries: (json['time_series'] as List?)
|
||||
?.map<UniqueUsersTimeSeriesEntry>(
|
||||
(e) => UniqueUsersTimeSeriesEntry.fromJson(
|
||||
e as Map<String, dynamic>,
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
breakdown: (json['breakdown'] as List?)
|
||||
?.map<UniqueUsersBreakdownEntry>(
|
||||
(e) =>
|
||||
UniqueUsersBreakdownEntry.fromJson(e as Map<String, dynamic>),
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Convenience to create a nullable type from a nullable json object.
|
||||
/// Useful when parsing optional fields.
|
||||
static GetUniqueUsersResponse? maybeFromJson(Map<String, dynamic>? json) {
|
||||
if (json == null) {
|
||||
return null;
|
||||
}
|
||||
return GetUniqueUsersResponse.fromJson(json);
|
||||
}
|
||||
|
||||
/// Distinct active devices over the whole window (an HLL count).
|
||||
final int uniqueUsers;
|
||||
|
||||
/// The time-series bucket resolution (`hour`, `day`, `week`, or
|
||||
/// `month`), or null when no time series was requested.
|
||||
final String? granularity;
|
||||
|
||||
/// The window the unique-users response covers.
|
||||
final UniqueUsersRange range;
|
||||
|
||||
/// Server's UTC timestamp at the moment the response was constructed.
|
||||
/// Not a freshness indicator for the underlying data, which is
|
||||
/// refreshed by an hourly scheduled query and may lag by up to ~1 hour.
|
||||
final DateTime asOf;
|
||||
|
||||
/// The top-level time series, present only when a `granularity` was
|
||||
/// requested; otherwise null.
|
||||
final List<UniqueUsersTimeSeriesEntry>? timeSeries;
|
||||
|
||||
/// Per-group unique users, present only when a `group_by` was
|
||||
/// requested; otherwise null.
|
||||
final List<UniqueUsersBreakdownEntry>? breakdown;
|
||||
|
||||
/// Converts a [GetUniqueUsersResponse] to a `Map<String, dynamic>`.
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'unique_users': uniqueUsers,
|
||||
'granularity': granularity,
|
||||
'range': range.toJson(),
|
||||
'as_of': asOf.toIso8601String(),
|
||||
'time_series': timeSeries?.map((e) => e.toJson()).toList(),
|
||||
'breakdown': breakdown?.map((e) => e.toJson()).toList(),
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hashAll([
|
||||
uniqueUsers,
|
||||
granularity,
|
||||
range,
|
||||
asOf,
|
||||
listHash(timeSeries),
|
||||
listHash(breakdown),
|
||||
]);
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
return other is GetUniqueUsersResponse &&
|
||||
uniqueUsers == other.uniqueUsers &&
|
||||
granularity == other.granularity &&
|
||||
range == other.range &&
|
||||
asOf == other.asOf &&
|
||||
listsEqual(timeSeries, other.timeSeries) &&
|
||||
listsEqual(breakdown, other.breakdown);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
enum GetUniqueUsersParameter2 {
|
||||
hour._('hour'),
|
||||
day._('day'),
|
||||
week._('week'),
|
||||
month._('month');
|
||||
|
||||
const GetUniqueUsersParameter2._(this.value);
|
||||
|
||||
/// Creates a GetUniqueUsersParameter2 from a json value.
|
||||
factory GetUniqueUsersParameter2.fromJson(String json) {
|
||||
return GetUniqueUsersParameter2.values.firstWhere(
|
||||
(value) => value.value == json,
|
||||
orElse: () => throw FormatException(
|
||||
'Unknown GetUniqueUsersParameter2 value: $json',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Convenience to create a nullable type from a nullable json value.
|
||||
/// Useful when parsing optional fields.
|
||||
static GetUniqueUsersParameter2? maybeFromJson(String? json) {
|
||||
if (json == null) {
|
||||
return null;
|
||||
}
|
||||
return GetUniqueUsersParameter2.fromJson(json);
|
||||
}
|
||||
|
||||
/// The value of the enum. This is the exact value
|
||||
/// from the OpenAPI spec and will be used for network transport.
|
||||
final String value;
|
||||
|
||||
/// Converts the enum to its json value.
|
||||
String toJson() => value;
|
||||
|
||||
/// Returns the string form of the enum.
|
||||
@override
|
||||
String toString() => value;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
enum GetUniqueUsersParameter3 {
|
||||
platform._('platform');
|
||||
|
||||
const GetUniqueUsersParameter3._(this.value);
|
||||
|
||||
/// Creates a GetUniqueUsersParameter3 from a json value.
|
||||
factory GetUniqueUsersParameter3.fromJson(String json) {
|
||||
return GetUniqueUsersParameter3.values.firstWhere(
|
||||
(value) => value.value == json,
|
||||
orElse: () => throw FormatException(
|
||||
'Unknown GetUniqueUsersParameter3 value: $json',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Convenience to create a nullable type from a nullable json value.
|
||||
/// Useful when parsing optional fields.
|
||||
static GetUniqueUsersParameter3? maybeFromJson(String? json) {
|
||||
if (json == null) {
|
||||
return null;
|
||||
}
|
||||
return GetUniqueUsersParameter3.fromJson(json);
|
||||
}
|
||||
|
||||
/// The value of the enum. This is the exact value
|
||||
/// from the OpenAPI spec and will be used for network transport.
|
||||
final String value;
|
||||
|
||||
/// Converts the enum to its json value.
|
||||
String toJson() => value;
|
||||
|
||||
/// Returns the string form of the enum.
|
||||
@override
|
||||
String toString() => value;
|
||||
}
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:shorebird_code_push_protocol/model_helpers.dart';
|
||||
import 'package:shorebird_code_push_protocol/src/models/unique_users_time_series_entry.dart';
|
||||
|
||||
/// {@template unique_users_breakdown_entry}
|
||||
/// Unique users for one value of the `group_by` dimension (e.g. one
|
||||
/// platform), optionally with its own time series.
|
||||
/// {@endtemplate}
|
||||
@immutable
|
||||
class UniqueUsersBreakdownEntry {
|
||||
/// {@macro unique_users_breakdown_entry}
|
||||
const UniqueUsersBreakdownEntry({
|
||||
required this.groupBy,
|
||||
required this.groupValue,
|
||||
required this.uniqueUsers,
|
||||
this.timeSeries,
|
||||
});
|
||||
|
||||
/// Converts a `Map<String, dynamic>` to a [UniqueUsersBreakdownEntry].
|
||||
factory UniqueUsersBreakdownEntry.fromJson(Map<String, dynamic> json) {
|
||||
return parseFromJson(
|
||||
'UniqueUsersBreakdownEntry',
|
||||
json,
|
||||
() => UniqueUsersBreakdownEntry(
|
||||
groupBy: json['group_by'] as String,
|
||||
groupValue: json['group_value'] as String,
|
||||
uniqueUsers: json['unique_users'] as int,
|
||||
timeSeries: (json['time_series'] as List?)
|
||||
?.map<UniqueUsersTimeSeriesEntry>(
|
||||
(e) => UniqueUsersTimeSeriesEntry.fromJson(
|
||||
e as Map<String, dynamic>,
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Convenience to create a nullable type from a nullable json object.
|
||||
/// Useful when parsing optional fields.
|
||||
static UniqueUsersBreakdownEntry? maybeFromJson(Map<String, dynamic>? json) {
|
||||
if (json == null) {
|
||||
return null;
|
||||
}
|
||||
return UniqueUsersBreakdownEntry.fromJson(json);
|
||||
}
|
||||
|
||||
/// The dimension this entry breaks down by (e.g. "platform").
|
||||
final String groupBy;
|
||||
|
||||
/// The value within `group_by` (e.g. "android").
|
||||
final String groupValue;
|
||||
|
||||
/// Distinct active devices for this group over the window (an HLL
|
||||
/// count).
|
||||
final int uniqueUsers;
|
||||
|
||||
/// Per-bucket series for this group, present only when a `granularity`
|
||||
/// was requested; otherwise null.
|
||||
final List<UniqueUsersTimeSeriesEntry>? timeSeries;
|
||||
|
||||
/// Converts a [UniqueUsersBreakdownEntry] to a `Map<String, dynamic>`.
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'group_by': groupBy,
|
||||
'group_value': groupValue,
|
||||
'unique_users': uniqueUsers,
|
||||
'time_series': timeSeries?.map((e) => e.toJson()).toList(),
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hashAll([
|
||||
groupBy,
|
||||
groupValue,
|
||||
uniqueUsers,
|
||||
listHash(timeSeries),
|
||||
]);
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
return other is UniqueUsersBreakdownEntry &&
|
||||
groupBy == other.groupBy &&
|
||||
groupValue == other.groupValue &&
|
||||
uniqueUsers == other.uniqueUsers &&
|
||||
listsEqual(timeSeries, other.timeSeries);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:shorebird_code_push_protocol/model_helpers.dart';
|
||||
|
||||
/// {@template unique_users_range}
|
||||
/// The window the unique-users response covers.
|
||||
/// {@endtemplate}
|
||||
@immutable
|
||||
class UniqueUsersRange {
|
||||
/// {@macro unique_users_range}
|
||||
const UniqueUsersRange({
|
||||
required this.start,
|
||||
required this.end,
|
||||
});
|
||||
|
||||
/// Converts a `Map<String, dynamic>` to a [UniqueUsersRange].
|
||||
factory UniqueUsersRange.fromJson(Map<String, dynamic> json) {
|
||||
return parseFromJson(
|
||||
'UniqueUsersRange',
|
||||
json,
|
||||
() => UniqueUsersRange(
|
||||
start: DateTime.parse(json['start'] as String),
|
||||
end: DateTime.parse(json['end'] as String),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Convenience to create a nullable type from a nullable json object.
|
||||
/// Useful when parsing optional fields.
|
||||
static UniqueUsersRange? maybeFromJson(Map<String, dynamic>? json) {
|
||||
if (json == null) {
|
||||
return null;
|
||||
}
|
||||
return UniqueUsersRange.fromJson(json);
|
||||
}
|
||||
|
||||
/// Window start (UTC, inclusive).
|
||||
final DateTime start;
|
||||
|
||||
/// Window end (UTC, exclusive).
|
||||
final DateTime end;
|
||||
|
||||
/// Converts a [UniqueUsersRange] to a `Map<String, dynamic>`.
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'start': start.toIso8601String(),
|
||||
'end': end.toIso8601String(),
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hashAll([
|
||||
start,
|
||||
end,
|
||||
]);
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
return other is UniqueUsersRange &&
|
||||
start == other.start &&
|
||||
end == other.end;
|
||||
}
|
||||
}
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:shorebird_code_push_protocol/model_helpers.dart';
|
||||
|
||||
/// {@template unique_users_time_series_entry}
|
||||
/// One bucket of a unique-users time series: the HLL count of distinct
|
||||
/// active devices in the bucket starting at `period`.
|
||||
/// {@endtemplate}
|
||||
@immutable
|
||||
class UniqueUsersTimeSeriesEntry {
|
||||
/// {@macro unique_users_time_series_entry}
|
||||
const UniqueUsersTimeSeriesEntry({
|
||||
required this.period,
|
||||
required this.uniqueUsers,
|
||||
});
|
||||
|
||||
/// Converts a `Map<String, dynamic>` to a [UniqueUsersTimeSeriesEntry].
|
||||
factory UniqueUsersTimeSeriesEntry.fromJson(Map<String, dynamic> json) {
|
||||
return parseFromJson(
|
||||
'UniqueUsersTimeSeriesEntry',
|
||||
json,
|
||||
() => UniqueUsersTimeSeriesEntry(
|
||||
period: DateTime.parse(json['period'] as String),
|
||||
uniqueUsers: json['unique_users'] as int,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Convenience to create a nullable type from a nullable json object.
|
||||
/// Useful when parsing optional fields.
|
||||
static UniqueUsersTimeSeriesEntry? maybeFromJson(Map<String, dynamic>? json) {
|
||||
if (json == null) {
|
||||
return null;
|
||||
}
|
||||
return UniqueUsersTimeSeriesEntry.fromJson(json);
|
||||
}
|
||||
|
||||
/// The bucket start (UTC).
|
||||
final DateTime period;
|
||||
|
||||
/// Distinct active devices in this bucket (an HLL count).
|
||||
final int uniqueUsers;
|
||||
|
||||
/// Converts a [UniqueUsersTimeSeriesEntry] to a `Map<String, dynamic>`.
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'period': period.toIso8601String(),
|
||||
'unique_users': uniqueUsers,
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hashAll([
|
||||
period,
|
||||
uniqueUsers,
|
||||
]);
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
return other is UniqueUsersTimeSeriesEntry &&
|
||||
period == other.period &&
|
||||
uniqueUsers == other.uniqueUsers;
|
||||
}
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
// GENERATED — do not hand-edit.
|
||||
import 'package:shorebird_code_push_protocol/shorebird_code_push_protocol.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
group('GetUniqueUsersResponse', () {
|
||||
test('round-trips via maybeFromJson/toJson', () {
|
||||
final instance = GetUniqueUsersResponse(
|
||||
uniqueUsers: 0,
|
||||
granularity: 'example',
|
||||
range: UniqueUsersRange(
|
||||
start: DateTime.utc(2024),
|
||||
end: DateTime.utc(2024),
|
||||
),
|
||||
asOf: DateTime.utc(2024),
|
||||
);
|
||||
final parsed = GetUniqueUsersResponse.maybeFromJson(instance.toJson());
|
||||
expect(parsed, equals(instance));
|
||||
expect(parsed.hashCode, equals(instance.hashCode));
|
||||
});
|
||||
|
||||
test('maybeFromJson returns null on null input', () {
|
||||
expect(GetUniqueUsersResponse.maybeFromJson(null), isNull);
|
||||
});
|
||||
|
||||
test('maybeFromJson throws FormatException on invalid input', () {
|
||||
expect(
|
||||
() => GetUniqueUsersResponse.maybeFromJson(<String, dynamic>{}),
|
||||
throwsFormatException,
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
// GENERATED — do not hand-edit.
|
||||
import 'package:shorebird_code_push_protocol/shorebird_code_push_protocol.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
group('GetUniqueUsersParameter2', () {
|
||||
test('round-trips via maybeFromJson/toJson', () {
|
||||
final instance = GetUniqueUsersParameter2.values.first;
|
||||
final parsed = GetUniqueUsersParameter2.maybeFromJson(instance.toJson());
|
||||
expect(parsed, equals(instance));
|
||||
expect(parsed.hashCode, equals(instance.hashCode));
|
||||
});
|
||||
|
||||
test('maybeFromJson returns null on null input', () {
|
||||
expect(GetUniqueUsersParameter2.maybeFromJson(null), isNull);
|
||||
});
|
||||
|
||||
test('maybeFromJson throws FormatException on invalid input', () {
|
||||
expect(
|
||||
() => GetUniqueUsersParameter2.maybeFromJson('__invalid_enum_value__'),
|
||||
throwsFormatException,
|
||||
);
|
||||
});
|
||||
|
||||
test('toString matches toJson for every value', () {
|
||||
for (final value in GetUniqueUsersParameter2.values) {
|
||||
expect(value.toString(), equals(value.toJson()));
|
||||
}
|
||||
});
|
||||
|
||||
test('fromJson round-trips every value', () {
|
||||
for (final value in GetUniqueUsersParameter2.values) {
|
||||
expect(
|
||||
GetUniqueUsersParameter2.fromJson(value.toJson()),
|
||||
equals(value),
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
// GENERATED — do not hand-edit.
|
||||
import 'package:shorebird_code_push_protocol/shorebird_code_push_protocol.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
group('GetUniqueUsersParameter3', () {
|
||||
test('round-trips via maybeFromJson/toJson', () {
|
||||
final instance = GetUniqueUsersParameter3.values.first;
|
||||
final parsed = GetUniqueUsersParameter3.maybeFromJson(instance.toJson());
|
||||
expect(parsed, equals(instance));
|
||||
expect(parsed.hashCode, equals(instance.hashCode));
|
||||
});
|
||||
|
||||
test('maybeFromJson returns null on null input', () {
|
||||
expect(GetUniqueUsersParameter3.maybeFromJson(null), isNull);
|
||||
});
|
||||
|
||||
test('maybeFromJson throws FormatException on invalid input', () {
|
||||
expect(
|
||||
() => GetUniqueUsersParameter3.maybeFromJson('__invalid_enum_value__'),
|
||||
throwsFormatException,
|
||||
);
|
||||
});
|
||||
|
||||
test('toString matches toJson for every value', () {
|
||||
for (final value in GetUniqueUsersParameter3.values) {
|
||||
expect(value.toString(), equals(value.toJson()));
|
||||
}
|
||||
});
|
||||
|
||||
test('fromJson round-trips every value', () {
|
||||
for (final value in GetUniqueUsersParameter3.values) {
|
||||
expect(
|
||||
GetUniqueUsersParameter3.fromJson(value.toJson()),
|
||||
equals(value),
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
// GENERATED — do not hand-edit.
|
||||
import 'package:shorebird_code_push_protocol/shorebird_code_push_protocol.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
group('UniqueUsersBreakdownEntry', () {
|
||||
test('round-trips via maybeFromJson/toJson', () {
|
||||
final instance = UniqueUsersBreakdownEntry(
|
||||
groupBy: 'example',
|
||||
groupValue: 'example',
|
||||
uniqueUsers: 0,
|
||||
);
|
||||
final parsed = UniqueUsersBreakdownEntry.maybeFromJson(
|
||||
instance.toJson(),
|
||||
);
|
||||
expect(parsed, equals(instance));
|
||||
expect(parsed.hashCode, equals(instance.hashCode));
|
||||
});
|
||||
|
||||
test('maybeFromJson returns null on null input', () {
|
||||
expect(UniqueUsersBreakdownEntry.maybeFromJson(null), isNull);
|
||||
});
|
||||
|
||||
test('maybeFromJson throws FormatException on invalid input', () {
|
||||
expect(
|
||||
() => UniqueUsersBreakdownEntry.maybeFromJson(<String, dynamic>{}),
|
||||
throwsFormatException,
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
// GENERATED — do not hand-edit.
|
||||
import 'package:shorebird_code_push_protocol/shorebird_code_push_protocol.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
group('UniqueUsersRange', () {
|
||||
test('round-trips via maybeFromJson/toJson', () {
|
||||
final instance = UniqueUsersRange(
|
||||
start: DateTime.utc(2024),
|
||||
end: DateTime.utc(2024),
|
||||
);
|
||||
final parsed = UniqueUsersRange.maybeFromJson(instance.toJson());
|
||||
expect(parsed, equals(instance));
|
||||
expect(parsed.hashCode, equals(instance.hashCode));
|
||||
});
|
||||
|
||||
test('maybeFromJson returns null on null input', () {
|
||||
expect(UniqueUsersRange.maybeFromJson(null), isNull);
|
||||
});
|
||||
|
||||
test('maybeFromJson throws FormatException on invalid input', () {
|
||||
expect(
|
||||
() => UniqueUsersRange.maybeFromJson(<String, dynamic>{}),
|
||||
throwsFormatException,
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
// GENERATED — do not hand-edit.
|
||||
import 'package:shorebird_code_push_protocol/shorebird_code_push_protocol.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
group('UniqueUsersTimeSeriesEntry', () {
|
||||
test('round-trips via maybeFromJson/toJson', () {
|
||||
final instance = UniqueUsersTimeSeriesEntry(
|
||||
period: DateTime.utc(2024),
|
||||
uniqueUsers: 0,
|
||||
);
|
||||
final parsed = UniqueUsersTimeSeriesEntry.maybeFromJson(
|
||||
instance.toJson(),
|
||||
);
|
||||
expect(parsed, equals(instance));
|
||||
expect(parsed.hashCode, equals(instance.hashCode));
|
||||
});
|
||||
|
||||
test('maybeFromJson returns null on null input', () {
|
||||
expect(UniqueUsersTimeSeriesEntry.maybeFromJson(null), isNull);
|
||||
});
|
||||
|
||||
test('maybeFromJson throws FormatException on invalid input', () {
|
||||
expect(
|
||||
() => UniqueUsersTimeSeriesEntry.maybeFromJson(<String, dynamic>{}),
|
||||
throwsFormatException,
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user