-
Notifications
You must be signed in to change notification settings - Fork 163
/
Copy pathBoxMetadataManager.cs
446 lines (390 loc) · 23.7 KB
/
BoxMetadataManager.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
using System;
using System.Collections.Generic;
using System.Net;
using System.Threading.Tasks;
using Box.V2.Auth;
using Box.V2.Config;
using Box.V2.Converter;
using Box.V2.Exceptions;
using Box.V2.Extensions;
using Box.V2.Models;
using Box.V2.Models.Request;
using Box.V2.Services;
using Newtonsoft.Json.Linq;
namespace Box.V2.Managers
{
/// <summary>
/// Metadata allows users and applications to define and store custom data associated with their files/folders
/// </summary>
public class BoxMetadataManager : BoxResourceManager, IBoxMetadataManager
{
public BoxMetadataManager(IBoxConfig config, IBoxService service, IBoxConverter converter, IAuthRepository auth, string asUser = null, bool? suppressNotifications = null)
: base(config, service, converter, auth, asUser, suppressNotifications) { }
/// <summary>
/// Used to retrieve the metadata template instance for a corresponding Box file.
/// </summary>
/// <param name="fileId">Id of file</param>
/// <param name="scope">Scope name. Currently, the only scopes supported are enterprise and global</param>
/// <param name="template">Metadata template name</param>
/// <returns>A Dictionary of key:value pairs representing the metadata.</returns>
public async Task<Dictionary<string, object>> GetFileMetadataAsync(string fileId, string scope, string template)
{
return await GetMetadata(_config.FilesEndpointUri, fileId, scope, template).ConfigureAwait(false);
}
/// <summary>
/// Used to retrieve the metadata template instance for a corresponding Box folder.
/// </summary>
/// <param name="folderId">Id of folder</param>
/// <param name="scope">Scope name. Currently, only the enterprise scope is supported</param>
/// <param name="template">Metadata template name</param>
/// <returns>A Dictionary of key:value pairs representing the metadata.</returns>
public async Task<Dictionary<string, object>> GetFolderMetadataAsync(string folderId, string scope, string template)
{
return await GetMetadata(_config.FoldersEndpointUri, folderId, scope, template).ConfigureAwait(false);
}
/// <summary>
/// Used to create the metadata template instance for a corresponding Box file. When creating metadata, only values that adhere to the metadata template schema will be accepted.
/// </summary>
/// <param name="fileId">Id of file</param>
/// <param name="metadata">Metadata to create</param>
/// <param name="scope">Scope name. Currently, the only scopes support are enterprise and global</param>
/// <param name="template">Metadata template name</param>
/// <returns>A Dictionary of key:value pairs representing the metadata.</returns>
public async Task<Dictionary<string, object>> CreateFileMetadataAsync(string fileId, Dictionary<string, object> metadata, string scope, string template)
{
return await CreateMetadata(_config.FilesEndpointUri, fileId, metadata, scope, template).ConfigureAwait(false);
}
/// <summary>
/// Used to create the metadata template instance for a corresponding Box folder. When creating metadata, only values that adhere to the metadata template schema will be accepted.
/// </summary>
/// <param name="folderId">Id of folder</param>
/// <param name="metadata">Metadata to create</param>
/// <param name="scope">Scope name. Currently, only the enterprise scope is supported</param>
/// <param name="template">Metadata template name</param>
/// <returns>A Dictionary of key:value pairs representing the metadata.</returns>
public async Task<Dictionary<string, object>> CreateFolderMetadataAsync(string folderId, Dictionary<string, object> metadata, string scope, string template)
{
return await CreateMetadata(_config.FoldersEndpointUri, folderId, metadata, scope, template).ConfigureAwait(false);
}
/// <summary>
/// Used to update the template instance. The request body must follow the JSON-Patch specification, which is represented as a JSON array of operation objects (see examples for more details). Updates can be either add, replace, remove , test, move, or copy. The template instance can only be updated if the template instance already exists. When editing metadata, only values that adhere to the metadata template schema will be accepted. The update is applied atomically. If any errors occur during the application of the update operations, the metadata instance remains unchanged.
/// </summary>
/// <param name="fileId">Id of file</param>
/// <param name="updates">Metadata updates to apply</param>
/// <param name="scope">Scope name. Currently, the only scopes support are enterprise and global</param>
/// <param name="template">Metadata template name</param>
/// <returns>A Dictionary of key:value pairs representing the metadata.</returns>
public async Task<Dictionary<string, object>> UpdateFileMetadataAsync(string fileId, List<BoxMetadataUpdate> updates, string scope, string template)
{
return await UpdateMetadata(_config.FilesEndpointUri, fileId, updates, scope, template).ConfigureAwait(false);
}
/// <summary>
/// Used to update the template instance. Updates can be either add, replace, remove , or test. The template instance can only be updated if the template instance already exists. When editing metadata, only values that adhere to the metadata template schema will be accepted.
/// </summary>
/// <param name="folderId">Id of folder</param>
/// <param name="updates">Metadata updates to apply</param>
/// <param name="scope">Scope name. Currently, only the enterprise scope is supported</param>
/// <param name="template">Metadata template name</param>
/// <returns>A Dictionary of key:value pairs representing the metadata.</returns>
public async Task<Dictionary<string, object>> UpdateFolderMetadataAsync(string folderId, List<BoxMetadataUpdate> updates, string scope, string template)
{
return await UpdateMetadata(_config.FoldersEndpointUri, folderId, updates, scope, template).ConfigureAwait(false);
}
/// <summary>
/// Sets the provided metadata, overwriting any existing metadata on the file.
/// </summary>
/// <param name="fileId">The ID of the file to write metadata on.</param>
/// <param name="metadata">The metadata key/value pairs to write.</param>
/// <param name="scope">The scope of the metadata template to write to.</param>
/// <param name="template">The key of the metadata template to write to.</param>
/// <returns>The full metadata on the file, after writes are applied.</returns>
public async Task<Dictionary<string, object>> SetFileMetadataAsync(string fileId, Dictionary<string, object> metadata, string scope, string template)
{
try
{
return await CreateFileMetadataAsync(fileId, metadata, scope, template).ConfigureAwait(false);
}
catch (BoxAPIException ex)
{
if (ex.StatusCode == HttpStatusCode.Conflict)
{
// Metadata already exists, try updating instead
var updates = new List<BoxMetadataUpdate>();
foreach (KeyValuePair<string, object> md in metadata)
{
updates.Add(new BoxMetadataUpdate()
{
Op = MetadataUpdateOp.add,
Path = "/" + md.Key,
Value = md.Value,
});
}
return await UpdateFileMetadataAsync(fileId, updates, scope, template).ConfigureAwait(false);
}
// Some other exception, just rethrow it
throw ex;
}
}
/// <summary>
/// Sets the provided metadata, overwriting any existing metadata on the folder.
/// </summary>
/// <param name="folderId">The ID of the folder to write metadata on.</param>
/// <param name="metadata">The metadata key/value pairs to write.</param>
/// <param name="scope">The scope of the metadata template to write to.</param>
/// <param name="template">The key of the metadata template to write to.</param>
/// <returns>The full metadata on the folder, after writes are applied.</returns>
public async Task<Dictionary<string, object>> SetFolderMetadataAsync(string folderId, Dictionary<string, object> metadata, string scope, string template)
{
try
{
return await CreateFolderMetadataAsync(folderId, metadata, scope, template).ConfigureAwait(false);
}
catch (BoxAPIException ex)
{
if (ex.StatusCode == HttpStatusCode.Conflict)
{
// Metadata already exists, try updating instead
var updates = new List<BoxMetadataUpdate>();
foreach (KeyValuePair<string, object> md in metadata)
{
updates.Add(new BoxMetadataUpdate()
{
Op = MetadataUpdateOp.add,
Path = "/" + md.Key,
Value = md.Value,
});
}
return await UpdateFolderMetadataAsync(folderId, updates, scope, template).ConfigureAwait(false);
}
// Some other exception, just rethrow it
throw ex;
}
}
/// <summary>
/// Used to delete the template instance. To delete custom key:value pairs within a template instance, you should refer to the updating metadata section.
/// </summary>
/// <param name="fileId">Id of file</param>
/// <param name="scope">Scope name. Currently, the only scopes support are enterprise and global</param>
/// <param name="template">Metadata template name</param>
/// <returns>True if successful, false otherwise.</returns>
public async Task<bool> DeleteFileMetadataAsync(string fileId, string scope, string template)
{
return await DeleteMetadata(_config.FilesEndpointUri, fileId, scope, template).ConfigureAwait(false);
}
/// <summary>
/// Used to delete the template instance. To delete custom key:value pairs within a template instance, you should refer to the updating metadata section.
/// </summary>
/// <param name="folderId">Id of folder</param>
/// <param name="scope">Scope name. Currently, only the enterprise scope is supported</param>
/// <param name="template">Metadata template name</param>
/// <returns>True if successful, false otherwise.</returns>
public async Task<bool> DeleteFolderMetadataAsync(string folderId, string scope, string template)
{
return await DeleteMetadata(_config.FoldersEndpointUri, folderId, scope, template).ConfigureAwait(false);
}
/// <summary>
/// Used to retrieve the schema for a given metadata template.
/// </summary>
/// <param name="scope">Scope name. Currently, the only scopes supported are enterprise and global</param>
/// <param name="template">Metadata template name</param>
/// <returns>Returns the schema for the specified metadata template.</returns>
public async Task<BoxMetadataTemplate> GetMetadataTemplate(string scope, string template)
{
var request = new BoxRequest(_config.MetadataTemplatesUri, string.Format(Constants.MetadataTemplatesPathString, scope, template));
IBoxResponse<BoxMetadataTemplate> response = await ToResponseAsync<BoxMetadataTemplate>(request).ConfigureAwait(false);
return response.ResponseObject;
}
/// <summary>
/// Used to create a new metadata template with the specified schema.
/// </summary>
/// <param name="template">BoxMetadataTemplate object</param>
/// <returns>The schema representing the metadata template created.</returns>
public async Task<BoxMetadataTemplate> CreateMetadataTemplate(BoxMetadataTemplate template)
{
BoxRequest request = new BoxRequest(_config.CreateMetadataTemplateUri)
.Method(RequestMethod.Post)
.Payload(_converter.Serialize(template));
request.ContentType = Constants.RequestParameters.ContentTypeJson;
IBoxResponse<BoxMetadataTemplate> response = await ToResponseAsync<BoxMetadataTemplate>(request).ConfigureAwait(false);
return response.ResponseObject;
}
/// <summary>
/// Used to delete an existing metadata template with the specified schema.
/// </summary>
/// <param name="scope">Scope name. Currently, the only scopes supported are enterprise and global</param>
/// <param name="template">Metadata template name</param>
/// <returns></returns>Returns boolean true if metadata schema was deleted successfully.
public async Task<bool> DeleteMetadataTemplate(string scope, string template)
{
BoxRequest request = new BoxRequest(_config.MetadataTemplatesUri, string.Format(Constants.MetadataTemplatesPathString, scope, template))
.Method(RequestMethod.Delete);
IBoxResponse<Dictionary<string, object>> response = await ToResponseAsync<Dictionary<string, object>>(request).ConfigureAwait(false);
return response.Status == ResponseStatus.Success;
}
/// <summary>
/// Used to update the schema of an existing template.
/// </summary>
/// <param name="metadataTemplateUpdate">BoxMetadataTemplateUpdate object</param>
/// <param name="scope">Scope name. Currently, the only scopes supported are enterprise and global</param>
/// <param name="template">Metadata template name</param>
/// <returns></returns>
public async Task<BoxMetadataTemplate> UpdateMetadataTemplate(IEnumerable<BoxMetadataTemplateUpdate> metadataTemplateUpdate, string scope, string template)
{
BoxRequest request = new BoxRequest(_config.MetadataTemplatesUri, string.Format(Constants.MetadataTemplatesPathString, scope, template))
.Method(RequestMethod.Put)
.Payload(_converter.Serialize(metadataTemplateUpdate));
IBoxResponse<BoxMetadataTemplate> response = await ToResponseAsync<BoxMetadataTemplate>(request).ConfigureAwait(false);
return response.ResponseObject;
}
/// <summary>
/// Used to retrieve the schema for a given metadata template by metadata template id.
/// </summary>
/// <param name="templateId">Metadata template id.</param>
/// <returns>Returns the schema for the specified metadata template.</returns>
public async Task<BoxMetadataTemplate> GetMetadataTemplateById(string templateId)
{
var request = new BoxRequest(_config.MetadataTemplatesUri, templateId);
IBoxResponse<BoxMetadataTemplate> response = await ToResponseAsync<BoxMetadataTemplate>(request).ConfigureAwait(false);
return response.ResponseObject;
}
/// <summary>
/// Used to retrieve all metadata associated with a given file
/// </summary>
/// <param name="fileId">Id of file</param>
/// <returns>Collection of metadata instances associated with the file.</returns>
public async Task<BoxMetadataTemplateCollection<Dictionary<string, object>>> GetAllFileMetadataTemplatesAsync(string fileId)
{
var request = new BoxRequest(_config.FilesEndpointUri, string.Format(Constants.AllFileMetadataPathString, fileId));
IBoxResponse<BoxMetadataTemplateCollection<Dictionary<string, object>>> response = await ToResponseAsync<BoxMetadataTemplateCollection<Dictionary<string, object>>>(request).ConfigureAwait(false);
return response.ResponseObject;
}
/// <summary>
/// Used to retrieve all metadata associated with a given folder
/// </summary>
/// <param name="folderId">Id of folder</param>
/// <returns>Collection of metadata instances associated with the file.</returns>
public async Task<BoxMetadataTemplateCollection<Dictionary<string, object>>> GetAllFolderMetadataTemplatesAsync(string folderId)
{
var request = new BoxRequest(_config.FoldersEndpointUri, string.Format(Constants.AllFolderMetadataPathString, folderId));
IBoxResponse<BoxMetadataTemplateCollection<Dictionary<string, object>>> response = await ToResponseAsync<BoxMetadataTemplateCollection<Dictionary<string, object>>>(request).ConfigureAwait(false);
return response.ResponseObject;
}
/// <summary>
/// Used to retrieve all metadata templates within a user's enterprise. Currently only the enterprise scope is supported.
/// </summary>
/// <param name="scope">Scope name. Currently, the only scopes support are enterprise and global</param>
/// <returns>Collection of enterprise metadata instances associated with the file.</returns>
public async Task<BoxEnterpriseMetadataTemplateCollection<BoxMetadataTemplate>> GetEnterpriseMetadataAsync(string scope = "enterprise")
{
var request = new BoxRequest(_config.MetadataTemplatesUri, string.Format(Constants.EnterpriseMetadataTemplatesPathString, scope));
IBoxResponse<BoxEnterpriseMetadataTemplateCollection<BoxMetadataTemplate>> response = await ToResponseAsync<BoxEnterpriseMetadataTemplateCollection<BoxMetadataTemplate>>(request).ConfigureAwait(false);
return response.ResponseObject;
}
/// <summary>
/// Allows you to query by metadata on Box items with fields passed in
/// </summary>
/// <param name="queryRequest">Request object.</param>
/// <returns>A collection of items and their associated metadata</returns>
public async Task<BoxCollectionMarkerBased<BoxItem>> ExecuteMetadataQueryAsync(BoxMetadataQueryRequest queryRequest)
{
queryRequest.From.ThrowIfNullOrWhiteSpace("from");
queryRequest.AncestorFolderId.ThrowIfNullOrWhiteSpace("ancestorFolderId");
JObject bodyObject = GetMetadataQueryBody(queryRequest.From, queryRequest.AncestorFolderId, queryRequest.Query, queryRequest.QueryParameters,
queryRequest.OrderBy, queryRequest.Fields, queryRequest.Limit, queryRequest.Marker);
BoxRequest request = new BoxRequest(_config.MetadataQueryUri)
.Method(RequestMethod.Post)
.Payload(_converter.Serialize(bodyObject));
request.ContentType = Constants.RequestParameters.ContentTypeJson;
if (queryRequest.AutoPaginate)
{
return await AutoPaginateMarkerMetadataQueryV2<BoxItem>(request).ConfigureAwait(false);
}
else
{
IBoxResponse<BoxCollectionMarkerBased<BoxItem>> response = await ToResponseAsync<BoxCollectionMarkerBased<BoxItem>>(request).ConfigureAwait(false);
return response.ResponseObject;
}
}
//************************************
//Private methods
//************************************
private JObject GetMetadataQueryBody(string from, string ancestorFolderId, string query = null, Dictionary<string, object> queryParameters = null, List<BoxMetadataQueryOrderBy> orderBy = null, IEnumerable<string> fields = null, int limit = 100, string marker = null)
{
dynamic bodyObject = new JObject();
bodyObject.from = from;
bodyObject.ancestor_folder_id = ancestorFolderId;
bodyObject.limit = limit;
if (query != null)
{
bodyObject.query = query;
}
if (queryParameters != null)
{
bodyObject.query_params = JObject.FromObject(queryParameters);
}
if (orderBy != null)
{
var orderByList = new List<JObject>();
foreach (var order in orderBy)
{
dynamic orderByObject = new JObject();
orderByObject.field_key = order.FieldKey;
orderByObject.direction = order.Direction.ToString();
orderByList.Add(orderByObject);
}
bodyObject.order_by = JArray.FromObject(orderByList);
}
if (fields != null)
{
var fieldArray = new JArray();
foreach (var field in fields)
{
fieldArray.Add(field);
}
bodyObject.fields = fieldArray;
}
if (marker != null)
{
bodyObject.marker = marker;
}
return bodyObject;
}
private async Task<Dictionary<string, object>> UpdateMetadata(Uri hostUri, string id, List<BoxMetadataUpdate> updates, string scope, string template)
{
foreach (BoxMetadataUpdate update in updates)
{
update.Path.ThrowIfNullOrWhiteSpace("Path");
update.Op.ThrowIfNull("Op");
}
BoxRequest request = new BoxRequest(hostUri, string.Format(Constants.MetadataPathString, id, scope, template))
.Method(RequestMethod.Put)
.Payload(_converter.Serialize(updates));
request.ContentType = Constants.RequestParameters.ContentTypeJsonPatch;
IBoxResponse<Dictionary<string, object>> response = await ToResponseAsync<Dictionary<string, object>>(request).ConfigureAwait(false);
return response.ResponseObject;
}
private async Task<Dictionary<string, object>> CreateMetadata(Uri hostUri, string id, Dictionary<string, object> metadata, string scope, string template)
{
BoxRequest request = new BoxRequest(hostUri, string.Format(Constants.MetadataPathString, id, scope, template))
.Method(RequestMethod.Post)
.Payload(_converter.Serialize(metadata));
request.ContentType = Constants.RequestParameters.ContentTypeJson;
IBoxResponse<Dictionary<string, object>> response = await ToResponseAsync<Dictionary<string, object>>(request).ConfigureAwait(false);
return response.ResponseObject;
}
private async Task<Dictionary<string, object>> GetMetadata(Uri hostUri, string id, string scope, string template)
{
BoxRequest request = new BoxRequest(hostUri, string.Format(Constants.MetadataPathString, id, scope, template))
.Method(RequestMethod.Get);
IBoxResponse<Dictionary<string, object>> response = await ToResponseAsync<Dictionary<string, object>>(request).ConfigureAwait(false);
return response.ResponseObject;
}
private async Task<bool> DeleteMetadata(Uri hostUri, string id, string scope, string template)
{
BoxRequest request = new BoxRequest(hostUri, string.Format(Constants.MetadataPathString, id, scope, template))
.Method(RequestMethod.Delete);
IBoxResponse<Dictionary<string, object>> response = await ToResponseAsync<Dictionary<string, object>>(request).ConfigureAwait(false);
return response.Status == ResponseStatus.Success;
}
}
}