public async Task <IReadOnlyList <EntityWithId <Uri, string> > > GetEntities(IEnumerable <Uri> eventUrls)
        {
            var requestBody = @"<?xml version=""1.0""?>
			                    <C:calendar-multiget xmlns:C=""urn:ietf:params:xml:ns:caldav"" xmlns:D=""DAV:"">
			                        <D:prop>
			                            <D:getetag/>
			                            <D:displayname/>
			                            <C:calendar-data/>
			                        </D:prop>
                                        " + String.Join("\r\n", eventUrls.Select(u => string.Format("<D:href>{0}</D:href>", SecurityElement.Escape(u.ToString())))) + @"
                                    </C:calendar-multiget>";

            var responseXml = await _webDavClient.ExecuteWebDavRequestAndReadResponse(
                _serverUrl,
                "REPORT",
                1,
                null,
                null,
                "application/xml",
                requestBody
                );

            XmlNodeList responseNodes = responseXml.XmlDocument.SelectNodes("/D:multistatus/D:response", responseXml.XmlNamespaceManager);

            var entities = new List <EntityWithId <Uri, string> >();

            if (responseNodes == null)
            {
                return(entities);
            }

            // ReSharper disable once LoopCanBeConvertedToQuery
            // ReSharper disable once PossibleNullReferenceException
            foreach (XmlElement responseElement in responseNodes)
            {
                var urlNode  = responseElement.SelectSingleNode("D:href", responseXml.XmlNamespaceManager);
                var dataNode = responseElement.SelectSingleNode("D:propstat/D:prop/C:calendar-data", responseXml.XmlNamespaceManager);
                if (urlNode != null && dataNode != null)
                {
                    entities.Add(EntityWithId.Create(UriHelper.UnescapeRelativeUri(_serverUrl, urlNode.InnerText), dataNode.InnerText));
                }
            }

            return(entities);
        }
        public async Task Should_store_entity_with_value_id()
        {
            using var svc = CreateDataService();
            var http = new HttpClient();
            var id   = new Guid("d390c9533f5545588c0b77fbae798c9d");
            var e    = new EntityWithId {
                Id = id, Some = "abc", Int = 123
            };

            var response = await http.PostBsonAsync($"{svc.ListenUri}/store/Entity/single", e);

            response.StatusCode.Should().Be(HttpStatusCode.Created);
            response.Headers.Location.Should().Be(new Uri($"{svc.ListenUri}/store/Entity/single/d390c9533f5545588c0b77fbae798c9d"));
            (await http.GetBsonAsync <EntityWithId>(response.Headers.Location)).Should().Be(e);

            e.Some   = "eee";
            response = await http.PostBsonAsync($"{svc.ListenUri}/store/Entity/single", e);

            response.StatusCode.Should().Be(HttpStatusCode.Created);
            response.Headers.Location.Should().Be(new Uri($"{svc.ListenUri}/store/Entity/single/d390c9533f5545588c0b77fbae798c9d"));
            (await http.GetBsonAsync <EntityWithId>(response.Headers.Location)).Should().Be(e);
        }
Beispiel #3
0
        public static string SyncOutlookToCalDav_EventsExistsInCalDav(string existingEventData, IEntityRelationDataAccess <string, DateTime, WebResourceName, string> entityRelationDataAccess = null)
        {
            string            roundTrippedData = null;
            ICalDavDataAccess calDavDataAccess = MockRepository.GenerateMock <ICalDavDataAccess>();
            var entityUri = new WebResourceName("/e1");

            calDavDataAccess
            .Expect(r => r.GetEventVersions(null))
            .IgnoreArguments()
            .Return(Task.FromResult <IReadOnlyList <EntityVersion <WebResourceName, string> > > (
                        new[] { EntityVersion.Create(entityUri, "v1") }));

            calDavDataAccess
            .Expect(r => r.GetEntities(Arg <ICollection <WebResourceName> > .List.Equal(new[] { entityUri })))
            .Return(Task.FromResult <IReadOnlyList <EntityWithId <WebResourceName, string> > > (
                        new[] { EntityWithId.Create(entityUri, existingEventData) }));

            calDavDataAccess
            .Expect(r => r.TryUpdateEntity(
                        new WebResourceName("http://bla.com"),
                        null,
                        null))
            .IgnoreArguments()
            .Return(Task.FromResult <EntityVersion <WebResourceName, string> > (
                        EntityVersion.Create(new WebResourceName("http://bla.com"), "blubb")))
            .WhenCalled(a => { roundTrippedData = (string)a.Arguments[2]; });

            var synchronizer = OutlookTestContext.CreateEventSynchronizer(
                SynchronizationMode.ReplicateOutlookIntoServer,
                calDavDataAccess,
                entityRelationDataAccess);

            WaitForTask(synchronizer.SynchronizeNoThrow(NullSynchronizationLogger.Instance));

            return(roundTrippedData);
        }
