Ejemplo n.º 1
0
        public virtual async Task <ActionResult <EntitiesResponse <TEntity> > > GetByIds([FromQuery] GetByIdsArguments <TKey> args, CancellationToken cancellation)
        {
            return(await ControllerUtilities.InvokeActionImpl(async() =>
            {
                // Calculate server time at the very beginning for consistency
                var serverTime = DateTimeOffset.UtcNow;

                // Load the data
                var service = GetFactWithIdService();
                var(entities, extras) = await service.GetByIds(args.I, args, Constants.Read, cancellation);

                // Flatten and Trim
                var relatedEntities = FlattenAndTrim(entities, cancellation);

                // Prepare the result in a response object
                var result = new EntitiesResponse <TEntity>
                {
                    Result = entities,
                    RelatedEntities = relatedEntities,
                    CollectionName = ControllerUtilities.GetCollectionName(typeof(TEntity)),
                    Extras = extras,
                    ServerTime = serverTime,
                };
                return Ok(result);
            }, _logger));
        }
Ejemplo n.º 2
0
        public static IActionResult Run(
            [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = null)] HttpRequest req,
            ILogger log,
            ExecutionContext context)
        {
            IConfigurationRoot  settings         = null;
            string              connectionString = null;
            CloudStorageAccount storageAccount   = null;
            CloudTableClient    tableClient      = null;

            try
            {
                settings = new ConfigurationBuilder()
                           .SetBasePath(context.FunctionAppDirectory)
                           .AddJsonFile("local.settings.json", optional: true, reloadOnChange: true)
                           .AddEnvironmentVariables()
                           .Build();
                connectionString = settings["AzureWebJobsStorage"];
                storageAccount   = CloudStorageAccount.Parse(connectionString);
                tableClient      = storageAccount.CreateCloudTableClient();
            }
            catch (Exception ex)
            {
                log.LogError(ex, "Failed to initialize Table Client.");
                return(new BadRequestResult());
            }

            DateTime season = DateTime.UtcNow;

            var seasonStr = req.Query["season"].ToString();

            if (seasonStr != "")
            {
                try
                {
                    season = DateTime.ParseExact(req.Query["season"], "yyyyMM", CultureInfo.InvariantCulture);
                }
                catch (Exception)
                {
                    return(new BadRequestObjectResult("Invalid season parameter. It should be a string formatted like 'yyyyMM'"));
                }
            }

            var responseObject = new EntitiesResponse <LedgerEntity>();
            var table          = tableClient.GetTableReference(settings["TableName"]);
            var query          = table.CreateQuery <LedgerEntity>()
                                 .Where(x => x.PartitionKey == $"{Util.GetPartitionKey(season)}")
                                 .Where(x => x.RowKey.CompareTo($"faction-") > 0)
                                 .Where(x => x.RowKey.CompareTo($"faction-{season.AddMonths(1):yyyyMM}01-00") < 0);

            foreach (var thing in query)
            {
                responseObject.Entries[thing.RowKey] = thing;
            }
            var resp = new OkObjectResult(responseObject);

            resp.ContentTypes.Add(new Microsoft.Net.Http.Headers.MediaTypeHeaderValue("application/json"));

            return(resp);
        }
Ejemplo n.º 3
0
        /// <summary>
        /// Saves the entities (Insert or Update) into the database after authorization and validation
        /// </summary>
        /// <returns>Optionally returns the same entities in their persisted READ form</returns>
        protected virtual async Task <EntitiesResponse <TDto> > SaveImplAsync(List <TDtoForSave> entities, SaveArguments args)
        {
            await CheckUpdatePermissions(entities, args);

            // Trim all strings as a preprocessing step
            entities.ForEach(e => TrimStringProperties(e));

            using (var trx = await BeginSaveTransaction())
            {
                try
                {
                    // Validate
                    await ValidateAsync(entities);

                    if (!ModelState.IsValid)
                    {
                        throw new UnprocessableEntityException(ModelState);
                    }

                    // Save
                    var(memoryList, query) = await PersistAsync(entities, args);

                    // Add the metadata
                    ApplySelectAndAddMetadata(memoryList, args.Expand, null);

                    // Apply the permission masks (setting restricted fields to null) and adjust the metadata accordingly
                    if (memoryList != null && memoryList.Any())
                    {
                        var permissions = await UserPermissions(PermissionLevel.Read);

                        var defaultMask = GetDefaultMask();
                        await ApplyReadPermissionsMask(memoryList, query, permissions, defaultMask);
                    }

                    // Flatten related entities and map each to its respective DTO
                    var relatedEntities = FlattenRelatedEntitiesAndTrim(memoryList, args.Expand);

                    // Map the primary result to DTOs as well
                    var resultData = Mapper.Map <List <TDto> >(memoryList);

                    // Prepare the result in a response object
                    var result = new EntitiesResponse <TDto>
                    {
                        Data            = resultData,
                        RelatedEntities = relatedEntities,
                        CollectionName  = GetCollectionName(typeof(TDto))
                    };

                    // Commit and return
                    trx.Commit();
                    return(result);
                }
                catch (Exception ex)
                {
                    // Roll back the transaction
                    trx.Rollback();
                    throw ex;
                }
            }
        }
