public IHttpActionResult Clipboard([FromBody] PostBody body)
        {
            if (body == null)
            {
                throw new ArgumentNullException(nameof(body));
            }
            if (string.IsNullOrEmpty(body.Data))
            {
                throw new ArgumentNullException(nameof(body.Data));
            }

            //todo improve this, hard coded as dev session id for now
            if (!CheckHeaderSession())
            {
                //return Unauthorized();
            }

            var time = body.CreatedAt ?? DateTime.Now;
            var item = new DtoClipboardItem()
            {
                Uid       = Guid.NewGuid(),
                CreatedBy = MapToUserId(Request.Headers.GetValues(Constants.HeaderSessionId).First()),
                CreatedAt = time,
                Data      = body.Data
            };

            string path = GetFullClipboardDataPath(time);

            AppendNoteToFile(path, item);

            string indexPath = GetFullClipboardIndexPath(time);

            AppendObjectToFile(indexPath, DtoLskjsonIndex.From(item));

            return(DtoResultV5.Success(Json, $"{body.Data.Length} characters saved to clipboard."));
        }
        public IHttpActionResult Clipboard([FromBody] PutBody putBody)
        {
            if (putBody == null)
            {
                throw new ArgumentNullException(nameof(putBody));
            }
            if (putBody.Uid == null)
            {
                throw new ArgumentNullException(nameof(putBody.Uid));
            }

            var inputUid = Guid.Parse(putBody.Uid);

            //todo improve this, hard coded as dev session id for now
            if (!CheckHeaderSession())
            {
                //return Unauthorized();
            }

            var createdAt = GetOriginCreatedAt(inputUid);

            if (!createdAt.HasValue)
            {
                throw new InvalidOperationException("Data already deleted, please reload to latest then edit");
            }

            var path = GetFullClipboardDataPath(createdAt.Value); //ensure orig item and updated item in the same file

            if (!File.Exists(path))
            {
                return(DtoResultV5.Success(this.Json, "no data"));
            }

            //perf - O(n) is 2n here, can be optimized to n
            var notes      = ReadLskjson <Guid, DtoClipboardItem>(path, CollectLskjsonLineClipboard);
            var foundNote  = notes.FirstOrDefault(n => n.Uid == inputUid);
            var foundChild = notes.FirstOrDefault(n => n.ParentUid == inputUid);

            //ensure the relation is a chain, not a tree
            if (foundChild != null)
            {
                throw new InvalidOperationException("Data already changed, please reload to latest then edit");
            }

            if (foundNote != null)
            {
                var newNote = Utility.DeepClone(foundNote);

                newNote.Uid  = Guid.NewGuid(); //reset
                newNote.Data = putBody.Data;

                //todo - replace with real UserId(get by session id)
                newNote.HasUpdated    = true;
                newNote.LastUpdatedBy = Constants.DevUpdateUserId + DateTime.Now.ToString("dd");
                newNote.LastUpdatedAt = DateTime.Now;
                newNote.ParentUid     = foundNote.Uid;

                AppendNoteToFile(path, newNote);
                AppendObjectToFile(GetFullClipboardIndexPath(newNote.CreatedAt), DtoLskjsonIndex.From(newNote));

                //return DtoResultV5.Success(Json, MapToJSNameConvention(newNote), "Data updated");
                return(DtoResultV5.Success(Json, newNote, "Data updated")); //do the map on client side
            }
            else
            {
                return(DtoResultV5.Fail(Json, "The data you want to update may have been deleted"));
            }
        }