Skip to content

Commit

Permalink
Support upserting/querying user-defined attributes of users/groups wi…
Browse files Browse the repository at this point in the history
…th all client SDKs
  • Loading branch information
JamesChenX committed Sep 15, 2024
1 parent 0848fda commit 5fe5806
Show file tree
Hide file tree
Showing 12 changed files with 283 additions and 52 deletions.
34 changes: 32 additions & 2 deletions turms-client-cpp/include/turms/client/service/group_service.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ class GroupService : private boost::noncopyable {
using UserInfo = model::proto::UserInfo;
using UserInfosWithVersion = model::proto::UserInfosWithVersion;

using Value = model::proto::Value;

public:
explicit GroupService(TurmsClient& turmsClient);

Expand Down Expand Up @@ -84,6 +86,14 @@ class GroupService : private boost::noncopyable {
* * If the logged-in user does not have the permission to create the group with typeId,
* throws ResponseException with the code
* ResponseStatusCode::kNoPermissionToCreateGroupWithGroupType.
* @param userDefinedAttributes the user-defined attributes for upsert.
* 1. The attributes must have been defined on the server side via
* `turms.service.group.info.user-defined-attributes.allowed-attributes`. Otherwise, the method
* will throw with ResponseStatusCode::kIllegalArgument if
* `turms.service.group.info.user-defined-attributes.ignore-unknown-attributes-on-upsert` is
* false (false by default), or silently ignored if it is true.
* 2. Only public attributes are supported currently, which means other users can find out these
* attributes via queryGroups().
* @return the group ID.
* @throws ResponseException if an error occurs.
*/
Expand All @@ -92,7 +102,8 @@ class GroupService : private boost::noncopyable {
const boost::optional<absl::string_view>& announcement = boost::none,
const boost::optional<int>& minScore = boost::none,
const boost::optional<time_point>& muteEndDate = boost::none,
const boost::optional<int64_t>& typeId = boost::none)
const boost::optional<int64_t>& typeId = boost::none,
const std::unordered_map<std::string, Value>& userDefinedAttributes = {})
-> boost::future<Response<int64_t>>;

/**
Expand Down Expand Up @@ -206,6 +217,24 @@ class GroupService : private boost::noncopyable {
* Authorization:
* * If the logged-in user is not the owner of the group,
* throws ResponseException with the code ResponseStatusCode::kNotGroupOwnerToTransferGroup.
* @param userDefinedAttributes the user-defined attributes for upsert.
* 1. The attributes must have been defined on the server side via
* `turms.service.group.info.user-defined-attributes.allowed-attributes`. Otherwise, the method
* will throw with ResponseStatusCode::kIllegalArgument if
* `turms.service.group.info.user-defined-attributes.ignore-unknown-attributes-on-upsert` is
* false (false by default), or silently ignored if it is true.
* 2. If trying to update existing immutable attribute, throws with
* ResponseStatusCode::kIllegalArgument.
* 3. Only public attributes are supported currently, which means other users can find out these
* attributes via queryGroups().
*
* Authorization:
* * Whether the logged-in user can change the user-defined attributes depends on the group
* type. If not null and the logged-in user does NOT have the permission to change the group
* name, throws ResponseException with the code
* ResponseStatusCode::kNotGroupMemberToUpdateGroupInfo or
* ResponseStatusCode::kNotGroupOwnerOrManagerToUpdateGroupInfo or
* ResponseStatusCode::kNotGroupOwnerToUpdateGroupInfo.
* @throws ResponseException if an error occurs.
*/
auto updateGroup(int64_t groupId,
Expand All @@ -216,7 +245,8 @@ class GroupService : private boost::noncopyable {
const boost::optional<int64_t>& typeId = boost::none,
const boost::optional<time_point>& muteEndDate = boost::none,
const boost::optional<int64_t>& successorId = boost::none,
const boost::optional<bool>& quitAfterTransfer = boost::none)
const boost::optional<bool>& quitAfterTransfer = boost::none,
const std::unordered_map<std::string, Value>& userDefinedAttributes = {})
-> boost::future<Response<void>>;

/**
Expand Down
23 changes: 18 additions & 5 deletions turms-client-cpp/include/turms/client/service/user_service.h
Original file line number Diff line number Diff line change
Expand Up @@ -189,13 +189,26 @@ class UserService : private boost::noncopyable, private std::enable_shared_from_
* to upload the profile picture and use the returned URL as profilePicture.
* @param profileAccessStrategy the new profile access strategy.
* If null, the profile access strategy will not be updated.
* @param userDefinedAttributes the user-defined attributes for upsert.
* 1. The attributes must have been defined on the server side via
* `turms.service.user.info.user-defined-attributes.allowed-attributes`. Otherwise, the method
* will throw with ResponseStatusCode::kIllegalArgument if
* `turms.service.user.info.user-defined-attributes.ignore-unknown-attributes-on-upsert` is
* false (false by default), or silently ignored if it is true.
* 2. If trying to update existing immutable attribute, throws with
* ResponseStatusCode::kIllegalArgument.
* 3. Only public attributes are supported currently, which means other users can find out these
* attributes via queryUserProfiles().
* @param userDefinedAttributes
* @throws ResponseException if an error occurs.
*/
auto updateProfile(const boost::optional<absl::string_view>& name = boost::none,
const boost::optional<absl::string_view>& intro = boost::none,
const boost::optional<absl::string_view>& profilePicture = boost::none,
const boost::optional<ProfileAccessStrategy>& profileAccessStrategy =
boost::none) -> boost::future<Response<void>>;
auto updateProfile(
const boost::optional<absl::string_view>& name = boost::none,
const boost::optional<absl::string_view>& intro = boost::none,
const boost::optional<absl::string_view>& profilePicture = boost::none,
const boost::optional<ProfileAccessStrategy>& profileAccessStrategy = boost::none,
const std::unordered_map<std::string, Value>& userDefinedAttributes = {})
-> boost::future<Response<void>>;

/**
* Find user profiles.
Expand Down
16 changes: 13 additions & 3 deletions turms-client-cpp/src/turms/client/service/group_service.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ auto GroupService::createGroup(const absl::string_view& name,
const boost::optional<absl::string_view>& announcement,
const boost::optional<int>& minScore,
const boost::optional<time_point>& muteEndDate,
const boost::optional<int64_t>& typeId)
const boost::optional<int64_t>& typeId,
const std::unordered_map<std::string, Value>& userDefinedAttributes)
-> boost::future<Response<int64_t>> {
TurmsRequest turmsRequest;
auto* request = turmsRequest.mutable_create_group_request();
Expand All @@ -34,6 +35,10 @@ auto GroupService::createGroup(const absl::string_view& name,
if (typeId) {
request->set_type_id(*typeId);
}
if (!userDefinedAttributes.empty()) {
request->mutable_user_defined_attributes()()->insert(userDefinedAttributes.cbegin(),
userDefinedAttributes.cend());
}
return turmsClient_.driver()
.send(turmsRequest)
.then([](boost::future<TurmsNotification> response) {
Expand Down Expand Up @@ -62,10 +67,11 @@ auto GroupService::updateGroup(int64_t groupId,
const boost::optional<int64_t>& typeId,
const boost::optional<time_point>& muteEndDate,
const boost::optional<int64_t>& successorId,
const boost::optional<bool>& quitAfterTransfer)
const boost::optional<bool>& quitAfterTransfer,
const std::unordered_map<std::string, Value>& userDefinedAttributes)
-> boost::future<Response<void>> {
if (!name && !intro && !announcement && !minScore && !typeId && !muteEndDate && !successorId &&
!quitAfterTransfer) {
userDefinedAttributes.empty()) {
return boost::make_ready_future<>(Response<void>{});
}
TurmsRequest turmsRequest;
Expand Down Expand Up @@ -95,6 +101,10 @@ auto GroupService::updateGroup(int64_t groupId,
if (quitAfterTransfer) {
request->set_quit_after_transfer(*quitAfterTransfer);
}
if (!userDefinedAttributes.empty()) {
request->mutable_user_defined_attributes()()->insert(userDefinedAttributes.cbegin(),
userDefinedAttributes.cend());
}
return turmsClient_.driver()
.send(turmsRequest)
.then([](boost::future<TurmsNotification> response) {
Expand Down
10 changes: 8 additions & 2 deletions turms-client-cpp/src/turms/client/service/user_service.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -211,9 +211,11 @@ auto UserService::updatePassword(const std::string& password) -> boost::future<R
auto UserService::updateProfile(const boost::optional<absl::string_view>& name,
const boost::optional<absl::string_view>& intro,
const boost::optional<absl::string_view>& profilePicture,
const boost::optional<ProfileAccessStrategy>& profileAccessStrategy)
const boost::optional<ProfileAccessStrategy>& profileAccessStrategy,
const std::unordered_map<std::string, Value>& userDefinedAttributes)
-> boost::future<Response<void>> {
if (!name && !intro && !profileAccessStrategy) {
if (!name && !intro && !profilePicture && !profileAccessStrategy &&
userDefinedAttributes.empty()) {
return boost::make_ready_future<>(Response<void>{});
}
TurmsRequest turmsRequest;
Expand All @@ -230,6 +232,10 @@ auto UserService::updateProfile(const boost::optional<absl::string_view>& name,
if (profileAccessStrategy) {
request->set_profile_access_strategy(*profileAccessStrategy);
}
if (!userDefinedAttributes.empty()) {
request->mutable_user_defined_attributes()->insert(userDefinedAttributes.cbegin(),
userDefinedAttributes.cend());
}
return turmsClient_.driver()
.send(turmsRequest)
.then([](boost::future<TurmsNotification> response) {
Expand Down
48 changes: 40 additions & 8 deletions turms-client-dart/lib/src/service/group_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ import 'package:fixnum/fixnum.dart' show Int64;

import '../../turms_client.dart';
import '../extension/notification_extensions.dart';
import '../model/proto/request/group/enrollment/update_group_invitation_request.pb.dart';
import '../model/proto/request/group/enrollment/update_group_join_request_request.pb.dart';

class GroupService {
final TurmsClient _turmsClient;
Expand Down Expand Up @@ -38,6 +36,13 @@ class GroupService {
/// throws [ResponseException] with the code [ResponseStatusCode.createGroupWithNonexistentGroupType].
/// * If the logged-in user does not have the permission to create the group with [typeId],
/// throws [ResponseException] with the code [ResponseStatusCode.noPermissionToCreateGroupWithGroupType].
/// * `userDefinedAttributes`: The user-defined attributes for upsert.
/// 1. The attributes must have been defined on the server side via `turms.service.group.info.user-defined-attributes.allowed-attributes`.
/// Otherwise, the method will throw with [ResponseStatusCode.illegalArgument]
/// if `turms.service.group.info.user-defined-attributes.ignore-unknown-attributes-on-upsert` is false (false by default),
/// or silently ignored if it is true.
/// 2. Only public attributes are supported currently, which means other users can find out these attributes
/// via [queryGroups].
///
/// **Returns**: The group ID.
///
Expand All @@ -47,14 +52,16 @@ class GroupService {
String? announcement,
int? minScore,
DateTime? muteEndDate,
Int64? typeId}) async {
Int64? typeId,
Map<String, Value>? userDefinedAttributes}) async {
final n = await _turmsClient.driver.send(CreateGroupRequest(
name: name,
intro: intro,
announcement: announcement,
minScore: minScore,
muteEndDate: muteEndDate?.toInt64(),
typeId: typeId));
typeId: typeId,
userDefinedAttributes: userDefinedAttributes));
return n.toResponse((data) => data.getLongOrThrow());
}

Expand Down Expand Up @@ -161,6 +168,21 @@ class GroupService {
/// Authorization:
/// * If the logged-in user is not the owner of the group,
/// throws [ResponseException] with the code [ResponseStatusCode.notGroupOwnerToTransferGroup].
/// * `userDefinedAttributes`: The user-defined attributes for upsert.
/// 1. The attributes must have been defined on the server side via `turms.service.group.info.user-defined-attributes.allowed-attributes`.
/// Otherwise, the method will throw with [ResponseStatusCode.illegalArgument]
/// if `turms.service.group.info.user-defined-attributes.ignore-unknown-attributes-on-upsert` is false (false by default),
/// or silently ignored if it is true.
/// 2. If trying to update existing immutable attribute, throws with [ResponseStatusCode.illegalArgument].
/// 3. Only public attributes are supported currently, which means other users can find out these attributes
/// via [queryGroups].
///
/// Authorization:
/// * Whether the logged-in user can change the user-defined attributes depends on the group type.
/// If not null and the logged-in user does NOT have the permission to change the group name,
/// throws [ResponseException] with the code [ResponseStatusCode.notGroupMemberToUpdateGroupInfo]
/// or [ResponseStatusCode.notGroupOwnerOrManagerToUpdateGroupInfo]
/// or [ResponseStatusCode.notGroupOwnerToUpdateGroupInfo].
///
/// **Throws**: [ResponseException] if an error occurs.
Future<Response<void>> updateGroup(Int64 groupId,
Expand All @@ -171,9 +193,18 @@ class GroupService {
Int64? typeId,
DateTime? muteEndDate,
Int64? successorId,
bool? quitAfterTransfer}) async {
if ([name, intro, announcement, minScore, typeId, muteEndDate, successorId]
.areAllNull) {
bool? quitAfterTransfer,
Map<String, Value>? userDefinedAttributes}) async {
if ([
name,
intro,
announcement,
minScore,
typeId,
muteEndDate,
successorId,
userDefinedAttributes
].areAllNullOrEmpty) {
return Response.nullValue();
}
final n = await _turmsClient.driver.send(UpdateGroupRequest(
Expand All @@ -185,7 +216,8 @@ class GroupService {
minScore: minScore,
typeId: typeId,
successorId: successorId,
quitAfterTransfer: quitAfterTransfer));
quitAfterTransfer: quitAfterTransfer,
userDefinedAttributes: userDefinedAttributes));
return n.toNullResponse();
}

Expand Down
35 changes: 23 additions & 12 deletions turms-client-dart/lib/src/service/user_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,6 @@ import 'package:fixnum/fixnum.dart';

import '../../turms_client.dart';
import '../extension/notification_extensions.dart';
import '../model/proto/model/common/value.pb.dart';
import '../model/proto/model/user/user_settings.pb.dart';
import '../model/proto/request/user/delete_user_settings_request.pb.dart';
import '../model/proto/request/user/query_user_settings_request.pb.dart';
import '../model/proto/request/user/relationship/delete_friend_request_request.pb.dart';
import '../model/proto/request/user/update_user_settings_request.pb.dart';
import '../util/system.dart';

class Location {
Expand Down Expand Up @@ -237,21 +231,38 @@ class UserService {
/// to upload the profile picture and use the returned URL as [profilePicture].
/// * `profileAccessStrategy`: The new profile access strategy.
/// If null, the profile access strategy will not be updated.
/// * `userDefinedAttributes`: The user-defined attributes for upsert.
/// 1. The attributes must have been defined on the server side via `turms.service.user.info.user-defined-attributes.allowed-attributes`.
/// Otherwise, the method will throw with [ResponseStatusCode.illegalArgument]
/// if `turms.service.user.info.user-defined-attributes.ignore-unknown-attributes-on-upsert` is false (false by default),
/// or silently ignored if it is true.
/// 2. If trying to update existing immutable attribute, throws with [ResponseStatusCode.illegalArgument].
/// 3. Only public attributes are supported currently, which means other users can find out these attributes
/// via [queryUserProfiles].
///
/// **Throws**: [ResponseException] if an error occurs.
Future<Response<void>> updateProfile(
{String? name,
String? intro,
String? profilePicture,
ProfileAccessStrategy? profileAccessStrategy}) async {
if ([name, intro, profilePicture, profileAccessStrategy].areAllNull) {
ProfileAccessStrategy? profileAccessStrategy,
Map<String, Value>? userDefinedAttributes}) async {
if ([
name,
intro,
profilePicture,
profileAccessStrategy,
userDefinedAttributes
].areAllNullOrEmpty) {
return Response.nullValue();
}
final n = await _turmsClient.driver.send(UpdateUserRequest(
name: name,
intro: intro,
profilePicture: profilePicture,
profileAccessStrategy: profileAccessStrategy));
name: name,
intro: intro,
profilePicture: profilePicture,
profileAccessStrategy: profileAccessStrategy,
userDefinedAttributes: userDefinedAttributes,
));
return n.toNullResponse();
}

Expand Down
Loading

0 comments on commit 5fe5806

Please sign in to comment.