Ejemplo n.º 4
0
        public async Task <ActionResult <EntitiesResponse <LineForSave> > > Generate([FromRoute] int lineDefId, [FromQuery] Dictionary <string, string> args, CancellationToken cancellation)
        {
            var serverTime = DateTimeOffset.UtcNow;

            var(lines, accounts, resources, agents, entryTypes, centers, currencies, units) = await GetService().Generate(lineDefId, args, cancellation);

            // Related entitiess
            var relatedEntities = new RelatedEntities
            {
                Account   = accounts,
                Resource  = resources,
                Agent     = agents,
                EntryType = entryTypes,
                Center    = centers,
                Currency  = currencies,
                Unit      = units
            };

            // Prepare the result in a response object
            var response = new EntitiesResponse <LineForSave>
            {
                Result          = lines,
                RelatedEntities = relatedEntities,
                CollectionName  = "", // Not important
                ServerTime      = serverTime,
            };

            // Return
            return(Ok(response));
        }
Ejemplo n.º 5
0
        /// <summary>
        /// Example code to call Rosette API to get entities from a piece of text.
        /// Requires Nuget Package:
        /// rosette_api
        /// </summary>
        static void Main(string[] args)
        {
            //To use the C# API, you must provide an API key
            string apikey = "Your API key";
            string alturl = string.Empty;

            //You may set the API key via command line argument:
            //entities yourapikeyhere
            if (args.Length != 0)
            {
                apikey = args[0];
                alturl = args.Length > 1 ? args[1] : string.Empty;
            }
            try
            {
                CAPI   EntitiesCAPI       = string.IsNullOrEmpty(alturl) ? new CAPI(apikey) : new CAPI(apikey, alturl);
                string entities_text_data = @"Bill Murray will appear in new Ghostbusters film: Dr. Peter Venkman was spotted filming a cameo in Boston this… http://dlvr.it/BnsFfS";
                //The results of the API call will come back in the form of a Dictionary
                EntitiesResponse response = EntitiesCAPI.Entity(entities_text_data, null, null, null, false, "social-media");
                foreach (KeyValuePair <string, string> h in response.Headers)
                {
                    Console.WriteLine(string.Format("{0}:{1}", h.Key, h.Value));
                }
                Console.WriteLine(response.ToString());
            }
            catch (Exception e)
            {
                Console.WriteLine("Exception: " + e.Message);
            }
        }
Ejemplo n.º 6
0
        /// <summary>
        /// Saves the entities (Insert or Update) into the database after authorization and validation.
        /// </summary>
        /// <returns>Optionally returns the same entities in their persisted READ form.</returns>
        protected virtual async Task <EntitiesResponse <TEntity> > SaveImplAsync(List <TEntityForSave> entities, SaveArguments args)
        {
            try
            {
                // Parse arguments
                var expand         = ExpandExpression.Parse(args?.Expand);
                var returnEntities = args?.ReturnEntities ?? false;

                // Trim all strings as a preprocessing step
                entities.ForEach(e => TrimStringProperties(e));

                // This implements field level security
                entities = await ApplyUpdatePermissionsMask(entities);

                // Start a transaction scope for save since it causes data modifications
                using var trx = ControllerUtilities.CreateTransaction(null, GetSaveTransactionOptions());

                // Optional preprocessing
                await SavePreprocessAsync(entities);

                // Validate
                // Basic validation that applies to all entities
                ControllerUtilities.ValidateUniqueIds(entities, ModelState, _localizer);

                // Actual Validation
                await SaveValidateAsync(entities);

                if (!ModelState.IsValid)
                {
                    throw new UnprocessableEntityException(ModelState);
                }

                // Save and retrieve Ids
                var ids = await SaveExecuteAsync(entities, expand, returnEntities);

                // Use the Ids to retrieve the items
                EntitiesResponse <TEntity> result = null;
                if (returnEntities && ids != null)
                {
                    result = await GetByIdListAsync(ids.ToArray(), expand);
                }

                await PostProcess(result);

                // Commit and return
                await OnSaveCompleted();

                trx.Complete();
                return(result);
            }
            catch (Exception ex)
            {
                await OnSaveError(ex);

                throw ex;
            }
        }
