Skip to content

Commit

Permalink
APP-3213 - add wrappers for org members/invites (#157)
Browse files Browse the repository at this point in the history
* add wrappers for org members/invites

* add tests

* add resend org invite wrapper and test

* update return types

* add Authorizations to call

* Sentence case the documentation

* add documentation comments for listOrganizationMembers & createOrganizationInvite
  • Loading branch information
clintpurser authored Dec 11, 2023
1 parent 1c61e9b commit 63add0e
Show file tree
Hide file tree
Showing 3 changed files with 145 additions and 2 deletions.
43 changes: 43 additions & 0 deletions lib/src/app/app.dart
Original file line number Diff line number Diff line change
Expand Up @@ -109,4 +109,47 @@ class AppClient {
.map((e) => Permission.values.firstWhere((element) => element.value == e))
.toList();
}

/// List the members and pending invites for an [Organization].
Future<ListOrganizationMembersResponse> listOrganizationMembers(Organization org) async {
final request = ListOrganizationMembersRequest()..organizationId = org.id;
final response = await _client.listOrganizationMembers(request);
return response;
}

/// Send an invitation to to join an [Organization] to the specified email. Grant the level of permission defined in the [ViamAuthorization] object attached.
Future<OrganizationInvite> createOrganizationInvite(Organization org, String email, List<ViamAuthorization> authorizations) async {
final List<Authorization> protoAuthorizations = [];
for (final authorization in authorizations) {
protoAuthorizations.add(authorization.toProto);
}

final request = CreateOrganizationInviteRequest(authorizations: protoAuthorizations)
..organizationId = org.id
..email = email;
final response = await _client.createOrganizationInvite(request);
return response.invite;
}

Future<OrganizationInvite> resendOrganizationInvite(Organization org, String email) async {
final request = ResendOrganizationInviteRequest()
..organizationId = org.id
..email = email;
final response = await _client.resendOrganizationInvite(request);
return response.invite;
}

Future<void> deleteOrganizationInvite(Organization org, String email) async {
final request = DeleteOrganizationInviteRequest()
..organizationId = org.id
..email = email;
await _client.deleteOrganizationInvite(request);
}

Future<void> deleteOrganizationMember(Organization org, String userId) async {
final request = DeleteOrganizationMemberRequest()
..organizationId = org.id
..userId = userId;
await _client.deleteOrganizationMember(request);
}
}
60 changes: 58 additions & 2 deletions lib/src/app/permissions.dart
Original file line number Diff line number Diff line change
@@ -1,17 +1,31 @@
enum Role {
import '../gen/app/v1/app.pbgrpc.dart' as proto;

enum AuthorizationId {
organizationOwner,
organizationOperator,
robotOwner,
robotOperator,
locationOwner,
locationOperator;

String get value {
@override
String toString() {
final exp = RegExp('(?<=[a-z])[A-Z]');
return name.replaceAllMapped(exp, (m) => '_${m.group(0)}').toLowerCase();
}
}

enum IdentityType {
user,
apiKey;

@override
String toString() {
final exp = RegExp('(?<=[a-z])[A-Z]');
return name.replaceAllMapped(exp, (m) => '-${m.group(0)}').toLowerCase();
}
}

enum ResourceType { location, organization, robot }

enum Permission {
Expand All @@ -37,3 +51,45 @@ enum Permission {
return name.replaceAllMapped(exp, (m) => '_${m.group(0)}').toLowerCase();
}
}

class ViamAuthorization {
String authorizationType;

/// The authorization itself, either [AuthorizationId.organizationOwner] or [AuthorizationId.organizationOperator]
AuthorizationId authorizationId;

/// The resource type that the authorization is granted on.
ResourceType resourceType;

/// The id of the resource the authorization is granted on: should be an [Organization] id, [Location] id, or [Robot] id
String resourceId;

/// The id of the identity the authorization is granted to: should be a user id or api key id. Should be empty when granting access to a new user.
String identityId;

/// The id of the [Organization] that the resource belongs to
String organizationId;

/// The type of identity that the authorization is granted to.
IdentityType identityType;

ViamAuthorization({
this.authorizationType = 'role',
required this.authorizationId,
required this.resourceType,
required this.resourceId,
this.identityId = '',
required this.organizationId,
required this.identityType,
});

proto.Authorization get toProto => proto.Authorization(
authorizationType: authorizationType,
authorizationId: authorizationId.toString(),
resourceType: resourceType.name,
resourceId: resourceId,
identityId: identityId,
organizationId: organizationId,
identityType: identityType.toString(),
);
}
44 changes: 44 additions & 0 deletions test/unit_test/app/app_client_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,50 @@ void main() {
expect(response, equals(expected));
});

test('listOrganizationMembers', () async {
final expected = [OrganizationMember()..userId = 'userId'];
when(serviceClient.listOrganizationMembers(any))
.thenAnswer((_) => MockResponseFuture.value(ListOrganizationMembersResponse(members: expected)));
final response = await appClient.listOrganizationMembers(Organization());
expect(response.members, equals(expected));
});

test('createOrganizationInvite', () async {
final expected = OrganizationInvite()
..organizationId = 'organizationId'
..email = 'email'
..createdOn = Timestamp.create();
when(serviceClient.createOrganizationInvite(any))
.thenAnswer((_) => MockResponseFuture.value(CreateOrganizationInviteResponse()..invite = expected));
final response = await appClient.createOrganizationInvite(Organization(), 'email', []);
expect(response, equals(expected));
});

test('resendOrganizationInvite', () async {
final expected = OrganizationInvite()
..organizationId = 'organizationId'
..email = 'email'
..createdOn = Timestamp.create();
when(serviceClient.resendOrganizationInvite(any))
.thenAnswer((_) => MockResponseFuture.value(ResendOrganizationInviteResponse()..invite = expected));
final response = await appClient.resendOrganizationInvite(Organization(), 'email');
expect(response, equals(expected));
});

test('deleteOrganizationInvite', () async {
final expected = DeleteOrganizationInviteResponse();
when(serviceClient.deleteOrganizationInvite(any)).thenAnswer((_) => MockResponseFuture.value(expected));
await appClient.deleteOrganizationInvite(Organization(), 'email');
verify(serviceClient.deleteOrganizationInvite(any)).called(1);
});

test('deleteOrganizationMember', () async {
final expected = DeleteOrganizationMemberResponse();
when(serviceClient.deleteOrganizationMember(any)).thenAnswer((_) => MockResponseFuture.value(expected));
await appClient.deleteOrganizationMember(Organization(), 'user id');
verify(serviceClient.deleteOrganizationMember(any)).called(1);
});

test('tailLogs', () async {
final expected = LogEntry()..message = 'My log entry';
final response = TailRobotPartLogsResponse()..logs.add(expected);
Expand Down

0 comments on commit 63add0e

Please sign in to comment.