/// <summary> /// Given parsed untyped JSON in full compatible format, construct it into a typed daton /// </summary> public static Daton FromCompatibleWireFull(DataDictionary dbdef, JObject jroot) { var datonKey = DatonKey.Parse(jroot.Value <string>("key")); var datondef = dbdef.FindDef(datonKey); var daton = Utils.ConstructDaton(datondef.Type, datondef); daton.Key = datonKey; daton.Version = jroot.Value <string>("version"); if (daton is Viewon viewon && jroot.Value <bool>("isComplete") == false) { viewon.IsCompleteLoad = false; } var mainRowsNode = jroot[CamelCasify(datondef.MainTableDef.Name)]; if (mainRowsNode == null) { return(daton); } if (!(mainRowsNode is JArray mainRowsArray)) { throw new Exception($"{datondef.MainTableDef.Name} node must be an array"); } if (datondef.MultipleMainRows) { var targetListInfo = datondef.Type.GetField(datondef.MainTableDef.Name); if (targetListInfo == null) { throw new Exception($"Expected {datondef.MainTableDef.Name} to be a member of {datondef.Type.Name}"); } var targetList = Utils.CreateOrGetFieldValue <IList>(daton, targetListInfo) as IList; ReadCompatibleJsonRowArray(mainRowsArray, datondef.MainTableDef, (IList)targetList); } else { if (mainRowsArray.Count != 1) { throw new Exception($"{datondef.MainTableDef.Name} node must have one element for this daton type"); } ReadCompatibleJsonRow(mainRowsArray[0] as JObject, datondef.MainTableDef, daton); } daton.Recompute(datondef); return(daton); }
/// <summary> /// Get the list of daton keys with version numbers that were updated by other servers since the given time /// </summary> /// <param name="serverLifeNumber">the number for this server; updates from this server are excluded from the query</param> public static List <(DatonKey, string)> GetRecentUpdatesByOtherServers(DbConnection db, DateTime sinceUtc, int serverLifeNumber) { var ret = new List <(DatonKey, string)>(); using (var cmd = db.CreateCommand()) { cmd.CommandText = $"select DatonKey,DatonVersion from RetroLock where Touched>@t and UpdatedByServer<>@u"; Utils.AddParameterWithValue(cmd, "t", sinceUtc); Utils.AddParameterWithValue(cmd, "u", serverLifeNumber); using (var rdr = cmd.ExecuteReader()) { while (rdr.Read()) { var key = DatonKey.Parse(Utils.Read <string>(rdr, 0)); string version = Utils.Read <string>(rdr, 1); ret.Add((key, version)); } } } return(ret); }
/// <summary> /// Given parsed untyped JSON in diff format, construct a PersistonDiff /// </summary> public static PersistonDiff FromDiff(DataDictionary dbdef, JObject jroot) { var datonKey = DatonKey.Parse(jroot.Value <string>("key")); var datondef = dbdef.FindDef(datonKey); var diff = new PersistonDiff(datondef, datonKey, jroot.Value <string>("version")); ReadJsonDiffRowArray(jroot, datondef.MainTableDef, diff.MainTable); //existing single-main-row diffs might not include the primary key in the main row, so add it here if (!datondef.MultipleMainRows) { var mainDiffRow = diff.MainTable.First(); if (!mainDiffRow.Columns.ContainsKey(datondef.MainTableDef.PrimaryKeyColName)) { var pkColdef = datondef.MainTableDef.FindCol(datondef.MainTableDef.PrimaryKeyColName); var pk = Utils.ChangeType(((PersistonKey)datonKey).PrimaryKey, pkColdef.CSType); mainDiffRow.Columns[datondef.MainTableDef.PrimaryKeyColName] = pk; } } return(diff); }
private async Task HandleHttpMain(MainRequest req, IUser user, MainResponse resp) { //initialize if (req.Initialze != null) { resp.DataDictionary = Retrovert.DataDictionaryToWire(DataDictionary, user, LanguageMessages); } //load datons if (req.GetDatons != null) { var getResponses = new List <GetDatonResponse>(); foreach (var drequest in req.GetDatons) { var loadResult = await GetDaton(DatonKey.Parse(drequest.Key), user, forceCheckLatest : drequest.ForceLoad); var getResponse = new GetDatonResponse { Errors = loadResult.Errors }; if (loadResult.Daton != null) //null means it was not found by key, usually { bool doReturnToCaller = loadResult.Daton.Version == null || drequest.KnownVersion != loadResult.Daton.Version; //omit if client already has the current version if (doReturnToCaller) { getResponse.CondensedDaton = new CondensedDatonResponse { CondensedDatonJson = Retrovert.ToWire(DataDictionary, loadResult.Daton, false) }; } if (drequest.DoSubscribe && loadResult.Daton is Persiston) { ClientPlex.ManageSubscribe(req.SessionKey, loadResult.Daton.Key, loadResult.Daton.Version, true); } } else { getResponse.Key = drequest.Key; //only needed if daton is not returned to client } getResponses.Add(getResponse); } resp.GetDatons = getResponses.ToArray(); } //save datons if (req.SaveDatons != null) { var diffs = new List <PersistonDiff>(); foreach (var saveRequest in req.SaveDatons) { var diff = Retrovert.FromDiff(DataDictionary, saveRequest); diffs.Add(diff); } (bool success, var results) = await SaveDatons(req.SessionKey, user, diffs.ToArray()); var saveResponses = new List <SavePersistonResponse>(); foreach (var result in results) { saveResponses.Add(new SavePersistonResponse { IsDeleted = result.IsDeleted, IsSuccess = result.IsSuccess, OldKey = result.OldKey.ToString(), NewKey = result.NewKey?.ToString(), Errors = result.Errors }); } resp.SavedPersistons = saveResponses.ToArray(); resp.SavePersistonsSuccess = success; } //change datons state if (req.ManageDatons != null) { var manageResponses = new List <ManageDatonResponse>(req.ManageDatons.Length); foreach (var mrequest in req.ManageDatons) { //what does the caller wants to change? var datonKey = DatonKey.Parse(mrequest.Key); bool wantsLock = mrequest.SubscribeState == 2; bool wantsSubscribe = mrequest.SubscribeState >= 1; //handle change in subscription //(Performance note: unsubscribe should happen before unlock so that the unlock-propogation can short circuit reloading. Ultimately //if only one client is dealing with a daton and that client releases the lock and subscription, this server can forget about it //immediately.) bool isSubscribed = false; if (datonKey is PersistonKey) { ClientPlex.ManageSubscribe(req.SessionKey, datonKey, mrequest.Version, wantsSubscribe); isSubscribed = wantsSubscribe; } //handle change in lock string lockErrorCode = ""; bool hasLock = false; if (wantsLock) { if (string.IsNullOrEmpty(mrequest.Version)) { throw new Exception("Version required to lock daton"); } (hasLock, lockErrorCode) = LockManager.RequestLock(datonKey, mrequest.Version, req.SessionKey); } else { LockManager.ReleaseLock(datonKey, req.SessionKey); } manageResponses.Add(new ManageDatonResponse { ErrorCode = lockErrorCode, Key = mrequest.Key, SubscribeState = hasLock ? 2 : (isSubscribed ? 1 : 0) }); } resp.ManageDatons = manageResponses.ToArray(); } //quit - free up locks and memory if (req.DoQuit) { ClientPlex.DeleteSession(req.SessionKey); LockManager.ReleaseLocksForSession(req.SessionKey); } }