Ejemplo n.º 7
0
        /// <summary>
        /// Saves the entities (Insert or Update) into the database after authorization and validation
        /// </summary>
        /// <returns>Optionally returns the same entities in their persisted READ form</returns>
        protected virtual async Task <EntitiesResponse <TDto> > SaveImplAsync(List <TDtoForSave> entities, SaveArguments args)
        {
            // TODO Authorize POST
            await CheckUpdatePermissions(entities, args);

            // Trim all strings as a preprocessing step
            entities.ForEach(e => TrimStringProperties(e));

            using (var trx = await BeginSaveTransaction())
            {
                try
                {
                    // Validate
                    await ValidateAsync(entities);

                    if (!ModelState.IsValid)
                    {
                        throw new UnprocessableEntityException(ModelState);
                    }

                    // Save
                    var memoryList = await PersistAsync(entities, args);

                    // Flatten related entities and map each to its respective DTO
                    var relatedEntities = FlattenRelatedEntities(memoryList, args.Expand);

                    // Map the primary result to DTOs as well
                    var resultData = Map(memoryList);

                    // Prepare the result in a response object
                    var result = new EntitiesResponse <TDto>
                    {
                        Data            = resultData,
                        RelatedEntities = relatedEntities,
                        CollectionName  = GetCollectionName(typeof(TDto))
                    };

                    // Commit and return
                    trx.Commit();
                    return(result);
                }
                catch (Exception ex)
                {
                    // Roll back the transaction
                    trx.Rollback();
                    throw ex;
                }
            }
        }
Ejemplo n.º 8
0
        /// <summary>
        /// Example code to call Rosette API to get entities from a piece of text.
        /// Requires Nuget Package:
        /// rosette_api
        /// </summary>
        static void Main(string[] args)
        {
            //To use the C# API, you must provide an API key
            string apikey = "Your API key";
            string alturl = string.Empty;

            //You may set the API key via command line argument:
            //entities yourapikeyhere
            if (args.Length != 0)
            {
                apikey = args[0];
                alturl = args.Length > 1 ? args[1] : string.Empty;
            }
            try
            {
                CAPI   EntitiesCAPI       = string.IsNullOrEmpty(alturl) ? new CAPI(apikey) : new CAPI(apikey, alturl);
                string entities_text_data = @"The Securities and Exchange Commission today announced the leadership of the agency’s trial unit.  Bridget Fitzpatrick has been named Chief Litigation Counsel of the SEC and David Gottesman will continue to serve as the agency’s Deputy Chief Litigation Counsel. Since December 2016, Ms. Fitzpatrick and Mr. Gottesman have served as Co-Acting Chief Litigation Counsel.  In that role, they were jointly responsible for supervising the trial unit at the agency’s Washington D.C. headquarters as well as coordinating with litigators in the SEC’s 11 regional offices around the country.";
                //The results of the API call will come back in the form of a Dictionary
                EntitiesResponse response = EntitiesCAPI.Entity(entities_text_data, null, null, null, "social-media");
                foreach (KeyValuePair <string, string> h in response.Headers)
                {
                    Console.WriteLine(string.Format("{0}:{1}", h.Key, h.Value));
                }
                Console.WriteLine(response.ToString());

                // Entities with full ADM
                EntitiesCAPI.SetUrlParameter("output", "rosette");
                response = EntitiesCAPI.Entity(entities_text_data, null, null, null, "social-media");
                // response.Content contains the IDictionary results of the full ADM.
                // PrintContent() is a provided method to print the Dictionary to the console
                response.PrintContent();
            }
            catch (Exception e)
            {
                Console.WriteLine("Exception: " + e.Message);
            }
        }