Beispiel #4
0
        public static void SyncCalDavToOutlook(string eventData, IEntityRelationDataAccess <string, DateTime, WebResourceName, string> entityRelationDataAccess)
        {
            var calDavDataAccess = MockRepository.GenerateMock <ICalDavDataAccess>();

            var entityUri = new WebResourceName("/e1");

            calDavDataAccess
            .Expect(r => r.GetEventVersions(null))
            .IgnoreArguments()
            .Return(Task.FromResult <IReadOnlyList <EntityVersion <WebResourceName, string> > > (
                        new[] { EntityVersion.Create(entityUri, "v1") }));

            calDavDataAccess
            .Expect(r => r.GetEntities(Arg <ICollection <WebResourceName> > .List.Equal(new[] { entityUri })))
            .Return(Task.FromResult <IReadOnlyList <EntityWithId <WebResourceName, string> > > (
                        new[] { EntityWithId.Create(entityUri, eventData) }));

            var synchronizer = OutlookTestContext.CreateEventSynchronizer(
                SynchronizationMode.ReplicateServerIntoOutlook,
                calDavDataAccess,
                entityRelationDataAccess);

            WaitForTask(synchronizer.SynchronizeNoThrow(NullSynchronizationLogger.Instance));
        }
        public async Task Synchronize_GetVersionsAndGetReturnDuplicateEntries_RemovesDuplicates()
        {
            var builder = new SynchronizerBuilder();

            builder.AtypeIdComparer = StringComparer.InvariantCultureIgnoreCase;

            builder.AtypeRepository
            .Expect(r => r.GetAllVersions(new string[] { }))
            .IgnoreArguments()
            .Return(
                Task.FromResult <IReadOnlyList <EntityVersion <string, string> > > (
                    new[] { EntityVersion.Create("A1", "v1"), EntityVersion.Create("a1", "v3") }));

            builder.BtypeRepository
            .Expect(r => r.GetAllVersions(new string[] { }))
            .IgnoreArguments()
            .Return(
                Task.FromResult <IReadOnlyList <EntityVersion <string, string> > > (
                    new[] { EntityVersion.Create("b1", "v2") }));


            Task <IReadOnlyList <EntityWithId <string, string> > > aTypeLoadTask = new Task <IReadOnlyList <EntityWithId <string, string> > > (
                () => new List <EntityWithId <string, string> > {
                EntityWithId.Create("A1", "AAAA"), EntityWithId.Create("a1", "____")
            });

            aTypeLoadTask.RunSynchronously();
            builder.AtypeRepository
            .Expect(r => r.Get(Arg <ICollection <string> > .Matches(c => c.Count == 1 && c.First() == "A1"), Arg <ILoadEntityLogger> .Is.NotNull))
            .Return(aTypeLoadTask);

            Task <IReadOnlyList <EntityWithId <string, string> > > bTypeLoadTask = new Task <IReadOnlyList <EntityWithId <string, string> > > (
                () => new List <EntityWithId <string, string> > {
                EntityWithId.Create("b1", "BBBB"),
            });

            bTypeLoadTask.RunSynchronously();
            builder.BtypeRepository
            .Expect(r => r.Get(Arg <ICollection <string> > .Matches(c => c.Count == 1 && c.First() == "b1"), Arg <ILoadEntityLogger> .Is.NotNull))
            .Return(bTypeLoadTask);


            var knownData = new EntityRelationData <string, string, string, string> ("A1", "v1", "b1", "v2");

            builder.InitialEntityMatcher
            .Expect(m => m.FindMatchingEntities(null, null, null, null, null))
            .IgnoreArguments()
            .Return(new List <IEntityRelationData <string, string, string, string> > {
                knownData
            });

            builder.InitialSyncStateCreationStrategy
            .Expect(s => s.CreateFor_Unchanged_Unchanged(knownData))
            .Return(new DoNothing <string, string, string, string, string, string> (knownData));

            var synchronizer = builder.Build();
            await synchronizer.SynchronizeNoThrow(NullSynchronizationLogger.Instance);

            builder.EntityRelationDataAccess.AssertWasCalled(
                c => c.SaveEntityRelationData(Arg <List <IEntityRelationData <string, string, string, string> > > .Matches(l => l.Count == 1 && l[0] == knownData)));
        }
        public async Task <IReadOnlyList <EntityWithId <WebResourceName, string> > > GetEntities(IEnumerable <WebResourceName> eventUrls)
        {
            s_logger.Debug("Entered GetEntities.");

            WebResourceName firstResourceNameOrNull = null;

            var requestBody = @"<?xml version=""1.0""?>
			                    <C:calendar-multiget xmlns:C=""urn:ietf:params:xml:ns:caldav"" xmlns:D=""DAV:"">
			                        <D:prop>
			                            <D:getetag/>
			                            <D:displayname/>
			                            <C:calendar-data/>
			                        </D:prop>
                                        " +
                              String.Join(
                "\r\n",
                eventUrls.Select(
                    u =>
            {
                if (s_logger.IsDebugEnabled)
                {
                    s_logger.Debug($"Requesting: '{u}'");
                }
                if (firstResourceNameOrNull == null)
                {
                    firstResourceNameOrNull = u;
                }
                return($"<D:href>{SecurityElement.Escape(u.OriginalAbsolutePath)}</D:href>");
            })) + @"
                                    </C:calendar-multiget>";

            var responseXml = await _webDavClient.ExecuteWebDavRequestAndReadResponse(
                UriHelper.AlignServerUrl(_serverUrl, firstResourceNameOrNull),
                "REPORT",
                1,
                null,
                null,
                "application/xml",
                requestBody
                );

            XmlNodeList responseNodes = responseXml.XmlDocument.SelectNodes("/D:multistatus/D:response", responseXml.XmlNamespaceManager);

            var entities = new List <EntityWithId <WebResourceName, string> >();

            if (responseNodes == null)
            {
                return(entities);
            }

            // ReSharper disable once LoopCanBeConvertedToQuery
            // ReSharper disable once PossibleNullReferenceException
            foreach (XmlElement responseElement in responseNodes)
            {
                var urlNode  = responseElement.SelectSingleNode("D:href", responseXml.XmlNamespaceManager);
                var dataNode = responseElement.SelectSingleNode("D:propstat/D:prop/C:calendar-data", responseXml.XmlNamespaceManager);
                if (urlNode != null && !string.IsNullOrEmpty(dataNode?.InnerText))
                {
                    if (s_logger.IsDebugEnabled)
                    {
                        s_logger.DebugFormat($"Got: '{urlNode.InnerText}'");
                    }
                    entities.Add(EntityWithId.Create(new WebResourceName(urlNode.InnerText), dataNode.InnerText));
                }
            }

            s_logger.Debug("Exiting GetEntities.");
            return(entities);
        }
