1 ////////////////////////////////////////////////////////////////////////////
3 // Copyright 2017 Realm Inc.
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
9 // http://www.apache.org/licenses/LICENSE-2.0
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
17 ////////////////////////////////////////////////////////////////////////////
19 #import "RLMSyncPermissionResults.h"
21 #import "RLMCollection_Private.hpp"
22 #import "RLMObjectSchema_Private.hpp"
23 #import "RLMQueryUtil.hpp"
24 #import "RLMResults_Private.hpp"
25 #import "RLMSchema_Private.hpp"
26 #import "RLMSyncPermission_Private.hpp"
27 #import "RLMSyncUtil_Private.hpp"
32 using namespace realm;
36 bool keypath_is_valid(NSString *keypath)
38 static NSSet<NSString *> *valid = nil;
39 static dispatch_once_t onceToken;
40 dispatch_once(&onceToken, ^{
41 valid = [NSSet setWithArray:@[RLMSyncPermissionSortPropertyPath,
42 RLMSyncPermissionSortPropertyUserID,
43 RLMSyncPermissionSortPropertyUpdated]];
45 return [valid containsObject:keypath];
50 /// Sort by the Realm Object Server path to the Realm to which the permission applies.
51 RLMSyncPermissionSortProperty const RLMSyncPermissionSortPropertyPath = @"path";
52 /// Sort by the identity of the user to whom the permission applies.
53 RLMSyncPermissionSortProperty const RLMSyncPermissionSortPropertyUserID = @"userId";
54 /// Sort by the date the permissions were last updated.
55 RLMSyncPermissionSortProperty const RLMSyncPermissionSortPropertyUpdated = @"updatedAt";
57 @interface RLMSyncPermissionResults ()
58 @property (nonatomic, strong) RLMSchema *schema;
59 @property (nonatomic, strong) RLMObjectSchema *objectSchema;
62 @implementation RLMSyncPermissionResults
64 #pragma mark - Public API
66 - (RLMPropertyType)type {
67 return RLMPropertyTypeObject;
70 - (NSString *)objectClassName {
71 return NSStringFromClass([RLMSyncPermission class]);
78 - (RLMSyncPermission *)objectAtIndex:(NSUInteger)index {
79 return translateRLMResultsErrors([&] {
80 Object permission(_results.get_realm(), _results.get_object_schema(), _results.get(index));
81 return [[RLMSyncPermission alloc] initWithPermission:Permission(permission)];
85 - (RLMSyncPermission *)firstObject {
86 return self.count == 0 ? nil : [self objectAtIndex:0];
89 - (RLMSyncPermission *)lastObject {
90 return self.count == 0 ? nil : [self objectAtIndex:(self.count - 1)];
93 - (NSUInteger)indexOfObject:(RLMSyncPermission *)object {
95 // Key-value permissions are only used for setting; they are never returned.
98 // Canonicalize the path.
99 NSString *path = object.path;
100 if ([path rangeOfString:@"~"].location != NSNotFound) {
101 path = [path stringByReplacingOccurrencesOfString:@"~" withString:object.identity];
103 NSString *topPrivilege;
104 switch (object.accessLevel) {
105 case RLMSyncAccessLevelNone:
106 // Deleted permissions are removed from the permissions Realm by ROS.
108 case RLMSyncAccessLevelRead:
109 topPrivilege = @"mayRead";
111 case RLMSyncAccessLevelWrite:
112 topPrivilege = @"mayWrite";
114 case RLMSyncAccessLevelAdmin:
115 topPrivilege = @"mayManage";
118 // Build the predicate.
119 NSPredicate *p = [NSPredicate predicateWithFormat:@"%K = %@ AND %K = %@ AND %K == YES",
120 RLMSyncPermissionSortPropertyPath, path,
121 RLMSyncPermissionSortPropertyUserID, object.identity,
123 return [self indexOfObjectWithPredicate:p];
126 - (NSUInteger)indexOfObjectWithPredicate:(NSPredicate *)predicate {
127 return translateRLMResultsErrors([&] {
128 auto& group = _results.get_realm()->read_group();
129 auto query = RLMPredicateToQuery(predicate, self.objectSchema, self.schema, group);
130 return RLMConvertNotFound(_results.index_of(std::move(query)));
134 - (RLMResults<RLMSyncPermission *> *)objectsWithPredicate:(NSPredicate *)predicate {
135 return translateRLMResultsErrors([&] {
136 auto query = RLMPredicateToQuery(predicate, self.objectSchema, self.schema, _results.get_realm()->read_group());
137 return [[RLMSyncPermissionResults alloc] initWithResults:_results.filter(std::move(query))];
141 - (RLMResults<RLMSyncPermission *> *)sortedResultsUsingDescriptors:(NSArray<RLMSortDescriptor *> *)properties {
142 if (properties.count == 0) {
145 for (RLMSortDescriptor *descriptor in properties) {
146 if (!keypath_is_valid(descriptor.keyPath)) {
147 @throw RLMException(@"Invalid keypath specified. Use one of the constants defined in "
148 @" `RLMSyncPermissionSortProperty`.");
151 return translateRLMResultsErrors([&] {
152 auto sorted = _results.sort(RLMSortDescriptorsToKeypathArray(properties));
153 return [[RLMSyncPermissionResults alloc] initWithResults:std::move(sorted)];
157 #pragma clang diagnostic push
158 #pragma clang diagnostic ignored "-Wmismatched-parameter-types"
159 - (RLMNotificationToken *)addNotificationBlock:(void(^)(RLMSyncPermissionResults *results,
160 RLMCollectionChange *change,
161 NSError *error))block {
162 auto cb = [=](const realm::CollectionChangeSet& changes, std::exception_ptr ptr) {
164 NSError *error = translateSyncExceptionPtrToError(std::move(ptr), RLMPermissionActionTypeGet);
166 block(nil, nil, error);
168 // Finished successfully
169 block(self, [[RLMCollectionChange alloc] initWithChanges:changes], nil);
172 return [[RLMCancellationToken alloc] initWithToken:_results.add_notification_callback(std::move(cb)) realm:nil];
174 #pragma clang diagnostic pop
176 - (id)aggregate:(__unused NSString *)property
177 method:(__unused util::Optional<Mixed> (Results::*)(size_t))method
178 methodName:(__unused NSString *)methodName returnNilForEmpty:(__unused BOOL)returnNilForEmpty {
179 // We don't support any of the min/max/average/sum APIs; they don't make sense for this collection type.
183 - (id)valueForKey:(NSString *)key {
184 size_t count = self.count;
188 NSMutableArray *results = [NSMutableArray arrayWithCapacity:count];
189 if ([key isEqualToString:@"self"]) {
190 for (size_t i = 0; i < count; i++) {
191 [results addObject:[self objectAtIndex:i]];
194 for (size_t i = 0; i < count; i++) {
195 [results addObject:[[self objectAtIndex:i] valueForKey:key] ?: NSNull.null];
201 - (void)setValue:(__unused id)value forKey:(__unused NSString *)key {
202 @throw RLMException(@"Cannot set values for the read-only type `RLMSyncPermission`.");
205 #pragma mark - System
207 - (RLMSchema *)schema {
209 _schema = [RLMSchema dynamicSchemaFromObjectStoreSchema:_results.get_realm()->schema()];
214 - (RLMObjectSchema *)objectSchema {
215 if (!_objectSchema) {
216 _objectSchema = [RLMObjectSchema objectSchemaForObjectStoreSchema:_results.get_object_schema()];
218 return _objectSchema;
221 - (NSString *)description {
222 return RLMDescriptionWithMaxDepth(@"RLMSyncPermissionResults", self, 1);
225 - (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state
226 objects:(id __unsafe_unretained [])buffer
227 count:(NSUInteger)len {
228 // FIXME: It would be nice to have a shared fast enumeration implementation for `realm::Results`-only RLMResults.
229 NSUInteger thisSize = self.count;
230 if (state->state == 0) {
232 state->extra[1] = (long)thisSize;
235 NSUInteger objectsInBuffer = 0;
236 long idx = state->extra[0];
237 if ((unsigned long)idx == thisSize) {
241 state->itemsPtr = buffer;
242 state->mutationsPtr = state->extra + 1;
244 if (objectsInBuffer == len) {
246 state->extra[0] = idx;
247 return objectsInBuffer;
249 if ((unsigned long)idx == thisSize) {
251 state->extra[0] = idx;
252 return objectsInBuffer;
254 // Otherwise, add an object and advance the index pointer.
255 RLMSyncPermission * __autoreleasing thisPermission = [self objectAtIndex:idx];
256 buffer[objectsInBuffer] = thisPermission;