Ejemplo n.º 9
0
        private async Task <ActionResult <EntitiesResponse <LocalUser> > > ActivateDeactivate([FromBody] List <int> ids, bool returnEntities, string expand, bool isActive)
        {
            await CheckActionPermissions(ids.Cast <int?>());

            var isActiveParam = new SqlParameter("@IsActive", isActive);

            DataTable idsTable = DataTable(ids.Select(id => new { Id = id }), addIndex: false);
            var       idsTvp   = new SqlParameter("@Ids", idsTable)
            {
                TypeName  = $"dbo.IdList",
                SqlDbType = SqlDbType.Structured
            };

            string sql = @"
    SET NOCOUNT ON;
    DECLARE @Now DATETIMEOFFSET(7) = SYSDATETIMEOFFSET();
    DECLARE @UserId INT = CONVERT(INT, SESSION_CONTEXT(N'UserId'));
    DECLARE @Emails [dbo].[CodeList];

    INSERT INTO @Emails([Code])
    SELECT x.[Email]
    FROM
    (
        MERGE INTO [dbo].[LocalUsers] AS t
	        USING (
		        SELECT [Id]
		        FROM @Ids
	        ) AS s ON (t.Id = s.Id)
	        WHEN MATCHED AND (t.IsActive <> @IsActive)
	        THEN
		        UPDATE SET 
			    t.[IsActive]	= @IsActive,
			    t.[ModifiedAt]	= @Now,
			    t.[ModifiedById]= @UserId
                OUTPUT inserted.[Email]
    ) As x;

    SELECT [Code] AS [Value] FROM @Emails;
";

            // Tenant Id
            var tenantId = new SqlParameter("TenantId", _tenantIdProvider.GetTenantId());

            using (var trxApp = await _db.Database.BeginTransactionAsync())
            {
                try
                {
                    // Update the entities and retrieve the emails of the entities that were updated
                    List <string> emails = await _db.Strings.FromSql(sql, idsTvp, isActiveParam).Select(e => e.Value).ToListAsync();

                    // Prepare the TVP of emails to update from the manager
                    DataTable emailsTable = DataTable(emails.Select(e => new { Code = e }), addIndex: false);
                    var       emailsTvp   = new SqlParameter("Emails", emailsTable)
                    {
                        TypeName  = $"dbo.CodeList",
                        SqlDbType = SqlDbType.Structured
                    };

                    using (var trxAdmin = await _adminDb.Database.BeginTransactionAsync())
                    {
                        try
                        {
                            if (isActive)
                            {
                                // Insert efficiently with a SQL query
                                await _adminDb.Database.ExecuteSqlCommandAsync($@"
    INSERT INTO dbo.[TenantMemberships] 
    SELECT Id, @TenantId FROM [dbo].[GlobalUsers] WHERE Email IN (SELECT Code from @Emails);
", emailsTvp, tenantId);
                            }
                            else
                            {
                                // Delete efficiently with a SQL query
                                await _adminDb.Database.ExecuteSqlCommandAsync($@"
    DELETE FROM dbo.[TenantMemberships] 
    WHERE TenantId = @TenantId AND UserId IN (
        SELECT Id FROM [dbo].[GlobalUsers] WHERE Email IN (SELECT Code from @Emails)
    );
", emailsTvp, tenantId);
                            }

                            // Commit both
                            trxAdmin.Commit();
                            trxApp.Commit();
                        }
                        catch (Exception ex)
                        {
                            trxApp.Rollback();
                            trxAdmin.Rollback();
                            throw ex;
                        }
                    }
                }
                catch (Exception ex)
                {
                    trxApp.Rollback();
                    throw ex;
                }
            }

            // Determine whether entities should be returned
            if (!returnEntities)
            {
                // IF no returned items are expected, simply return 200 OK
                return(Ok());
            }
            else
            {
                // Load the entities using their Ids
                var affectedDbEntitiesQ         = _db.LocalUsers.Where(e => ids.Contains(e.Id)); // _db.LocalUsers.FromSql("SELECT * FROM [dbo].[LocalUsers] WHERE Id IN (SELECT Id FROM @Ids)", idsTvp);
                var affectedDbEntitiesExpandedQ = Expand(affectedDbEntitiesQ, expand);
                var affectedDbEntities          = await affectedDbEntitiesExpandedQ.ToListAsync();

                var affectedEntities = _mapper.Map <List <LocalUser> >(affectedDbEntities);

                // sort the entities the way their Ids came, as a good practice
                LocalUser[] sortedAffectedEntities = new LocalUser[ids.Count];
                Dictionary <int, LocalUser> affectedEntitiesDic = affectedEntities.ToDictionary(e => e.Id.Value);
                for (int i = 0; i < ids.Count; i++)
                {
                    var       id     = ids[i];
                    LocalUser entity = null;
                    if (affectedEntitiesDic.ContainsKey(id))
                    {
                        entity = affectedEntitiesDic[id];
                    }

                    sortedAffectedEntities[i] = entity;
                }

                // Prepare a proper response
                var response = new EntitiesResponse <LocalUser>
                {
                    Data           = sortedAffectedEntities,
                    CollectionName = GetCollectionName(typeof(LocalUser))
                };

                // Commit and return
                return(Ok(response));
            }
        }
Ejemplo n.º 10
0
 /// <summary>
 /// Gives an opportunity for inheriting controllers to post process the result before it is served
 /// </summary>
 protected virtual Task PostProcess(EntitiesResponse <TEntity> result)
 {
     return(Task.CompletedTask);
 }
Ejemplo n.º 11
0
        private async Task <ActionResult <EntitiesResponse <Role> > > ActivateDeactivate([FromBody] List <int> ids, bool returnEntities, string expand, bool isActive)
        {
            await CheckActionPermissions(ids.Cast <int?>());

            using (var trx = await _db.Database.BeginTransactionAsync())
            {
                try
                {
                    // TODO Authorize Activate

                    // TODO Validate (No used units, no duplicate Ids, no missing Ids?)

                    var isActiveParam = new SqlParameter("@IsActive", isActive);

                    DataTable idsTable = ControllerUtilities.DataTable(ids.Select(id => new { Id = id }), addIndex: false);
                    var       idsTvp   = new SqlParameter("@Ids", idsTable)
                    {
                        TypeName  = $"dbo.IdList",
                        SqlDbType = SqlDbType.Structured
                    };

                    string sql = @"
-- TODO: PermissionsVersion
DECLARE @NewId UNIQUEIDENTIFIER = NEWID();
UPDATE [dbo].[LocalUsers] SET PermissionsVersion = @NewId;

DECLARE @Now DATETIMEOFFSET(7) = SYSDATETIMEOFFSET();
DECLARE @UserId INT = CONVERT(INT, SESSION_CONTEXT(N'UserId'));

MERGE INTO [dbo].[Roles] AS t
	USING (
		SELECT [Id]
		FROM @Ids
	) AS s ON (t.Id = s.Id)
	WHEN MATCHED AND (t.IsActive <> @IsActive)
	THEN
		UPDATE SET 
			t.[IsActive]	= @IsActive,
			t.[ModifiedAt]	= @Now,
			t.[ModifiedById]	= @UserId;
";

                    // Update the entities
                    await _db.Database.ExecuteSqlCommandAsync(sql, idsTvp, isActiveParam);

                    trx.Commit();
                }
                catch (Exception ex)
                {
                    trx.Rollback();
                    throw ex;
                }
            }

            // Determine whether entities should be returned
            if (!returnEntities)
            {
                // IF no returned items are expected, simply return 200 OK
                return(Ok());
            }
            else
            {
                // Load the entities using their Ids
                var affectedDbEntitiesQ         = _db.VW_Roles.Where(e => ids.Contains(e.Id.Value)); // _db.VW_Roles.FromSql("SELECT * FROM [dbo].[Roles] WHERE Id IN (SELECT Id FROM @Ids)", idsTvp);
                var affectedDbEntitiesExpandedQ = Expand(affectedDbEntitiesQ, expand);
                var affectedDbEntities          = await affectedDbEntitiesExpandedQ.ToListAsync();

                // Add the metadata
                ApplySelectAndAddMetadata(affectedDbEntities, expand, null);

                // sort the entities the way their Ids came, as a good practice
                var    affectedEntities       = Mapper.Map <List <Role> >(affectedDbEntities);
                Role[] sortedAffectedEntities = new Role[ids.Count];
                Dictionary <int, Role> affectedEntitiesDic = affectedEntities.ToDictionary(e => e.Id.Value);
                for (int i = 0; i < ids.Count; i++)
                {
                    var  id     = ids[i];
                    Role entity = null;
                    if (affectedEntitiesDic.ContainsKey(id))
                    {
                        entity = affectedEntitiesDic[id];
                    }

                    sortedAffectedEntities[i] = entity;
                }

                // Apply the permission masks (setting restricted fields to null) and adjust the metadata accordingly
                await ApplyReadPermissionsMask(affectedDbEntities, affectedDbEntitiesExpandedQ, await UserPermissions(PermissionLevel.Read), GetDefaultMask());

                // Flatten related entities and map each to its respective DTO
                var relatedEntities = FlattenRelatedEntitiesAndTrim(affectedDbEntities, expand);

                // Prepare a proper response
                var response = new EntitiesResponse <Role>
                {
                    Data            = sortedAffectedEntities,
                    CollectionName  = GetCollectionName(typeof(Role)),
                    RelatedEntities = relatedEntities
                };

                // Commit and return
                return(Ok(response));
            }
        }
Ejemplo n.º 12
0
        private async Task <ActionResult <EntitiesResponse <Agent> > > ActivateDeactivate([FromBody] List <int> ids, bool returnEntities, string expand, bool isActive)
        {
            await CheckActionPermissions(ids.Cast <int?>());

            var isActiveParam = new SqlParameter("@IsActive", isActive);

            DataTable idsTable = DataTable(ids.Select(id => new { Id = id }), addIndex: false);
            var       idsTvp   = new SqlParameter("@Ids", idsTable)
            {
                TypeName  = $"dbo.IdList",
                SqlDbType = SqlDbType.Structured
            };

            string sql = @"
DECLARE @Now DATETIMEOFFSET(7) = SYSDATETIMEOFFSET();
DECLARE @UserId INT = CONVERT(INT, SESSION_CONTEXT(N'UserId'));

MERGE INTO [dbo].[Custodies] AS t
	USING (
		SELECT [Id]
		FROM @Ids
	) AS s ON (t.Id = s.Id)
	WHEN MATCHED AND (t.IsActive <> @IsActive)
	THEN
		UPDATE SET 
			t.[IsActive]	= @IsActive,
			t.[ModifiedAt]	= @Now,
			t.[ModifiedById]	= @UserId;
";

            using (var trx = await _db.Database.BeginTransactionAsync())
            {
                try
                {
                    // Update the entities
                    await _db.Database.ExecuteSqlCommandAsync(sql, idsTvp, isActiveParam);

                    trx.Commit();
                }
                catch (Exception ex)
                {
                    trx.Rollback();
                    throw ex;
                }
            }

            // Determine whether entities should be returned
            if (!returnEntities)
            {
                // IF no returned items are expected, simply return 200 OK
                return(Ok());
            }
            else
            {
                // Load the entities using their Ids
                var affectedDbEntitiesQ         = _db.Agents.Where(e => ids.Contains(e.Id)); //.FromSql("SELECT * FROM [dbo].[Custodies] WHERE Id IN (SELECT Id FROM @Ids)", idsTvp);
                var affectedDbEntitiesExpandedQ = Expand(affectedDbEntitiesQ, expand);
                var affectedDbEntities          = await affectedDbEntitiesExpandedQ.ToListAsync();

                var affectedEntities = _mapper.Map <List <Agent> >(affectedDbEntities);

                // sort the entities the way their Ids came, as a good practice
                Agent[] sortedAffectedEntities = new Agent[ids.Count];
                Dictionary <int, Agent> affectedEntitiesDic = affectedEntities.ToDictionary(e => e.Id.Value);
                for (int i = 0; i < ids.Count; i++)
                {
                    var   id     = ids[i];
                    Agent entity = null;
                    if (affectedEntitiesDic.ContainsKey(id))
                    {
                        entity = affectedEntitiesDic[id];
                    }

                    sortedAffectedEntities[i] = entity;
                }

                // Prepare a proper response
                var response = new EntitiesResponse <Agent>
                {
                    Data           = sortedAffectedEntities,
                    CollectionName = GetCollectionName(typeof(Agent))
                };

                // Commit and return
                return(Ok(response));
            }
        }