Beispiel #7
0
 public Task <IReadOnlyList <EntityWithId <Identifier, string> > > Get(ICollection <Identifier> ids, ILoadEntityLogger logger)
 {
     return(Task.FromResult <IReadOnlyList <EntityWithId <Identifier, string> > > (
                ids.Select(id => EntityWithId.Create(id, EntityVersionAndContentById[id].Item2)).ToArray()));
 }
Beispiel #8
0
        private Task <IReadOnlyList <EntityWithId <WebResourceName, IICalendar> > > ParallelDeserialize(IReadOnlyList <EntityWithId <WebResourceName, string> > serializedEntities, ILoadEntityLogger logger)
        {
            return(Task.Factory.StartNew(() =>
            {
                var result = new List <EntityWithId <WebResourceName, IICalendar> >();

                Parallel.ForEach(
                    serializedEntities,
                    () => Tuple.Create(new iCalendarSerializer(), new List <Tuple <WebResourceName, IICalendar> >()),
                    (serialized, loopState, threadLocal) =>
                {
                    IICalendar calendar;
                    string normalizedICalData, fixedICalData;

                    // fix some linebreak issues with Open-Xchange
                    if (serialized.Entity.Contains("\r\r\n"))
                    {
                        normalizedICalData = CalendarDataPreprocessor.NormalizeLineBreaks(serialized.Entity);
                    }
                    else
                    {
                        normalizedICalData = serialized.Entity;
                    }

                    // emClient sets DTSTART in VTIMEZONE to year 0001, which causes a 90 sec delay in DDay.iCal to evaluate the recurrence rule.
                    // If we find such a DTSTART we replace it 0001 with 1970 since the historic data is not valid anyway and avoid the performance issue.
                    if (normalizedICalData.Contains("DTSTART:00010101"))
                    {
                        fixedICalData = CalendarDataPreprocessor.FixInvalidDTSTARTInTimeZoneNoThrow(normalizedICalData);
                        s_logger.InfoFormat("Changed DTSTART from year 0001 to 1970 in VTIMEZONE of ICalData '{0}'.", serialized.Id);
                    }
                    else
                    {
                        fixedICalData = normalizedICalData;
                    }

                    if (TryDeserializeCalendar(fixedICalData, out calendar, serialized.Id, threadLocal.Item1, NullLoadEntityLogger.Instance))
                    {
                        threadLocal.Item2.Add(Tuple.Create(serialized.Id, calendar));
                    }
                    else
                    {
                        // maybe deserialization failed because of the iCal-TimeZone-Bug =>  try to fix it
                        var fixedICalData2 = CalendarDataPreprocessor.FixTimeZoneComponentOrderNoThrow(fixedICalData);
                        if (TryDeserializeCalendar(fixedICalData2, out calendar, serialized.Id, threadLocal.Item1, logger))
                        {
                            threadLocal.Item2.Add(Tuple.Create(serialized.Id, calendar));
                            s_logger.Info(string.Format("Deserialized ICalData with reordering of TimeZone data '{0}'.", serialized.Id));
                        }
                    }

                    return threadLocal;
                },
                    threadLocal =>
                {
                    lock (result)
                    {
                        foreach (var calendar in threadLocal.Item2)
                        {
                            result.Add(EntityWithId.Create(calendar.Item1, calendar.Item2));
                        }
                    }
                });

                IReadOnlyList <EntityWithId <WebResourceName, IICalendar> > readOnlyResult = result;
                return readOnlyResult;
            }));
        }
