Skip to content

Commit 8a92443

Browse files
author
serverpod_cloud
committed
feat(gc): 21021171c24845b30236807de279fff8d3780a63
1 parent c3b4875 commit 8a92443

2 files changed

Lines changed: 138 additions & 0 deletions

File tree

ground_control_client/lib/src/protocol/client.dart

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,51 @@ import 'package:serverpod_auth_bridge_client/serverpod_auth_bridge_client.dart'
6060
import 'package:serverpod_auth_client/serverpod_auth_client.dart' as _i26;
6161
import 'protocol.dart' as _i27;
6262

63+
/// Endpoint for global administrator to handle procurement for users.
64+
/// {@category Endpoint}
65+
class EndpointAdminProcurement extends _i1.EndpointRef {
66+
EndpointAdminProcurement(_i1.EndpointCaller caller) : super(caller);
67+
68+
@override
69+
String get name => 'adminProcurement';
70+
71+
/// Procures a product for a user.
72+
/// If [productVersion] is not provided, the latest version is used.
73+
/// If [overrideChecks] is true, the product availability checks are overridden.
74+
///
75+
/// Throws a [NotFoundException] if the user or product is not found.
76+
/// Throws a [InvalidValueException] if the user has no owner (not fully registered).
77+
_i2.Future<void> procureProduct({
78+
required String userEmail,
79+
required String productName,
80+
int? productVersion,
81+
bool? overrideChecks,
82+
}) =>
83+
caller.callServerEndpoint<void>(
84+
'adminProcurement',
85+
'procureProduct',
86+
{
87+
'userEmail': userEmail,
88+
'productName': productName,
89+
'productVersion': productVersion,
90+
'overrideChecks': overrideChecks,
91+
},
92+
);
93+
94+
/// Fetches a user's procured products.
95+
/// Returns a list of `(String, String)` with the product ID and its type.
96+
///
97+
/// Throws a [NotFoundException] if the user is not found.
98+
/// Throws a [InvalidValueException] if the user has no owner (not fully registered).
99+
_i2.Future<List<(String, String)>> listProcuredProducts(
100+
{required String userEmail}) =>
101+
caller.callServerEndpoint<List<(String, String)>>(
102+
'adminProcurement',
103+
'listProcuredProducts',
104+
{'userEmail': userEmail},
105+
);
106+
}
107+
63108
/// Endpoint for global administrator projects access.
64109
/// {@category Endpoint}
65110
class EndpointAdminProjects extends _i1.EndpointRef {
@@ -949,6 +994,7 @@ class Client extends _i1.ServerpodClientShared {
949994
disconnectStreamsOnLostInternetConnection:
950995
disconnectStreamsOnLostInternetConnection,
951996
) {
997+
adminProcurement = EndpointAdminProcurement(this);
952998
adminProjects = EndpointAdminProjects(this);
953999
adminUsers = EndpointAdminUsers(this);
9541000
auth = EndpointAuth(this);
@@ -969,6 +1015,8 @@ class Client extends _i1.ServerpodClientShared {
9691015
modules = Modules(this);
9701016
}
9711017

1018+
late final EndpointAdminProcurement adminProcurement;
1019+
9721020
late final EndpointAdminProjects adminProjects;
9731021

9741022
late final EndpointAdminUsers adminUsers;
@@ -1007,6 +1055,7 @@ class Client extends _i1.ServerpodClientShared {
10071055

10081056
@override
10091057
Map<String, _i1.EndpointRef> get endpointRefLookup => {
1058+
'adminProcurement': adminProcurement,
10101059
'adminProjects': adminProjects,
10111060
'adminUsers': adminUsers,
10121061
'auth': auth,

ground_control_client/lib/src/protocol/protocol.dart

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -510,6 +510,23 @@ class Protocol extends _i1.SerializationManager {
510510
.toList()
511511
: null) as T;
512512
}
513+
if (t == List<(String, String)>) {
514+
return (data as List)
515+
.map((e) => deserialize<(String, String)>(e))
516+
.toList() as T;
517+
}
518+
if (t == _i1.getType<(String, String)>()) {
519+
return (
520+
deserialize<String>(((data as Map)['p'] as List)[0]),
521+
deserialize<String>(data['p'][1]),
522+
) as T;
523+
}
524+
if (t == _i1.getType<(String, String)>()) {
525+
return (
526+
deserialize<String>(((data as Map)['p'] as List)[0]),
527+
deserialize<String>(data['p'][1]),
528+
) as T;
529+
}
513530
if (t == List<_i49.Project>) {
514531
return (data as List).map((e) => deserialize<_i49.Project>(e)).toList()
515532
as T;
@@ -869,3 +886,75 @@ class Protocol extends _i1.SerializationManager {
869886
return super.deserializeByClassName(data);
870887
}
871888
}
889+
890+
/// Maps any `Record`s known to this [Protocol] to their JSON representation
891+
///
892+
/// Throws in case the record type is not known.
893+
///
894+
/// This method will return `null` (only) for `null` inputs.
895+
Map<String, dynamic>? mapRecordToJson(Record? record) {
896+
if (record == null) {
897+
return null;
898+
}
899+
if (record is (String, String)) {
900+
return {
901+
"p": [
902+
record.$1,
903+
record.$2,
904+
],
905+
};
906+
}
907+
throw Exception('Unsupported record type ${record.runtimeType}');
908+
}
909+
910+
/// Maps container types (like [List], [Map], [Set]) containing
911+
/// [Record]s or non-String-keyed [Map]s to their JSON representation.
912+
///
913+
/// It should not be called for [SerializableModel] types. These
914+
/// handle the "[Record] in container" mapping internally already.
915+
///
916+
/// It is only supposed to be called from generated protocol code.
917+
///
918+
/// Returns either a `List<dynamic>` (for List, Sets, and Maps with
919+
/// non-String keys) or a `Map<String, dynamic>` in case the input was
920+
/// a `Map<String, …>`.
921+
Object? mapContainerToJson(Object obj) {
922+
if (obj is! Iterable && obj is! Map) {
923+
throw ArgumentError.value(
924+
obj,
925+
'obj',
926+
'The object to serialize should be of type List, Map, or Set',
927+
);
928+
}
929+
930+
dynamic mapIfNeeded(Object? obj) {
931+
return switch (obj) {
932+
Record record => mapRecordToJson(record),
933+
Iterable iterable => mapContainerToJson(iterable),
934+
Map map => mapContainerToJson(map),
935+
Object? value => value,
936+
};
937+
}
938+
939+
switch (obj) {
940+
case Map<String, dynamic>():
941+
return {
942+
for (var entry in obj.entries) entry.key: mapIfNeeded(entry.value),
943+
};
944+
case Map():
945+
return [
946+
for (var entry in obj.entries)
947+
{
948+
'k': mapIfNeeded(entry.key),
949+
'v': mapIfNeeded(entry.value),
950+
}
951+
];
952+
953+
case Iterable():
954+
return [
955+
for (var e in obj) mapIfNeeded(e),
956+
];
957+
}
958+
959+
return obj;
960+
}

0 commit comments

Comments
 (0)