public async Task <IActionResult> SetUserData([FromBody] Poco.User.SyncData syncData) { var user = await _userManager.GetUserAsync(User); var userId = new Guid(user.Id); if (syncData.People.Any() && (syncData.People.First().Id != userId)) { return(BadRequest("Non-matching user id.")); } if (syncData.Locations.Any(l => l.PersonId != userId)) { return(BadRequest("Can't edit data with non-matching user.")); } // These two requre a db query to make sure they have valid locations (owned by them). if (syncData.Devices.Any() || syncData.CropCycles.Any() || syncData.Sensors.Any()) { var existingLocations = await _db.Locations.Where(l => l.PersonId == userId).ToListAsync(); // The personId on the syncData locations has been checked already, new locations sent are valid too. var existingLocationIds = existingLocations.Concat(existingLocations).Select(l => l.Id).ToList(); if (!syncData.Devices.All(d => existingLocationIds.Contains(d.LocationId)) || !syncData.CropCycles.All(c => existingLocationIds.Contains(c.LocationId))) { return(BadRequest("Can't edit data with non-matching user.")); } if (syncData.Sensors.Any()) { var validDeviceIds = (await _db.Devices.Where(d => existingLocationIds.Contains(d.LocationId)) .Select(d => d.Id).ToListAsync()) .Concat(syncData.Devices.Select(d => d.Id)); if (!syncData.Sensors.All(s => validDeviceIds.Contains(s.DeviceId))) { return(BadRequest("Can't edit data with non-matching user.")); } } } // The updatedAt times are automatically set by savechanges. await _db.People.AddOrUpdateRange(syncData.People, (a, b) => a.Id == b.Id); await _db.Locations.AddOrUpdateRange(syncData.Locations, (a, b) => a.Id == b.Id); await _db.Devices.AddOrUpdateRange(syncData.Devices, (a, b) => a.Id == b.Id); await _db.CropCycles.AddOrUpdateRange(syncData.CropCycles, (a, b) => a.Id == b.Id); await _db.Sensors.AddOrUpdateRange(syncData.Sensors, (a, b) => a.Id == b.Id); await _db.SaveChangesAsync(); return(new OkResult()); }
/// <summary>All user data including Person that has been updated after a specified date.</summary> /// <param name="startUnixUtcTimeSeconds">The earliest updated time to include, in UTC seconds since Unix epoch.</param> /// <returns>All user data apart from SensorHistory; people, locations, devies, cropCycles and sensors. Also sends /// serverDateTime just before the queries to get the data are made.</returns> public async Task <IActionResult> GetUserData(long?startUnixUtcTimeSeconds) { // Get the current server time before any database requests are started. // The client will regard itself be up-to-date to this time. var serverDateTime = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); var startDateTime = DateTimeOffset.FromUnixTimeSeconds(startUnixUtcTimeSeconds ?? 0); var user = await _userManager.GetUserAsync(User); var userGuid = new Guid(user.Id); // This will be an array with either 1 or 0 entries. This is more conventient than not using an array. var people = await _db.People .Where(p => p.Id == userGuid) .Where(p => p.UpdatedAt >= startDateTime).ToArrayAsync(); var locations = await _db.Locations .Where(l => l.PersonId == userGuid) .Where(l => l.UpdatedAt >= startDateTime).ToArrayAsync(); var locationIds = locations.Select(l => l.Id); var devices = await _db.Devices .Where(d => locationIds.Contains(d.LocationId)) .Where(d => d.UpdatedAt >= startDateTime).ToArrayAsync(); var cropcycles = await _db.CropCycles .Where(c => locationIds.Contains(c.LocationId)) .Where(c => c.UpdatedAt >= startDateTime).ToArrayAsync(); var cropCycleIds = cropcycles.Select(c => c.Id); var sensors = await _db.Sensors .Where(s => cropCycleIds.Contains(s.Id)) .Where(s => s.UpdatedAt >= startDateTime).ToArrayAsync(); var userData = new Poco.User.SyncData(serverDateTime, people, locations, devices, cropcycles, sensors); return(new JsonResult(userData)); }