Beispiel #9
0
 private static void AddToDictionary <TKey, TValue>(Dictionary <TKey, TValue> dictionary, EntityWithId <TKey, TValue> tuple)
 {
     if (!dictionary.ContainsKey(tuple.Id))
     {
         dictionary.Add(tuple.Id, tuple.Entity);
     }
     else
     {
         s_logger.WarnFormat("Entitiy '{0}' was contained multiple times in server response. Ignoring redundant entity", tuple.Id);
     }
 }
        public async Task <IReadOnlyList <EntityWithId <WebResourceName, string> > > GetEntities(IEnumerable <WebResourceName> urls)
        {
            s_logger.Debug("Entered GetEntities.");


            var requestBody = @"<?xml version=""1.0"" encoding=""utf-8"" ?>
   <A:addressbook-multiget xmlns:D=""DAV:"" xmlns:A=""urn:ietf:params:xml:ns:carddav"">
     <D:prop>
       <D:getetag/>
       <D:getcontenttype/>
       <A:address-data/>
     </D:prop>
     " +
                              String.Join("\r\n", urls.Select(u =>
            {
                if (s_logger.IsDebugEnabled)
                {
                    s_logger.Debug($"Requesting: '{u}'");
                }

                return($"<D:href>{SecurityElement.Escape (u.OriginalAbsolutePath)}</D:href>");
            }))
                              + @"
   </A:addressbook-multiget>
 ";


            var responseXml = await _webDavClient.ExecuteWebDavRequestAndReadResponse(
                _serverUrl,
                "REPORT",
                0,
                null,
                null,
                "application/xml",
                requestBody
                );

            XmlNodeList responseNodes = responseXml.XmlDocument.SelectNodes("/D:multistatus/D:response", responseXml.XmlNamespaceManager);

            var entities = new List <EntityWithId <WebResourceName, string> >();

            if (responseNodes == null)
            {
                return(entities);
            }

            // ReSharper disable once LoopCanBeConvertedToQuery
            // ReSharper disable once PossibleNullReferenceException
            foreach (XmlElement responseElement in responseNodes)
            {
                var    urlNode         = responseElement.SelectSingleNode("D:href", responseXml.XmlNamespaceManager);
                var    dataNode        = responseElement.SelectSingleNode("D:propstat/D:prop/A:address-data", responseXml.XmlNamespaceManager);
                var    contentTypeNode = responseElement.SelectSingleNode("D:propstat/D:prop/D:getcontenttype", responseXml.XmlNamespaceManager);
                string contentType     = contentTypeNode.InnerText ?? string.Empty;

                // TODO: add vlist support but for now filter out sogo vlists since we can't parse them atm
                if (urlNode != null && dataNode != null && !string.IsNullOrEmpty(dataNode.InnerText) && contentType != "text/x-vlist")
                {
                    if (s_logger.IsDebugEnabled)
                    {
                        s_logger.DebugFormat($"Got: '{urlNode.InnerText}'");
                    }
                    entities.Add(EntityWithId.Create(new WebResourceName(urlNode.InnerText), dataNode.InnerText));
                }
            }

            s_logger.Debug("Exiting GetEntities.");
            return(entities);
        }
Beispiel #11
0
 public Task <IReadOnlyList <EntityWithId <Uri, string> > > GetEntities(IEnumerable <Uri> eventUrls)
 {
     return(Task.FromResult <IReadOnlyList <EntityWithId <Uri, string> > > (
                eventUrls.Select(id => EntityWithId.Create(id, _entites[id].Item2)).ToList()));
 }
        private async Task <IReadOnlyList <IEntitySyncStateContext <TAtypeEntityId, TAtypeEntityVersion, TAtypeEntity, TBtypeEntityId, TBtypeEntityVersion, TBtypeEntity, TContext> > > CreateEntitySyncStateContexts(
            IEntityContainer <TAtypeEntityId, TAtypeEntity, TContext> aEntities,
            IEntityContainer <TBtypeEntityId, TBtypeEntity, TContext> bEntities,
            IEnumerable <IEntityRelationData <TAtypeEntityId, TAtypeEntityVersion, TBtypeEntityId, TBtypeEntityVersion> > knownEntityRelations,
            Dictionary <TAtypeEntityId, TAtypeEntityVersion> newAVersions,
            Dictionary <TBtypeEntityId, TBtypeEntityVersion> newBVersions,
            ISynchronizationLogger logger,
            TContext synchronizationContext,
            ISynchronizationInterceptor <TAtypeEntityId, TAtypeEntityVersion, TAtypeEntity, TBtypeEntityId, TBtypeEntityVersion, TBtypeEntity, TContext> interceptor)
        {
            var entitySynchronizationContexts = new List <IEntitySyncStateContext <TAtypeEntityId, TAtypeEntityVersion, TAtypeEntity, TBtypeEntityId, TBtypeEntityVersion, TBtypeEntity, TContext> >();

            var aDeltaLogInfo = new VersionDeltaLoginInformation();
            var bDeltaLogInfo = new VersionDeltaLoginInformation();

            foreach (var knownEntityRelationData in knownEntityRelations)
            {
                TAtypeEntityVersion newAVersion;
                TBtypeEntityVersion newBVersion;

                var newAVersionAvailable = newAVersions.TryGetValue(knownEntityRelationData.AtypeId, out newAVersion);
                var newBVersionAvailable = newBVersions.TryGetValue(knownEntityRelationData.BtypeId, out newBVersion);

                if (newAVersionAvailable)
                {
                    newAVersions.Remove(knownEntityRelationData.AtypeId);
                }

                if (newBVersionAvailable)
                {
                    newBVersions.Remove(knownEntityRelationData.BtypeId);
                }

                entitySynchronizationContexts.Add(new EntitySyncStateContext <TAtypeEntityId, TAtypeEntityVersion, TAtypeEntity, TBtypeEntityId, TBtypeEntityVersion, TBtypeEntity, TContext>(
                                                      CreateInitialSyncState(knownEntityRelationData, newAVersionAvailable, newAVersion, newBVersionAvailable, newBVersion, aDeltaLogInfo, bDeltaLogInfo)));
            }

            await _atypeRepository.VerifyUnknownEntities(newAVersions, synchronizationContext);

            await _btypeRepository.VerifyUnknownEntities(newBVersions, synchronizationContext);

            if (newAVersions.Count > 0 && newBVersions.Count > 0)
            {
                s_logger.Info($"Performing entity matching with {newAVersions.Count} Atype and {newBVersions.Count} Btype entities.");

                var matchingEntites = _initialEntityMatcher.FindMatchingEntities(
                    _entityRelationDataFactory,
                    (await aEntities.SelectEntities(newAVersions.Keys, logger.ALoadEntityLogger, synchronizationContext, e => EntityWithId.Create(e.Id, _aMatchDataFactory.CreateMatchData(e.Entity)))).ToDictionary(e => e.Id, e => e.Entity),
                    (await bEntities.SelectEntities(newBVersions.Keys, logger.BLoadEntityLogger, synchronizationContext, e => EntityWithId.Create(e.Id, _bMatchDataFactory.CreateMatchData(e.Entity)))).ToDictionary(e => e.Id, e => e.Entity),
                    newAVersions,
                    newBVersions);

                foreach (var knownEntityRelationData in matchingEntites)
                {
                    newAVersions.Remove(knownEntityRelationData.AtypeId);
                    newBVersions.Remove(knownEntityRelationData.BtypeId);
                    var entitySyncState = _initialSyncStateCreationStrategy.CreateFor_Unchanged_Unchanged(knownEntityRelationData);
                    entitySynchronizationContexts.Add(new EntitySyncStateContext <TAtypeEntityId, TAtypeEntityVersion, TAtypeEntity, TBtypeEntityId, TBtypeEntityVersion, TBtypeEntity, TContext>(entitySyncState));
                    aDeltaLogInfo.IncUnchanged();
                    bDeltaLogInfo.IncUnchanged();
                }

                s_logger.Info("Entity matching finished.");
            }

            foreach (var newA in newAVersions)
            {
                var syncState = _initialSyncStateCreationStrategy.CreateFor_Added_NotExisting(newA.Key, newA.Value);
                entitySynchronizationContexts.Add(new EntitySyncStateContext <TAtypeEntityId, TAtypeEntityVersion, TAtypeEntity, TBtypeEntityId, TBtypeEntityVersion, TBtypeEntity, TContext>(syncState));
            }

            foreach (var newB in newBVersions)
            {
                var syncState = _initialSyncStateCreationStrategy.CreateFor_NotExisting_Added(newB.Key, newB.Value);
                entitySynchronizationContexts.Add(new EntitySyncStateContext <TAtypeEntityId, TAtypeEntityVersion, TAtypeEntity, TBtypeEntityId, TBtypeEntityVersion, TBtypeEntity, TContext>(syncState));
            }

            interceptor.TransformInitialCreatedStates(entitySynchronizationContexts, _syncStateFactory);

            // all the leftovers in newAVersions and newBVersions must be the added ones
            aDeltaLogInfo.IncAdded(newAVersions.Count);
            bDeltaLogInfo.IncAdded(newBVersions.Count);
            s_logger.InfoFormat("Atype delta: {0}", aDeltaLogInfo);
            s_logger.InfoFormat("Btype delta: {0}", bDeltaLogInfo);
            logger.LogDeltas(aDeltaLogInfo, bDeltaLogInfo);

            return(entitySynchronizationContexts);
        }