Exemple #1
0
        public void ExportManager_chains_exporter_to_packager() {

            // Arrange
            XUri dekiApiUri = new XUri("http://mock/@api/deki");
            XDoc exportDocument = new XDoc("export");
            XUri item1Uri = dekiApiUri.At("foo", "bar", "abc");
            XDoc item1Doc = new XDoc("item1");
            XUri item2Uri = dekiApiUri.At("foo", "bar", "def");
            XDoc item2Doc = new XDoc("item2");
            XDoc exportResponse = new XDoc("export")
                .Start("requests")
                    .Start("request")
                        .Attr("method", "GET")
                        .Attr("dataid", "abc")
                        .Attr("href", item1Uri)
                        .Start("header").Attr("name", "h_1").Attr("value", "v_1").End()
                        .Start("header").Attr("name", "h_2").Attr("value", "v_2").End()
                    .End()
                    .Start("request")
                        .Attr("method", "GET")
                        .Attr("dataid", "def")
                        .Attr("href", item2Uri)
                    .End()
                .End()
                .Start("manifest")
                    .Start("foo").Attr("dataid", "abc").End()
                    .Start("bar").Attr("dataid", "def").End()
                .End();
            AutoMockPlug mock = MockPlug.Register(dekiApiUri);
            mock.Expect().Verb("POST").Uri(dekiApiUri.At("site", "export").With("relto", "0")).RequestDocument(exportDocument).Response(DreamMessage.Ok(exportResponse));
            mock.Expect().Verb("GET").Uri(item1Uri).RequestHeader("h_1", "v_1").RequestHeader("h_2", "v_2").Response(DreamMessage.Ok(item1Doc));
            mock.Expect().Verb("GET").Uri(item2Uri).Response(DreamMessage.Ok(item2Doc));
            var writes = new List<string>();
            var mockPackageWriter = new Mock<IPackageWriter>();
            mockPackageWriter.Setup(x => x.WriteDataAsync(It.IsAny<ExportItem>(), It.IsAny<Result>()))
                .Returns(() => new Result().WithReturn())
                .Callback((ExportItem item, Result result) => writes.Add(item.DataId))
                .AtMost(2)
                .Verifiable();
            mockPackageWriter.Setup(x => x.WriteManifest(It.IsAny<XDoc>(), It.IsAny<Result>()))
                .Returns(() => new Result().WithReturn())
                .AtMostOnce()
                .Verifiable();

            // Act
            ExportManager manager = ExportManager.CreateAsync(Plug.New(dekiApiUri), exportDocument, 0, mockPackageWriter.Object, new Result<ExportManager>()).Wait();
            manager.ExportAsync(new Result()).Wait();

            // Assert
            Assert.IsTrue(mock.WaitAndVerify(TimeSpan.FromSeconds(1)), mock.VerificationFailure);
            Assert.AreEqual(2, manager.TotalItems);
            Assert.AreEqual(2, manager.CompletedItems);
            Assert.AreEqual(new[] { "abc", "def" }, writes.ToArray());
            mockPackageWriter.Verify(x => x.Dispose(), Times.Once());
            mockPackageWriter.VerifyAll();
        }
Exemple #2
0
        public void Exporter_hits_export_feature_on_creation_using_relto() {

            // Arrange
            XUri dekiApiUri = new XUri("http://mock/@api/deki");
            XDoc exportDocument = new XDoc("export")
                .Start("page")
                    .Attr("path", "/")
                    .Attr("recursive", "true")
                    .Attr("exclude", "all")
                .End();
            XDoc exportResponse = new XDoc("export")
                .Start("requests")
                .End()
                .Start("manifest")
                    .Elem("justanode")
                .End();
            AutoMockPlug mock = MockPlug.Register(dekiApiUri);
            mock.Expect("POST", dekiApiUri.At("site", "export").With("relto", 5.ToString()), exportDocument, DreamMessage.Ok(exportResponse));

            // Act
            Exporter exporter = Exporter.CreateAsync(Plug.New(dekiApiUri), exportDocument, 5, new Result<Exporter>()).Wait();

            //Assert
            Assert.IsTrue(mock.WaitAndVerify(TimeSpan.FromSeconds(1)));
            Assert.AreEqual(exportResponse["manifest"], exporter.Manifest);
        }
        public void ImportManager_chains_reader_to_importer() {

            // Arrange
            var dekiApiUri = new XUri("http://mock/@api/deki");
            var importManifest = new XDoc("manifest");
            var item1Uri = dekiApiUri.At("foo", "bar", "abc");
            var item1Doc = new XDoc("item1");
            var item2Uri = dekiApiUri.At("foo", "bar", "def");
            var item2Doc = new XDoc("item2");
            var importResponse = new XDoc("requests")
                .Start("request")
                    .Attr("method", "POST")
                    .Attr("dataid", "abc")
                    .Attr("href", item1Uri)
                    .Start("header").Attr("name", "h_1").Attr("value", "v_1").End()
                    .Start("header").Attr("name", "h_2").Attr("value", "v_2").End()
                .End()
                .Start("request")
                    .Attr("method", "PUT")
                    .Attr("dataid", "def")
                    .Attr("href", item2Uri)
                .End();
            var mock = MockPlug.Register(dekiApiUri);
            mock.Expect().Verb("POST").Uri(dekiApiUri.At("site", "import").With("relto", "0")).RequestDocument(importManifest).Response(DreamMessage.Ok(importResponse));
            mock.Expect().Verb("POST").Uri(item1Uri).RequestHeader("h_1", "v_1").RequestHeader("h_2", "v_2").RequestDocument(item1Doc);
            mock.Expect().Verb("PUT").Uri(item2Uri).RequestDocument(item2Doc);

            var mockPackageReader = new Mock<IPackageReader>();
            mockPackageReader.Setup(x => x.ReadManifest(It.IsAny<Result<XDoc>>())).Returns(importManifest.AsResult()).Verifiable("didn't get manifest");
            var item1stream = new MemoryStream(item1Doc.ToBytes());
            mockPackageReader.Setup(x => x.ReadData(It.Is<ImportItem>(y => y.DataId == "abc"), It.IsAny<Result<ImportItem>>()))
                .Returns(() => new ImportItem("abc", importResponse["request[@dataid='abc']"], null, item1stream, item1stream.Length).AsResult())
                .Verifiable();
            var item2stream = new MemoryStream(item2Doc.ToBytes());
            mockPackageReader.Setup(x => x.ReadData(It.Is<ImportItem>(y => y.DataId == "def"), It.IsAny<Result<ImportItem>>()))
                .Returns(() => new ImportItem("def", importResponse["request[@dataid='def']"], null, item2stream, item2stream.Length).AsResult())
                .Verifiable();
            mockPackageReader.Setup(x => x.Dispose()).Verifiable();
            
            // Act
            var manager = ImportManager.CreateAsync(Plug.New(dekiApiUri), 0, mockPackageReader.Object, new Result<ImportManager>()).Wait();
            manager.ImportAsync(new Result()).Wait();

            //Assert
            Assert.IsTrue(mock.WaitAndVerify(TimeSpan.FromSeconds(1)), mock.VerificationFailure);
            mockPackageReader.VerifyAll();
        }
        public void Importer_hits_import_feature_with_reltopath() {

            // Arrange
            XUri dekiApiUri = new XUri("http://mock/@api/deki");
            XDoc importManifest = new XDoc("manifest");
            XDoc importResponse = new XDoc("requests")
                .Start("request").Attr("dataid", "a").End()
                .Start("request").Attr("dataid", "b").End()
                .Start("request").Attr("dataid", "c").End();
            AutoMockPlug mock = MockPlug.Register(dekiApiUri);
            mock.Expect("POST", dekiApiUri.At("site", "import").With("reltopath", "/foo/bar"), importManifest, DreamMessage.Ok(importResponse));

            // Act
            Importer importer = Importer.CreateAsync(Plug.New(dekiApiUri), importManifest, "/foo/bar", new Result<Importer>()).Wait();

            //Assert
            Assert.IsTrue(mock.WaitAndVerify(TimeSpan.FromSeconds(1)));
            Assert.AreEqual(importManifest, importer.Manifest);
            Assert.AreEqual(new[] { "a", "b", "c" }, importer.Items.Select(x => x.DataId).ToArray());
        }
        public static XDoc GetUserXml(UserBE user, string relation, bool showPrivateInfo) {
            XDoc userXml = new XDoc(string.IsNullOrEmpty(relation) ? "user" : "user." + relation);
            userXml.Attr("id", user.ID);
            userXml.Attr("href", DekiContext.Current.ApiUri.At("users", user.ID.ToString()));
            userXml.Elem("nick", user.Name);
            userXml.Elem("username", user.Name);
            userXml.Elem("fullname", user.RealName ?? String.Empty);

            // check if we can add the email address
            if(showPrivateInfo) {
                userXml.Elem("email", user.Email);
            } else {
                userXml.Start("email").Attr("hidden", true).End();
            }

            // add gravatar
            if(!IsAnonymous(user) && !string.IsNullOrEmpty(user.Email)) {
                DekiContext context = DekiContext.CurrentOrNull;
                XUri gravatar = new XUri("http://www.gravatar.com/avatar");
                string hash = string.Empty;
                if(context != null) {
                    DekiInstance deki = context.Instance;
                    string secure = context.Instance.GravatarSalt ?? string.Empty;
                    if(!secure.EqualsInvariantIgnoreCase("hidden")) {
                        hash = StringUtil.ComputeHashString(secure + (user.Email ?? string.Empty).Trim().ToLowerInvariant(), System.Text.Encoding.UTF8);
                    }

                    // add size, if any
                    string size = deki.GravatarSize;
                    if(size != null) {
                        gravatar = gravatar.With("s", size);
                    }

                    // add rating, if any
                    string rating = deki.GravatarRating;
                    if(rating != null) {
                        gravatar = gravatar.With("r", rating);
                    }

                    // add default icon, if any
                    string def = deki.GravatarDefault;
                    if(def != null) {
                        gravatar = gravatar.With("d", def);
                    }
                }
                if(!string.IsNullOrEmpty(hash)) {
                    userXml.Elem("hash.email", hash);
                    userXml.Elem("uri.gravatar", gravatar.At(hash + ".png"));
                } else {
                    userXml.Elem("hash.email", string.Empty);
                    userXml.Elem("uri.gravatar", gravatar.At("no-email.png"));
                }
            }
            return userXml;
        }
Exemple #6
0
        internal Hashtable MakeUserObject(UserBE user) {

            // initialize gravatar link
            DekiInstance deki = DekiContext.Current.Instance;
            XUri gravatar = new XUri("http://www.gravatar.com/avatar");

            // add size, if any
            string size = deki.GravatarSize;
            if(size != null) {
                gravatar = gravatar.With("s", size);
            }

            // add rating, if any
            string rating = deki.GravatarRating;
            if(rating != null) {
                gravatar = gravatar.With("r", rating);
            }

            // add default icon, if any
            string def = deki.GravatarDefault;
            if(def != null) {
                gravatar = gravatar.With("d", def);
            }

            // initialize user object
            Hashtable result = new Hashtable(StringComparer.OrdinalIgnoreCase);
            string hash = string.Empty;
            if(user != null) {
                var env = DreamContext.Current.GetState<DekiScriptEnv>();
                string secure = deki.GravatarSalt ?? string.Empty;
                if(!secure.EqualsInvariantIgnoreCase("hidden") && !string.IsNullOrEmpty(user.Email)) {
                    hash = StringUtil.ComputeHashString(secure + (user.Email ?? string.Empty).Trim().ToLowerInvariant(), Encoding.UTF8);
                }
                PageBE homePage = UserBL.GetHomePage(user);
                result.Add("id", user.ID);
                result.Add("name", user.Name);
                result.Add("fullname", !string.IsNullOrEmpty(user.RealName) ? user.RealName : null);
                result.Add("uri", Utils.AsPublicUiUri(homePage.Title));
                result.Add("api", Utils.AsPublicApiUri("users", user.ID).ToString());
                result.Add("homepage", PropertyAt("$page", homePage.ID, true));
                result.Add("anonymous", UserBL.IsAnonymous(user));
                result.Add("admin", PermissionsBL.IsUserAllowed(user, Permissions.ADMIN));
                result.Add("feed", Utils.AsPublicApiUri("users", user.ID).At("feed").ToString());
                result.Add("authtoken", (!env.IsSafeMode && DekiContext.Current.User.ID == user.ID) ? DekiContext.Current.AuthToken : null);
                result.Add("properties", PropertyAt("$userprops", user.ID));
                result.Add("comments", PropertyAt("$usercomments", user.ID));
                result.Add("timezone", DekiScriptLibrary.RenderTimeZone(DekiScriptLibrary.ParseTimeZone(user.Timezone)));
                result.Add("language", DekiScriptExpression.Constant(string.IsNullOrEmpty(user.Language) ? null : user.Language));
                result.Add("metrics", PropertyAt("$usermetrics", user.ID));
                if(Utils.ShowPrivateUserInfo(user)) {
                    result.Add("email", user.Email);
                }
                result.Add("groups", PropertyAt("$usergroups", user.ID));
            } else {
                result.Add("id", 0);
                result.Add("name", null);
                result.Add("fullname", null);
                result.Add("uri", null);
                result.Add("api", null);
                result.Add("homepage", null);
                result.Add("anonymous", true);
                result.Add("admin", false);
                result.Add("feed", null);
                result.Add("authtoken", null);
                result.Add("properties", new Hashtable(StringComparer.OrdinalIgnoreCase));
                result.Add("comments", new ArrayList());
                result.Add("timezone", "GMT");
                result.Add("language", DekiScriptNil.Value);
                result.Add("metrics", new Hashtable(StringComparer.OrdinalIgnoreCase));
                result.Add("groups", new Hashtable(StringComparer.OrdinalIgnoreCase));
            }

            // set the emailhash and gravatar values
            if(!string.IsNullOrEmpty(hash)) {
                result.Add("emailhash", hash);
                result.Add("gravatar", gravatar.At(hash + ".png"));
            } else {
                result.Add("emailhash", string.Empty);
                result.Add("gravatar", gravatar.At("no-email.png"));
            }
            return result;
        }
Exemple #7
0
        private XDoc GetPropertyXml(IList<ResourceBE> properties, XUri parentResourceUri, bool collection, string propSuffix, bool? explicitRevisionInfo, uint? contentCutoff, XDoc doc) {
            bool requiresEnd = false;
            if(collection) {
                string rootPropertiesNode = string.IsNullOrEmpty(propSuffix) ? "properties" : "properties." + propSuffix;
                if(doc == null) {
                    doc = new XDoc(rootPropertiesNode);
                } else {
                    doc.Start(rootPropertiesNode);
                    requiresEnd = true;
                }

                doc.Attr("count", properties.Count);

                if(parentResourceUri != null) {

                    //Note: this assumes that the property collection of a resource is always accessed by appending "properties" to the parent URI
                    doc.Attr("href", parentResourceUri.At("properties"));
                }

            } else {
                doc = XDoc.Empty;
            }

            //Batch retrieve users for user.modified and user.deleted
            Dictionary<uint, UserBE> usersById = new Dictionary<uint, UserBE>();
            foreach(ResourceBE r in properties) {
                usersById[r.UserId] = null;
            }
            if(!ArrayUtil.IsNullOrEmpty(properties)) {
                usersById = DbUtils.CurrentSession.Users_GetByIds(usersById.Keys.ToArray()).AsHash(e => e.ID);
            }

            foreach(ResourceBE p in properties) {
                doc = AppendPropertyXml(doc, p, parentResourceUri, propSuffix, explicitRevisionInfo, contentCutoff, usersById);
            }

            if(requiresEnd) {
                doc.End();
            }

            return doc;
        }
 //--- Methods ---
 public XUri Uri(XUri parentUri) {
     return parentUri.At("properties", XUri.DoubleEncodeSegment(Name));
 }
        public void User_without_proper_page_permission_gets_forbidden_on_subscribe_attempt() {

            // set up mocks for all the support service calls
            XUri deki = new XUri("http://mock/deki");
            MockPlug.Register(deki, delegate(Plug p, string v, XUri u, DreamMessage r, Result<DreamMessage> r2) {
                _log.DebugFormat("deki: {0}", u);
                DreamMessage msg;
                if(u.Path.StartsWith("/deki/pages")) {
                    msg = DreamMessage.Ok(new XDoc("users"));
                } else {
                    msg = DreamMessage.Ok(new XDoc("user")
                        .Attr("id", "1")
                        .Elem("email", "*****@*****.**")
                        .Elem("language", "en")
                        .Start("permissions.user")
                            .Elem("operations", "READ,SUBSCRIBE,LOGIN")
                        .End());
                }
                msg.Headers.Add("X-Deki-Site", "id=wicked");
                r2.Return(msg);
            });
            XUri subscribe = new XUri("http://mock/sub");
            MockPlug.Register(subscribe, delegate(Plug p, string v, XUri u, DreamMessage r, Result<DreamMessage> r2) {
                if(u == subscribe.At("subscribers")) {
                    _log.Debug("creating subscription");
                    DreamMessage msg = DreamMessage.Ok(new XDoc("x"));
                    msg.Headers.Location = subscribe.At("testsub");
                    r2.Return(msg);
                } else {
                    _log.Debug("updating subscription");
                    r2.Return(DreamMessage.Ok());
                }
            });
            XUri storage = new XUri("http://mock/store");
            MockPlug.Register(storage, delegate(Plug p, string v, XUri u, DreamMessage r, Result<DreamMessage> r2) {
                _log.DebugFormat("storage: {0}", u);
                r2.Return(DreamMessage.Ok(new XDoc("foo")));
            });

            // set up service
            _log.Debug("set up service");
            DreamServiceInfo serviceInfo = DreamTestHelper.CreateService(
                _hostInfo,
                typeof(DekiChangeSubscriptionService),
                "email",
                new XDoc("config")
                    .Elem("uri.emailer", new XUri("http://mock/email"))
                    .Elem("uri.deki", deki)
                    .Elem("uri.pubsub", subscribe)
                    .Elem("uri.storage", storage)
                );
            Plug service = serviceInfo.WithInternalKey().AtLocalHost;

            // post a subscription
            _log.Debug("post page 10 subscription");
            DreamMessage response = service
                .At("pages", "10")
                .With("depth", "infinity")
                .WithHeader("X-Deki-Site", "id=wicked")
                .PostAsync()
                .Wait();
            Assert.IsFalse(response.IsSuccessful);
            Assert.AreEqual(DreamStatus.Forbidden, response.Status);
        }
 public void Initialize_service_with_persisted_subscriptions() {
     XUri email = new XUri("http://mock/email");
     XUri deki = new XUri("http://mock/deki");
     MockPlug.Register(deki, delegate(Plug p, string v, XUri u, DreamMessage r, Result<DreamMessage> r2) {
         _log.DebugFormat("deki: {0}", u);
         DreamMessage msg = DreamMessage.Ok(new XDoc("user")
             .Attr("id", "1")
             .Elem("email", "*****@*****.**")
             .Start("permissions.user")
                 .Elem("operations", "READ,SUBSCRIBE,LOGIN")
             .End());
         msg.Headers.Add("X-Deki-Site", "id=wicked");
         r2.Return(msg);
     });
     XUri subscribe = new XUri("http://mock/sub");
     int subscribeCalled = 0;
     MockPlug.Register(subscribe, delegate(Plug p, string v, XUri u, DreamMessage r, Result<DreamMessage> r2) {
         _log.DebugFormat("subscribe: {0}", u);
         subscribeCalled++;
         DreamMessage msg = DreamMessage.Ok(new XDoc("foo"));
         msg.Headers.Location = subscribe;
         r2.Return(msg);
     });
     XUri storage = new XUri("http://mock/store");
     int storageListCalled = 0;
     int storageFileCalled = 0;
     int storageBadCalled = 0;
     MockPlug.Register(storage, delegate(Plug p, string v, XUri u, DreamMessage r, Result<DreamMessage> r2) {
         _log.DebugFormat("storage: {0}", u);
         if(v == "GET") {
             if(u == storage.At("subscriptions")) {
                 storageListCalled++;
                 r2.Return(DreamMessage.Ok(new XDoc("files")
                                               .Start("folder").Elem("name", "wicked").End()
                               ));
                 return;
             } else if(u == storage.At("subscriptions", "wicked")) {
                 storageListCalled++;
                 r2.Return(DreamMessage.Ok(new XDoc("files")
                                               .Start("file").Elem("name", "user_1.xml").End()
                                               .Start("file").Elem("name", "bar.txt").End()
                               ));
                 return;
             } else if(u == storage.At("subscriptions", "wicked", "user_1.xml")) {
                 storageFileCalled++;
                 r2.Return(DreamMessage.Ok(new XDoc("user")
                                               .Attr("userid", 1)
                                               .Elem("email", "foo")
                                               .Start("subscription.page").Attr("id", 1).Attr("depth", 0).End()
                               ));
                 return;
             }
         }
         storageBadCalled++;
         throw new DreamBadRequestException("unexpected call");
     });
     DreamServiceInfo serviceInfo = DreamTestHelper.CreateService(
         _hostInfo,
         typeof(DekiChangeSubscriptionService),
         "email",
         new XDoc("config")
             .Elem("uri.emailer", email)
             .Elem("uri.deki", deki)
             .Elem("uri.pubsub", subscribe)
             .Elem("uri.storage", storage)
         );
     Assert.AreEqual(1, subscribeCalled);
     Assert.AreEqual(2, storageListCalled);
     Assert.AreEqual(1, storageFileCalled);
     Assert.AreEqual(0, storageBadCalled);
     _log.Debug("get all subscriptions");
     DreamMessage response = serviceInfo.AtLocalHost
         .At("subscriptions")
         .WithHeader("X-Deki-Site", "id=wicked")
         .GetAsync()
         .Wait();
     Assert.IsTrue(response.IsSuccessful);
     XDoc subscriptions = response.ToDocument();
     XDoc sub = subscriptions["subscription.page"];
     Assert.AreEqual(1, sub.ListLength);
     Assert.AreEqual("1", sub["@id"].AsText);
     Assert.AreEqual("0", sub["@depth"].AsText);
 }
        public void Request_without_valid_user_headers_results_in_not_authorized() {

            // set up mocks for all the support service calls
            XUri deki = new XUri("http://mock/deki");
            MockPlug.Register(deki, delegate(Plug p, string v, XUri u, DreamMessage r, Result<DreamMessage> r2) {
                _log.DebugFormat("deki: {0}", u);
                r2.Return(DreamMessage.AccessDenied("deki", "bad puppy"));
            });
            XUri subscribe = new XUri("http://mock/sub");
            MockPlug.Register(subscribe, delegate(Plug p, string v, XUri u, DreamMessage r, Result<DreamMessage> r2) {
                if(u == subscribe.At("subscribers")) {
                    _log.Debug("creating subscription");
                    DreamMessage msg = DreamMessage.Ok(new XDoc("x"));
                    msg.Headers.Location = subscribe.At("testsub");
                    r2.Return(msg);
                } else {
                    _log.Debug("updating subscription");
                    r2.Return(DreamMessage.Ok());
                }
            });
            XUri storage = new XUri("http://mock/store");
            MockPlug.Register(storage, delegate(Plug p, string v, XUri u, DreamMessage r, Result<DreamMessage> r2) {
                _log.DebugFormat("storage: {0}", u);
                r2.Return(DreamMessage.Ok(new XDoc("foo")));
            });

            // set up service
            _log.Debug("set up service");
            DreamServiceInfo serviceInfo = DreamTestHelper.CreateService(
                _hostInfo,
                typeof(DekiChangeSubscriptionService),
                "email",
                new XDoc("config")
                    .Elem("uri.emailer", new XUri("http://mock/email"))
                    .Elem("uri.deki", deki)
                    .Elem("uri.pubsub", subscribe)
                    .Elem("uri.storage", storage)
                );
            Plug service = serviceInfo.WithInternalKey().AtLocalHost;

            // post a subscription
            _log.Debug("post page 10 subscription");
            DreamMessage response = service
                .At("pages", "10")
                .With("depth", "infinity")
                .WithHeader("X-Deki-Site", "id=wicked")
                .PostAsync()
                .Wait();
            Assert.IsFalse(response.IsSuccessful);
            Assert.AreEqual(DreamStatus.Unauthorized, response.Status);
        }
        public void Uses_deki_to_prune_recipients() {
            XUri mockDeki = new XUri("http://mock/deki");
            int dekiCalled = 0;
            int dekipage42authCalled = 0;
            bool dekiArgsGood = false;
            int dekipage43authCalled = 0;
            MockPlug.Register(mockDeki, delegate(Plug p, string v, XUri u, DreamMessage r, Result<DreamMessage> r2) {
                _log.DebugFormat("mockDeki called at: {0}", u);
                dekiCalled++;
                dekiArgsGood = false;
                List<int> users = new List<int>();
                foreach(XDoc user in r.ToDocument()["user/@id"]) {
                    users.Add(user.AsInt.Value);
                }
                if(users.Count == 4 && users.Contains(1) && users.Contains(2) && users.Contains(3) && users.Contains(4)) {
                    dekiArgsGood = true;
                }
                DreamMessage msg = DreamMessage.Ok();
                if(u.WithoutQuery() == mockDeki.At("pages", "42", "allowed")) {
                    dekipage42authCalled++;
                    msg = DreamMessage.Ok(new XDoc("users")
                        .Start("user").Attr("id", 1).End()
                        .Start("user").Attr("id", 2).End());
                } else if(u.WithoutQuery() == mockDeki.At("pages", "43", "allowed")) {
                    dekipage43authCalled++;
                    msg = DreamMessage.Ok(new XDoc("users")
                        .Start("user").Attr("id", 3).End()
                        .Start("user").Attr("id", 4).End());
                }
                r2.Return(msg);
            });
            XUri mockRecipient = new XUri("http://mock/r1");
            int mockRecipientCalled = 0;
            XDoc received = null;
            List<string> recipients = new List<string>();
            AutoResetEvent are = new AutoResetEvent(false);
            MockPlug.Register(mockRecipient, delegate(Plug p, string v, XUri u, DreamMessage r, Result<DreamMessage> r2) {
                _log.DebugFormat("mockRecipient called at: {0}", u);
                mockRecipientCalled++;
                received = r.ToDocument();
                recipients.Clear();
                recipients.AddRange(r.Headers.DreamEventRecipients);
                are.Set();
                r2.Return(DreamMessage.Ok());
            });

            DreamServiceInfo serviceInfo = DreamTestHelper.CreateService(
                _hostInfo,
                "sid://mindtouch.com/dream/2008/10/pubsub",
                "whitelist",
                new XDoc("config")
                .Elem("uri.deki", mockDeki)
                .Start("components")
                    .Attr("context", "service")
                    .Start("component")
                        .Attr("implementation", typeof(DekiDispatcher).AssemblyQualifiedName)
                        .Attr("type", typeof(IPubSubDispatcher).AssemblyQualifiedName)
                    .End()
                .End()
            );
            XDoc sub = new XDoc("subscription-set")
                .Elem("uri.owner", mockRecipient)
                .Start("subscription")
                    .Attr("id", "1")
                    .Elem("uri.resource", "http://mock/resource/x")
                    .Elem("channel", "channel:///foo/*")
                    .Elem("uri.proxy", mockRecipient)
                    .Start("recipient").Attr("userid", "1").Elem("uri", "http://recipient/a").End()
                    .Start("recipient").Attr("userid", "2").Elem("uri", "http://recipient/b").End()
                    .Start("recipient").Attr("userid", "3").Elem("uri", "http://recipient/c").End()
                    .Start("recipient").Attr("userid", "4").Elem("uri", "http://recipient/d").End()
                .End()
                .Start("subscription")
                    .Attr("id", "2")
                    .Elem("uri.resource", "http://mock/resource/y")
                    .Elem("channel", "channel:///foo/*")
                    .Elem("uri.proxy", mockRecipient)
                    .Start("recipient").Attr("userid", "1").Elem("uri", "http://recipient/a").End()
                    .Start("recipient").Attr("userid", "2").Elem("uri", "http://recipient/b").End()
                    .Start("recipient").Attr("userid", "3").Elem("uri", "http://recipient/c").End()
                    .Start("recipient").Attr("userid", "4").Elem("uri", "http://recipient/d").End()
                .End();
            DreamMessage result = serviceInfo.WithInternalKey().AtLocalHost.At("subscribers").PostAsync(sub).Wait();
            Assert.IsTrue(result.IsSuccessful);
            XDoc ev = new XDoc("event").Elem("pageid", 42);
            result = serviceInfo.WithInternalKey()
                 .AtLocalHost
                 .At("publish")
                 .WithHeader(DreamHeaders.DREAM_EVENT_CHANNEL, "channel:///foo/bar")
                 .WithHeader(DreamHeaders.DREAM_EVENT_ORIGIN, mockDeki.ToString())
                 .WithHeader(DreamHeaders.DREAM_EVENT_RESOURCE, "http://mock/resource/x")
                 .PostAsync(ev).Wait();
            Assert.IsTrue(result.IsSuccessful);
            Assert.IsTrue(are.WaitOne(500, true));
            Assert.AreEqual(1, dekiCalled);
            Assert.AreEqual(1, dekipage42authCalled);
            Assert.IsTrue(dekiArgsGood);
            Assert.AreEqual(ev, received);
            Assert.AreEqual(2, recipients.Count);
            Assert.Contains("http://recipient/a", recipients);
            Assert.Contains("http://recipient/b", recipients);
            ev = new XDoc("event").Elem("pageid", 43);
            result = serviceInfo.WithInternalKey()
                 .AtLocalHost
                 .At("publish")
                 .WithHeader(DreamHeaders.DREAM_EVENT_CHANNEL, "channel:///foo/bar")
                 .WithHeader(DreamHeaders.DREAM_EVENT_ORIGIN, mockDeki.ToString())
                 .WithHeader(DreamHeaders.DREAM_EVENT_RESOURCE, "http://mock/resource/y")
                 .PostAsync(ev).Wait();
            Assert.IsTrue(result.IsSuccessful);
            Assert.IsTrue(are.WaitOne(5000, true));
            Assert.AreEqual(2, dekiCalled);
            Assert.AreEqual(1, dekipage42authCalled);
            Assert.IsTrue(dekiArgsGood);
            Assert.AreEqual(ev, received);
            Assert.AreEqual(2, recipients.Count);
            Assert.Contains("http://recipient/c", recipients);
            Assert.Contains("http://recipient/d", recipients);
        }
Exemple #13
0
        public void Exporter_can_retrieve_items_by_dataid() {

            // Arrange
            XUri dekiApiUri = new XUri("http://mock/@api/deki");
            XDoc exportDocument = new XDoc("export");
            XUri item1Uri = dekiApiUri.At("foo", "bar", "abc");
            XDoc item1Doc = new XDoc("item1");
            XUri item2Uri = dekiApiUri.At("foo", "bar", "def");
            XDoc item2Doc = new XDoc("item2");
            XDoc exportResponse = new XDoc("export")
                .Start("requests")
                    .Start("request")
                        .Attr("method", "GET")
                        .Attr("dataid", "abc")
                        .Attr("href", item1Uri)
                        .Start("header").Attr("name", "h_1").Attr("value", "v_1").End()
                        .Start("header").Attr("name", "h_2").Attr("value", "v_2").End()
                    .End()
                    .Start("request")
                        .Attr("method", "GET")
                        .Attr("dataid", "def")
                        .Attr("href", item2Uri)
                    .End()
                .End()
                .Start("manifest")
                    .Start("foo").Attr("dataid", "abc").End()
                    .Start("bar").Attr("dataid", "def").End()
                .End();
            AutoMockPlug mock = MockPlug.Register(dekiApiUri);
            mock.Expect().Verb("POST").Uri(dekiApiUri.At("site", "export").With("relto", "0")).RequestDocument(exportDocument).Response(DreamMessage.Ok(exportResponse));
            mock.Expect().Verb("GET").Uri(item1Uri).RequestHeader("h_1", "v_1").RequestHeader("h_2", "v_2").Response(DreamMessage.Ok(item1Doc));
            mock.Expect().Verb("GET").Uri(item2Uri).Response(DreamMessage.Ok(item2Doc));

            // Act
            Exporter exporter = Exporter.CreateAsync(Plug.New(dekiApiUri), exportDocument, 0, new Result<Exporter>()).Wait();
            ExportItem item1 = exporter.GetItemAsync("abc", new Result<ExportItem>()).Wait();
            ExportItem item2 = exporter.GetItemAsync("def", new Result<ExportItem>()).Wait();

            //Assert
            Assert.IsTrue(mock.WaitAndVerify(TimeSpan.FromSeconds(1)));
            Assert.AreEqual(exportResponse["manifest"], exporter.Manifest);
            Assert.AreEqual(new string[] { "abc", "def" }, exporter.DataIds);
            Assert.AreEqual(exporter.Manifest["*[@dataid='abc']"], item1.ItemManifest);
            Assert.AreEqual(exporter.Manifest["*[@dataid='def']"], item2.ItemManifest);
            Assert.AreEqual(item1Doc, XDocFactory.From(new StreamReader(item1.Data), MimeType.TEXT_XML));
            Assert.AreEqual(item2Doc, XDocFactory.From(new StreamReader(item2.Data), MimeType.TEXT_XML));
        }
Exemple #14
0
            //--- Class Methods ---
            internal static AMedia New(XUri uri, XDoc config) {

                // check if the uri is a viddler video
                if(uri.Scheme.EqualsInvariantIgnoreCase("kaltura")) {
                    if(uri.Segments.Length >= 1) {
                        string entryID = uri.Segments[0];
                        string partnerID = config["kaltura/partner-id"].AsText;

                        // check if extension is configured for kaltura integration
                        if(!string.IsNullOrEmpty(partnerID)) {
                            bool remix = !(uri.GetParam("edit", null) ?? uri.GetParam("remix", "no")).EqualsInvariantIgnoreCase("no");

                            // verify that user has permission to remix content on current page
                            if(remix) {
                                Plug dekiApi = GetDekiApi(config);
                                if(dekiApi != null) {
                                    try {
                                        DekiScriptMap env = DreamContext.Current.GetState<DekiScriptMap>();
                                        string pageid = env.GetAt("page.id").AsString();
                                        string userid = env.GetAt("user.id").AsString();
                                        XDoc users = dekiApi.At("pages", pageid, "allowed").With("permissions", "UPDATE").Post(new XDoc("users").Start("user").Attr("id", userid).End()).ToDocument();
                                        remix = !users[string.Format(".//user[@id='{0}']", userid)].IsEmpty;
                                    } catch(Exception e) {
                                        _log.Error("unable to verify user permission on page", e);
                                    }
                                }
                            }

                            // check if SEO links are explicitly disabled
                            bool seo = !(config["kaltura/seo-links"].AsText ?? "enabled").EqualsInvariantIgnoreCase("disabled");

                            // determin which UI configuration to use based on user's permissions and embed settings for video
                            string uiConfID = remix ? config["kaltura/uiconf/player-mix"].AsText : config["kaltura/uiconf/player-nomix"].AsText;
                            if(!string.IsNullOrEmpty(uiConfID)) {
                                uri = config["kaltura/server-uri"].AsUri ?? new XUri("http://www.kaltura.com");
                                uri = uri.At("index.php", "kwidget", "wid", "_" + partnerID, "uiconf_id", uiConfID, "entry_id", entryID);
                                return new KalturaVideo(uri, remix, seo);
                            }
                        }
                    }
                }
                return null;
            }
        public void Importer_Items_are_populated_with_request_and_manifest_docs() {

            // Arrange
            var dekiApiUri = new XUri("http://mock/@api/deki");
            var importManifest = new XDoc("manifest")
                .Start("item").Attr("dataid", "abc").Elem("foo", "bar").End()
                .Start("item").Attr("dataid", "def").Elem("baz", "flip").End();
            var item1Uri = dekiApiUri.At("foo", "bar", "abc");
            var item2Uri = dekiApiUri.At("foo", "bar", "def");
            var importResponse = new XDoc("requests")
                .Start("request")
                    .Attr("method", "POST")
                    .Attr("dataid", "abc")
                    .Attr("href", item1Uri)
                    .Start("header").Attr("name", "h_1").Attr("value", "v_1").End()
                    .Start("header").Attr("name", "h_2").Attr("value", "v_2").End()
                .End()
                .Start("request")
                    .Attr("method", "PUT")
                    .Attr("dataid", "def")
                    .Attr("href", item2Uri)
                .End();
            var mock = MockPlug.Register(dekiApiUri);
            mock.Expect().Verb("POST").Uri(dekiApiUri.At("site", "import").With("relto", "0")).RequestDocument(importManifest).Response(DreamMessage.Ok(importResponse));

            // Act
            var importer = Importer.CreateAsync(Plug.New(dekiApiUri), importManifest, 0, new Result<Importer>()).Wait();

            //Assert
            Assert.IsTrue(mock.WaitAndVerify(TimeSpan.FromSeconds(1)));
            var item1 = importer.Items.Where(x => x.DataId == "abc").FirstOrDefault();
            Assert.IsNotNull(item1);
            Assert.IsNotNull(item1.Manifest);
            Assert.AreEqual(importManifest[".//*[@dataid='abc']"], item1.Manifest);
            Assert.IsNotNull(item1.Request);
            Assert.AreEqual(importResponse[".//*[@dataid='abc']"], item1.Request);
            var item2 = importer.Items.Where(x => x.DataId == "def").FirstOrDefault();
            Assert.IsNotNull(item2);
            Assert.IsNotNull(item2.Manifest);
            Assert.AreEqual(importManifest[".//*[@dataid='def']"], item2.Manifest);
            Assert.IsNotNull(item1.Request);
            Assert.AreEqual(importResponse[".//*[@dataid='def']"], item2.Request);
        }
        public void Importer_can_send_ImportItem_with_xml_body_in_request_doc() {

            // Arrange
            var dekiApiUri = new XUri("http://mock/@api/deki");
            var importManifest = new XDoc("manifest");
            var item1Uri = dekiApiUri.At("foo", "bar", "abc");
            var importResponse = new XDoc("requests");
            var mock = MockPlug.Register(dekiApiUri);
            mock.Expect().Verb("POST").Uri(dekiApiUri.At("site", "import").With("relto", "0")).RequestDocument(importManifest).Response(DreamMessage.Ok(importResponse));
            mock.Expect().Verb("POST").Uri(item1Uri).RequestDocument(new XDoc("item1").Elem("foo", "bar"));

            // Act
            Importer importer = Importer.CreateAsync(Plug.New(dekiApiUri), importManifest, 0, new Result<Importer>()).Wait();
            var item1 = new ImportItem(
                "abc",
                new XDoc("request")
                    .Attr("method", "POST")
                    .Attr("dataid", "abc")
                    .Attr("href", item1Uri)
                    .Start("body")
                        .Attr("type","xml")
                        .Start("item1").Elem("foo","bar").End()
                    .End(),
                new XDoc("manifest"),
                null,
                0);
            importer.WriteDataAsync(item1, new Result()).Wait();

            //Assert
            Assert.IsTrue(mock.WaitAndVerify(TimeSpan.FromSeconds(1)));
        }
        public void User_delete_event_wipes_subscriptions() {

            // set up mocks for all the support service calls
            XUri deki = new XUri("http://mock/deki");
            XUri dekiAuth = deki.At("users");
            AutoResetEvent dekiResetEvent = new AutoResetEvent(false);
            XDoc dekiResponse = null;
            XUri dekiCalledUri = null;
            MockPlug.Register(deki, delegate(Plug p, string v, XUri u, DreamMessage r, Result<DreamMessage> r2) {
                _log.DebugFormat("deki: {0}", u);
                DreamMessage msg;
                if(u.Path.StartsWith("/deki/pages")) {
                    msg = DreamMessage.Ok(new XDoc("users").Start("user").Attr("id", "1").End());
                } else {
                    dekiCalledUri = u;
                    dekiResetEvent.Set();
                    msg = DreamMessage.Ok(dekiResponse);
                }
                msg.Headers.Add("X-Deki-Site", "id=wicked");
                r2.Return(msg);
            });
            XUri email = new XUri("http://mock/email").With("apikey", "123");
            AutoResetEvent emailResetEvent = new AutoResetEvent(false);
            List<XDoc> emailPosted = new List<XDoc>();
            MockPlug.Register(email, delegate(Plug p, string v, XUri u, DreamMessage r, Result<DreamMessage> r2) {
                _log.DebugFormat("email: {0}", u);
                emailPosted.Add(r.ToDocument());
                emailResetEvent.Set();
                r2.Return(DreamMessage.Ok());
            });
            XUri subscribe = new XUri("http://mock/sub");
            XUri subscriptionLocation = subscribe.At("testsub");
            AutoResetEvent subscribeResetEvent = new AutoResetEvent(false);
            XUri subscribeCalledUri = null;
            XDoc subscribePosted = null;
            MockPlug.Register(subscribe, delegate(Plug p, string v, XUri u, DreamMessage r, Result<DreamMessage> r2) {
                subscribeCalledUri = u;
                if(u == subscribe.At("subscribers")) {
                    _log.Debug("creating subscription");
                    DreamMessage msg = DreamMessage.Ok(new XDoc("x"));
                    msg.Headers.Location = subscriptionLocation;
                    subscribeResetEvent.Set();
                    r2.Return(msg);
                } else {
                    _log.Debug("updating subscription");
                    subscribePosted = r.ToDocument();
                    subscribeResetEvent.Set();
                    r2.Return(DreamMessage.Ok());
                }
            });
            XUri storage = new XUri("http://mock/store");
            AutoResetEvent storagePutResetEvent = new AutoResetEvent(false);
            AutoResetEvent storageGetResetEvent = new AutoResetEvent(false);
            AutoResetEvent storageDeleteResetEvent = new AutoResetEvent(false);
            XDoc storageResponse = null;
            List<Tuplet<string, XDoc>> storagePuts = new List<Tuplet<string, XDoc>>();
            int storagePutsExpected = 0;
            int deleteCalled = 0;
            MockPlug.Register(storage, delegate(Plug p, string v, XUri u, DreamMessage r, Result<DreamMessage> r2) {
                _log.DebugFormat("storage: {0} - {1}", v, u);
                if(v == "PUT") {
                    string wikihost = u.Segments[u.Segments.Length - 2];
                    storagePuts.Add(new Tuplet<string, XDoc>(wikihost, r.ToDocument()));
                    if(storagePuts.Count >= storagePutsExpected) {
                        storagePutResetEvent.Set();
                    }
                } else if(v == "DELETE") {
                    deleteCalled++;
                    storageDeleteResetEvent.Set();
                } else if(v == "GET") {
                    storageGetResetEvent.Set();
                }

                r2.Return(DreamMessage.Ok(storageResponse));
            });

            // set up service
            _log.Debug("set up service");
            storageResponse = new XDoc("files");
            DreamServiceInfo serviceInfo = DreamTestHelper.CreateService(
                _hostInfo,
                typeof(DekiChangeSubscriptionService),
                "email",
                new XDoc("config")
                    .Elem("uri.emailer", email)
                    .Elem("uri.deki", deki)
                    .Elem("uri.pubsub", subscribe)
                    .Elem("uri.storage", storage)
                );
            Plug service = serviceInfo.WithInternalKey().AtLocalHost;

            // expect:
            // - storage was queried
            // - subscription was created on subscribe
            Assert.IsTrue(storageGetResetEvent.WaitOne(100, true));
            Assert.IsTrue(subscribeResetEvent.WaitOne(100, true));
            Assert.AreEqual(subscribe.At("subscribers"), subscribeCalledUri);

            // post a subscription
            storagePutsExpected = 1;
            _log.Debug("post a subscription");
            dekiResponse = new XDoc("user")
                .Attr("id", "1")
                .Elem("email", "*****@*****.**")
                .Elem("language", "en")
                .Start("permissions.user")
                    .Elem("operations", "READ,SUBSCRIBE,LOGIN")
                .End();
            DreamMessage response = service
                .At("pages", "10")
                .With("depth", "infinity")
                .WithHeader("X-Deki-Site", "id=wicked")
                .PostAsync()
                .Wait();
            Assert.IsTrue(response.IsSuccessful);

            // expect:
            // - deki was queried for user info
            // - subscription location was updated with new sub
            // - storage was updated with new wiki subscription set
            Assert.IsTrue(storagePutResetEvent.WaitOne(1000, true));
            Assert.IsTrue(subscribeResetEvent.WaitOne(1000, true));
            Assert.IsTrue(dekiResetEvent.WaitOne(1000, true));
            Thread.Sleep(100);
            Assert.AreEqual(dekiAuth.At("current"), dekiCalledUri);
            Assert.AreEqual("wicked", storagePuts[0].Item1);
            Assert.AreEqual(1, storagePuts.Count);
            Assert.AreEqual(subscriptionLocation, subscribeCalledUri);
            Assert.AreEqual(2, subscribePosted["subscription"].ListLength);
            Assert.AreEqual("deki://wicked/pages/10#depth=infinity", subscribePosted["subscription[channel='event://wicked/deki/pages/create']/uri.resource"].AsText);

            // post a user delete event
            _log.Debug("posting a user delete event");
            dekiCalledUri = null;
            storagePuts.Clear();
            subscribeCalledUri = null;
            emailPosted.Clear();
            string channel = "event://wicked/deki/users/delete";
            response = service.At("updateuser")
                .WithHeader(DreamHeaders.DREAM_EVENT_CHANNEL, channel)
                .PostAsync(CreateDekiEvent().Elem("channel", channel).Elem("userid", 1))
                .Wait();
            Assert.IsTrue(response.IsSuccessful);

            // expect:
            // - storage should be called and user is no longer in set
            // - new sub set should be pushed upstream
            Assert.IsTrue(storageDeleteResetEvent.WaitOne(1000, true));
            Assert.IsTrue(subscribeResetEvent.WaitOne(1000, true));
            Assert.IsNull(dekiCalledUri);
            Assert.IsEmpty(emailPosted);
            Assert.AreEqual(1, deleteCalled);
            Assert.AreEqual(subscriptionLocation, subscribeCalledUri);
            Assert.AreEqual(1, subscribePosted["subscription"].ListLength);
        }
Exemple #18
0
 //--- Methods ---
 /// <summary>
 /// Application specific base uri for a given request
 /// </summary>
 /// <param name="request">HttpRequest instance.</param>
 /// <returns>Base Uri.</returns>
 public XUri GetRequestBaseUri(HttpRequest request)
 {
     var transport = new XUri(request.Url).WithoutPathQueryFragment().AtAbsolutePath(request.ApplicationPath);
     var prefix = _appConfig.Prefix;
     if(!string.IsNullOrEmpty(prefix)) {
         transport = transport.At(prefix);
     }
     return transport;
 }
        public void Deleted_page_event_wipes_subscriptions_for_page() {

            // set up mocks for all the support service calls
            XUri deki = new XUri("http://mock/deki");
            XUri dekiAuth = deki.At("users");
            AutoResetEvent dekiUserResetEvent = new AutoResetEvent(false);
            AutoResetEvent dekiPageResetEvent = new AutoResetEvent(false);
            AutoResetEvent dekiSiteResetEvent = new AutoResetEvent(false);
            XDoc dekiUserResponse = null;
            XDoc dekiPageResponse = null;
            XDoc dekiFeedResponse = null;
            XDoc dekiSiteResponse = null;
            XDoc dekiPageRequest = null;
            XUri dekiCalledUri = null;
            MockPlug.Register(deki, delegate(Plug p, string v, XUri u, DreamMessage r, Result<DreamMessage> r2) {
                _log.DebugFormat("deki: {0}", u);
                DreamMessage msg;
                if(u.Path.StartsWith("/deki/pages")) {
                    if(v != "GET") {
                        dekiPageRequest = r.ToDocument();
                    }
                    if(u.Path.EndsWith("feed")) {
                        msg = DreamMessage.Ok(dekiFeedResponse);
                    } else {
                        msg = DreamMessage.Ok(dekiPageResponse);
                    }
                    dekiPageResetEvent.Set();
                } else if(u.Path.StartsWith("/deki/site/settings")) {
                    msg = DreamMessage.Ok(dekiSiteResponse);
                    dekiSiteResetEvent.Set();
                } else {
                    msg = DreamMessage.Ok(dekiUserResponse);
                    dekiUserResetEvent.Set();
                    dekiCalledUri = u;
                }
                msg.Headers.Add("X-Deki-Site", "id=wicked");
                r2.Return(msg);
            });
            XUri email = new XUri("http://mock/email").With("apikey", "123");
            AutoResetEvent emailResetEvent = new AutoResetEvent(false);
            List<XDoc> emailPosted = new List<XDoc>();
            MockPlug.Register(email, delegate(Plug p, string v, XUri u, DreamMessage r, Result<DreamMessage> r2) {
                _log.DebugFormat("email: {0}", u);
                emailPosted.Add(r.ToDocument());
                emailResetEvent.Set();
                r2.Return(DreamMessage.Ok());
            });
            XUri subscribe = new XUri("http://mock/sub");
            XUri subscriptionLocation = subscribe.At("testsub");
            AutoResetEvent subscribeResetEvent = new AutoResetEvent(false);
            XUri subscribeCalledUri = null;
            XDoc subscribePosted = null;
            MockPlug.Register(subscribe, delegate(Plug p, string v, XUri u, DreamMessage r, Result<DreamMessage> r2) {
                subscribeCalledUri = u;
                if(u == subscribe.At("subscribers")) {
                    _log.Debug("creating subscription");
                    DreamMessage msg = DreamMessage.Ok(new XDoc("x"));
                    msg.Headers.Location = subscriptionLocation;
                    subscribeResetEvent.Set();
                    r2.Return(msg);
                } else {
                    _log.Debug("updating subscription");
                    subscribePosted = r.ToDocument();
                    subscribeResetEvent.Set();
                    r2.Return(DreamMessage.Ok());
                }
            });
            XUri storage = new XUri("http://mock/store");
            AutoResetEvent storagePutResetEvent = new AutoResetEvent(false);
            AutoResetEvent storageGetResetEvent = new AutoResetEvent(false);
            AutoResetEvent storageDeleteResetEvent = new AutoResetEvent(false);
            XDoc storageResponse = null;
            List<Tuplet<string, XDoc>> storagePuts = new List<Tuplet<string, XDoc>>();
            int storagePutsExpected = 0;
            XUri storageGetCalled = null;
            int deleteCalled = 0;
            MockPlug.Register(storage, delegate(Plug p, string v, XUri u, DreamMessage r, Result<DreamMessage> r2) {
                _log.DebugFormat("storage: {0} - {1}", v, u);
                if(v == "PUT") {
                    string wikihost = u.Segments[u.Segments.Length - 2];
                    storagePuts.Add(new Tuplet<string, XDoc>(wikihost, r.ToDocument()));
                    if(storagePuts.Count >= storagePutsExpected) {
                        storagePutResetEvent.Set();
                    }
                } else if(v == "DELETE") {
                    deleteCalled++;
                    storageDeleteResetEvent.Set();
                } else if(v == "GET") {
                    storageGetCalled = u;
                    storageGetResetEvent.Set();
                }
                r2.Return(DreamMessage.Ok(storageResponse));
            });

            // set up service
            _log.Debug("set up service");
            storageResponse = new XDoc("files");
            DreamServiceInfo serviceInfo = DreamTestHelper.CreateService(
                _hostInfo,
                typeof(DekiChangeSubscriptionService),
                "email",
                new XDoc("config")
                    .Elem("uri.emailer", email)
                    .Elem("uri.deki", deki)
                    .Elem("uri.pubsub", subscribe)
                    .Elem("uri.storage", storage)
                    .Elem("accumulation-time", 0)
                    .Elem("from-address", "*****@*****.**")
                );
            Plug service = serviceInfo.WithInternalKey().AtLocalHost;

            // expect:
            // - storage was queried
            // - subscription was created on subscribe
            Assert.IsTrue(storageGetResetEvent.WaitOne(100, true));
            Assert.IsTrue(subscribeResetEvent.WaitOne(100, true));
            Assert.AreEqual(storage.At("subscriptions"), storageGetCalled);
            Assert.AreEqual(subscribe.At("subscribers"), subscribeCalledUri);

            // post a subscription
            _log.Debug("post a subscription");
            storagePutsExpected = 1;
            dekiUserResponse = new XDoc("user")
                .Attr("id", "1")
                .Elem("email", "*****@*****.**")
                .Elem("language", "en")
                .Start("permissions.user")
                    .Elem("operations", "READ,SUBSCRIBE,LOGIN")
                .End();
            dekiPageResponse = new XDoc("users").Start("user").Attr("id", "1").End();
            DreamMessage response = service
                .At("pages", "10")
                .With("depth", "infinity")
                .WithHeader("X-Deki-Site", "id=wicked")
                .PostAsync()
                .Wait();
            Assert.IsTrue(response.IsSuccessful);

            // expect:
            // - deki was queried for user info
            // - subscription location was updated with new sub
            // - storage was updated with new wiki subscription set
            Assert.IsTrue(storagePutResetEvent.WaitOne(1000, true));
            Assert.IsTrue(subscribeResetEvent.WaitOne(1000, true));
            Assert.IsTrue(dekiUserResetEvent.WaitOne(1000, true));
            Assert.AreEqual(dekiAuth.At("current"), dekiCalledUri);
            Assert.AreEqual("wicked", storagePuts[0].Item1);
            Assert.AreEqual(1, storagePuts.Count);
            Assert.AreEqual(subscriptionLocation, subscribeCalledUri);
            Assert.AreEqual(2, subscribePosted["subscription"].ListLength);
            Assert.AreEqual("deki://wicked/pages/10#depth=infinity", subscribePosted["subscription[channel='event://wicked/deki/pages/create']/uri.resource"].AsText);

            // post a page deleted event
            _log.Debug("posting a page deleted event");
            dekiCalledUri = null;
            storagePutsExpected = 1;
            subscribeCalledUri = null;
            string channel = "event://wicked/deki/pages/delete";
            dekiPageResponse = new XDoc("page")
                .Elem("uri.ui", "http://foo.com/@api/deki/pages/10")
                .Elem("title", "foo")
                .Elem("path", "foo/bar");
            dekiFeedResponse = new XDoc("table")
                .Start("change")
                .Elem("rc_summary", "Two edits")
                .Elem("rc_comment", "edit 1")
                .Elem("rc_comment", "edit 2")
                .End();
            dekiSiteResponse = new XDoc("config")
                .Start("ui")
                    .Elem("sitename", "Test Site")
                    .Elem("language", "de-de")
                .End()
                .Start("page-subscription")
                    .Elem("from-address", "*****@*****.**")
                .End();
            response = service.At("notify")
                .WithHeader(DreamHeaders.DREAM_EVENT_CHANNEL, channel)
                .WithHeader(DreamHeaders.DREAM_EVENT_RECIPIENT, "deki://wicked/user/1")
                .WithHeader(DreamHeaders.DREAM_EVENT_RECIPIENT, "deki://wicked/user/2")
                .PostAsync(CreateDekiEvent().Elem("channel", channel).Elem("pageid", 10))
                .Wait();
            Assert.IsTrue(response.IsSuccessful);

            // expect:
            // - email service is called for user
            // - storage is called with subscription (and user) removed
            // - subscription is pushed upstream
            Assert.IsTrue(emailResetEvent.WaitOne(1000, true));
            Assert.IsTrue(storageDeleteResetEvent.WaitOne(1000, true));
            Assert.IsTrue(subscribeResetEvent.WaitOne(1000, true));
            Assert.IsTrue(dekiSiteResetEvent.WaitOne(1000, true));
            Assert.IsNull(dekiCalledUri);
            Assert.AreEqual(1, emailPosted.Count);
            Assert.AreEqual("*****@*****.**", emailPosted[0]["to"].AsText);
            Assert.AreEqual(1, deleteCalled);
            Assert.AreEqual(subscriptionLocation, subscribeCalledUri);
            Assert.AreEqual(1, subscribePosted["subscription"].ListLength);
        }
Exemple #20
0
 private static void PopulateActivities(XDoc doc, XUri self, DateTime now, IDreamActivityDescription[] activities)
 {
     doc.Attr("count", activities.Length).Attr("href", self.At("status", "activities"));
     foreach(var description in activities) {
         doc.Start("description").Attr("created", description.Created).Attr("age", (now - description.Created).TotalSeconds).Value(description.Description).End();
     }
 }
        public void Subscription_end_to_end() {

            // set up mocks for all the support service calls
            string apikey = "abc";
            XUri deki = new XUri("http://mock/deki");
            XUri dekiAuth = deki.At("users");
            var dekiUserResetEvent = new ManualResetEvent(false);
            var dekiPageResetEvent = new ManualResetEvent(false);
            var dekiSiteResetEvent = new ManualResetEvent(false);
            var subscribeResetEvent = new ManualResetEvent(false);
            var storagePutResetEvent = new ManualResetEvent(false);
            var storageGetResetEvent = new ManualResetEvent(false);
            var storageDeleteResetEvent = new ManualResetEvent(false);
            var emailResetEvent = new ManualResetEvent(false);
            Action reset = () => {
                dekiUserResetEvent.Reset();
                dekiPageResetEvent.Reset();
                dekiSiteResetEvent.Reset();
                subscribeResetEvent.Reset();
                storagePutResetEvent.Reset();
                storageGetResetEvent.Reset();
                storageDeleteResetEvent.Reset();
                emailResetEvent.Reset();
            };
            XDoc dekiUserResponse = null;
            XDoc dekiPageResponse = null;
            XDoc dekiFeedResponse = null;
            XDoc dekiSiteResponse = null;
            XDoc dekiPageRequest = null;
            XUri dekiCalledUri = null;
            MockPlug.Register(deki, delegate(Plug p, string v, XUri u, DreamMessage r, Result<DreamMessage> r2) {
                _log.DebugFormat("deki: {0}", u);
                DreamMessage msg;
                if(u.Path.StartsWith("/deki/pages")) {
                    if(v != "GET") {
                        dekiPageRequest = r.ToDocument();
                    }
                    if(u.Path.EndsWith("feed")) {
                        msg = DreamMessage.Ok(dekiFeedResponse);
                    } else {
                        msg = DreamMessage.Ok(dekiPageResponse);
                    }
                    dekiPageResetEvent.Set();
                } else if(u.Path.StartsWith("/deki/site/settings")) {
                    msg = DreamMessage.Ok(dekiSiteResponse);
                    dekiSiteResetEvent.Set();
                } else {
                    msg = DreamMessage.Ok(dekiUserResponse);
                    dekiUserResetEvent.Set();
                    dekiCalledUri = u;
                }
                msg.Headers.Add("X-Deki-Site", "id=wicked");
                r2.Return(msg);
            });
            XUri email = new XUri("http://mock/email").With("apikey", "123");
            List<XDoc> emailPosted = new List<XDoc>();
            int emailsExpected = 0;
            MockPlug.Register(email, delegate(Plug p, string v, XUri u, DreamMessage r, Result<DreamMessage> r2) {
                _log.DebugFormat("email: {0}", u);
                emailPosted.Add(r.ToDocument());
                if(emailPosted.Count >= emailsExpected) {
                    emailResetEvent.Set();
                }
                r2.Return(DreamMessage.Ok());
            });
            XUri subscribe = new XUri("http://mock/sub");
            XUri subscriptionLocation = subscribe.At("testsub");
            XUri subscribeCalledUri = null;
            XDoc subscribePosted = null;
            MockPlug.Register(subscribe, delegate(Plug p, string v, XUri u, DreamMessage r, Result<DreamMessage> r2) {
                subscribeCalledUri = u;
                if(u == subscribe.At("subscribers")) {
                    _log.Debug("creating subscription");
                    DreamMessage msg = DreamMessage.Ok(new XDoc("x"));
                    msg.Headers.Location = subscriptionLocation;
                    subscribeResetEvent.Set();
                    r2.Return(msg);
                } else {
                    _log.Debug("updating subscription");
                    subscribePosted = r.ToDocument();
                    subscribeResetEvent.Set();
                    r2.Return(DreamMessage.Ok());
                }
            });
            XUri storage = new XUri("http://mock/store");
            XDoc storageResponse = null;
            List<Tuplet<string, XDoc>> storagePuts = new List<Tuplet<string, XDoc>>();
            int storagePutsExpected = 1;
            int deleteCalled = 0;
            MockPlug.Register(storage, delegate(Plug p, string v, XUri u, DreamMessage r, Result<DreamMessage> r2) {
                _log.DebugFormat("storage: {0} - {1}", v, u);
                if(v == "PUT") {
                    string wikihost = u.Segments[u.Segments.Length - 2];
                    storagePuts.Add(new Tuplet<string, XDoc>(wikihost, r.ToDocument()));
                    if(storagePuts.Count >= storagePutsExpected) {
                        storagePutResetEvent.Set();
                    }
                } else if(v == "DELETE") {
                    deleteCalled++;
                    storageDeleteResetEvent.Set();
                } else if(v == "GET") {
                    storageGetResetEvent.Set();
                }

                r2.Return(DreamMessage.Ok(storageResponse));
            });
            // set up service
            _log.Debug("set up service");
            storageResponse = new XDoc("files");
            DreamServiceInfo serviceInfo = DreamTestHelper.CreateService(
                _hostInfo,
                typeof(DekiChangeSubscriptionService),
                "email",
                new XDoc("config")
                    .Elem("uri.emailer", email)
                    .Elem("uri.deki", deki)
                    .Elem("uri.pubsub", subscribe)
                    .Elem("uri.storage", storage)
                    .Elem("accumulation-time", 0)
                    .Elem("from-address", "*****@*****.**")
                    .Elem("apikey", apikey)
                );
            Plug service = serviceInfo.WithInternalKey().AtLocalHost;

            // expect:
            // - storage was queried
            // - subscription was created on subscribe
            Assert.IsTrue(Wait.For(() => storageGetResetEvent.WaitOne(100, true), 10.Seconds()));
            Assert.IsTrue(Wait.For(() => subscribeResetEvent.WaitOne(100, true), 10.Seconds()));
            Assert.AreEqual(subscribe.At("subscribers"), subscribeCalledUri);
            reset();

            // post a subscription
            _log.Debug("post a subscription");
            dekiUserResponse = new XDoc("user")
                .Attr("id", "1")
                .Elem("email", "*****@*****.**")
                .Elem("language", "en")
                .Start("permissions.user")
                    .Elem("operations", "READ,SUBSCRIBE,LOGIN")
                .End();
            dekiPageResponse = new XDoc("users").Start("user").Attr("id", 1).End();
            DreamMessage response = service
                .At("pages", "10")
                .With("depth", "infinity")
                .WithHeader("X-Deki-Site", "id=wicked")
                .PostAsync()
                .Wait();
            Assert.IsTrue(response.IsSuccessful, response.AsText());

            // expect:
            // - deki was queried for user info
            // - subscription location was updated with new sub
            // - storage was updated with new wiki subscription set
            Assert.IsTrue(Wait.For(() => storagePutResetEvent.WaitOne(100, true), 10.Seconds()));
            Assert.IsTrue(Wait.For(() => subscribeResetEvent.WaitOne(100, true), 10.Seconds()));
            Assert.IsTrue(Wait.For(() => dekiUserResetEvent.WaitOne(100, true), 10.Seconds()));
            Assert.IsTrue(Wait.For(() => dekiPageResetEvent.WaitOne(100, true), 10.Seconds()));
            Assert.AreEqual(1, dekiPageRequest["user/@id"].AsUInt.Value);
            Assert.AreEqual(dekiAuth.At("current"), dekiCalledUri);
            Assert.AreEqual(1, storagePuts.Count);
            Assert.AreEqual("wicked", storagePuts[0].Item1);
            Assert.AreEqual(subscriptionLocation, subscribeCalledUri);
            Assert.AreEqual(2, subscribePosted["subscription"].ListLength);
            Assert.AreEqual("deki://wicked/pages/10#depth=infinity", subscribePosted["subscription[channel='event://wicked/deki/pages/create']/uri.resource"].AsText);
            reset();

            //post another subscription
            _log.Debug("post another subscription");
            storagePuts.Clear();
            subscribePosted = null;
            dekiCalledUri = null;
            subscribeCalledUri = null;
            dekiPageRequest = null;
            dekiUserResponse = new XDoc("user")
                .Attr("id", "2")
                .Elem("email", "*****@*****.**")
                .Elem("language", "en")
                .Start("permissions.user")
                    .Elem("operations", "READ,SUBSCRIBE,LOGIN")
                .End();
            dekiPageResponse = new XDoc("users").Start("user").Attr("id", 2).End();
            response = service
                .At("pages", "10")
                .With("depth", "infinity")
                .WithHeader("X-Deki-Site", "id=wicked")
                .PostAsync()
                .Wait();
            Assert.IsTrue(response.IsSuccessful);

            // expect:
            // - deki was queried for user info
            // - subscription location was updated with new sub
            // - storage was updated with new wiki subscription set
            Assert.IsTrue(Wait.For(() => storagePutResetEvent.WaitOne(100, true), 10.Seconds()));
            Assert.IsTrue(Wait.For(() => subscribeResetEvent.WaitOne(100, true), 10.Seconds()));
            Assert.IsTrue(Wait.For(() => dekiUserResetEvent.WaitOne(100, true), 10.Seconds()));
            Assert.IsTrue(Wait.For(() => dekiPageResetEvent.WaitOne(100, true), 10.Seconds()));
            Assert.AreEqual(2, dekiPageRequest["user/@id"].AsUInt.Value);
            Assert.AreEqual(dekiAuth.At("current"), dekiCalledUri);
            Assert.AreEqual("wicked", storagePuts[0].Item1);
            Assert.AreEqual(1, storagePuts.Count);
            Assert.AreEqual(subscriptionLocation, subscribeCalledUri);
            Assert.AreEqual(2, subscribePosted["subscription"].ListLength);
            Assert.AreEqual(2, subscribePosted["subscription[channel='event://wicked/deki/pages/create']/recipient"].ListLength);
            reset();

            // post a page event
            _log.Debug("posting a page event");
            dekiCalledUri = null;
            subscribeCalledUri = null;
            string channel = "event://wicked/deki/pages/update";
            emailPosted.Clear();
            emailsExpected = 2;
            dekiPageResponse = new XDoc("page")
                .Elem("uri.ui", "http://foo.com/@api/deki/pages/10")
                .Elem("title", "foo")
                .Elem("path", "foo/bar");
            dekiFeedResponse = new XDoc("table")
                .Start("change")
                .Elem("rc_summary", "Two edits")
                .Elem("rc_comment", "edit 1")
                .Elem("rc_comment", "edit 2")
                .End();
            dekiSiteResponse = new XDoc("config")
                .Start("ui")
                    .Elem("sitename", "Test Site")
                    .Elem("language", "de-de")
                .End()
                .Start("page-subscription")
                    .Elem("from-address", "*****@*****.**")
                .End();
            response = service.At("notify")
                .WithHeader(DreamHeaders.DREAM_EVENT_CHANNEL, channel)
                .WithHeader(DreamHeaders.DREAM_EVENT_RECIPIENT, "deki://wicked/user/1")
                .WithHeader(DreamHeaders.DREAM_EVENT_RECIPIENT, "deki://wicked/user/2")
                .PostAsync(CreateDekiEvent().Elem("channel", channel).Elem("pageid", 10))
                .Wait();
            Assert.IsTrue(response.IsSuccessful);

            // expect:
            // - email service is called for both users
            Assert.IsTrue(Wait.For(() => emailResetEvent.WaitOne(100, true), 10.Seconds()));
            Assert.IsTrue(Wait.For(() => dekiSiteResetEvent.WaitOne(100, true), 10.Seconds()));
            Assert.IsNull(dekiCalledUri);
            Assert.IsNull(subscribeCalledUri);
            Assert.AreEqual(2, emailPosted.Count);
            bool found1 = false;
            bool found2 = false;
            foreach(XDoc emailDoc in emailPosted) {
                XDoc para = emailDoc["body[@html='true']/p"];
                Assert.AreEqual("http://foo.com/@api/deki/pages/10", para["b/a/@href"].AsText);
                para = para.Next;
                Assert.AreEqual("<li>edit 1 ( <a href=\"http://foo.com/@api/deki/pages/10?revision\">Mon, 01 Jan 0001 00:00:00 GMT</a> by <a href=\"http://foo.com/User%3a\" /> )</li>", para["ol/li"].ToString());
                if(emailDoc["to"].AsText == "*****@*****.**") {
                    found1 = true;
                } else if(emailDoc["to"].AsText == "*****@*****.**") {
                    found2 = true;
                }
            }
            Assert.IsTrue(found1);
            Assert.IsTrue(found2);
            reset();

            // post a user update event
            _log.Debug("posting a user update event");
            dekiCalledUri = null;
            storagePuts.Clear();
            subscribeCalledUri = null;
            emailPosted.Clear();
            channel = "event://wicked/deki/users/update";
            response = service.At("updateuser")
                .WithHeader(DreamHeaders.DREAM_EVENT_CHANNEL, channel)
                .PostAsync(CreateDekiEvent().Elem("channel", channel).Elem("userid", 1))
                .Wait();
            Assert.IsTrue(response.IsSuccessful);

            // expect:
            // - nothing, user should be invalidated but no action should be taken on it
            Thread.Sleep(500); // give 'nothing' a chance to happen anyhow
            Assert.IsNull(dekiCalledUri);
            Assert.IsNull(subscribeCalledUri);
            Assert.IsEmpty(emailPosted);
            reset();

            // post another page event
            _log.Debug("posting another page event");
            dekiUserResponse = new XDoc("user")
                .Attr("id", "1")
                .Elem("email", "*****@*****.**")
                .Elem("language", "en")
                .Start("permissions.user")
                    .Elem("operations", "READ,SUBSCRIBE,LOGIN")
                .End();
            channel = "event://wicked/deki/pages/update";
            emailPosted.Clear();
            emailsExpected = 2;
            response = service.At("notify")
                .WithHeader(DreamHeaders.DREAM_EVENT_CHANNEL, channel)
                .WithHeader(DreamHeaders.DREAM_EVENT_RECIPIENT, "http://wicked/user/1")
                .WithHeader(DreamHeaders.DREAM_EVENT_RECIPIENT, "http://wicked/user/2")
                .PostAsync(CreateDekiEvent().Elem("channel", channel).Elem("pageid", 10))
                .Wait();
            Assert.IsTrue(response.IsSuccessful);

            // expect:
            // - deki is queried for invalidated user info
            // - storage was updated with new wiki subscription set
            // - email service is called for both users
            Assert.IsTrue(Wait.For(() => emailResetEvent.WaitOne(100, true), 10.Seconds()));
            Assert.IsTrue(Wait.For(() => dekiUserResetEvent.WaitOne(100, true), 10.Seconds()));
            Assert.IsTrue(Wait.For(() => storagePutResetEvent.WaitOne(100, true), 10.Seconds()));
            Assert.IsNull(subscribeCalledUri);
            Assert.AreEqual(dekiAuth.At("1").With("apikey", apikey), dekiCalledUri);
            Assert.AreEqual("wicked", storagePuts[0].Item1);
            Assert.AreEqual(1, storagePuts.Count);
            Assert.AreEqual(2, emailPosted.Count);
            found1 = false;
            found2 = false;
            foreach(XDoc emailDoc in emailPosted) {
                if(emailDoc["to"].AsText == "*****@*****.**") {
                    found1 = true;
                } else if(emailDoc["to"].AsText == "*****@*****.**") {
                    found2 = true;
                }
            }
            Assert.IsTrue(found1);
            Assert.IsTrue(found2);
            reset();

            // remove a subscription
            _log.Debug("remove a subscription");
            dekiCalledUri = null;
            storagePuts.Clear();
            subscribeCalledUri = null;
            dekiUserResponse = new XDoc("user")
                .Attr("id", "1")
                .Elem("email", "*****@*****.**")
                .Elem("language", "en")
                .Start("permissions.user")
                    .Elem("operations", "READ,SUBSCRIBE,LOGIN")
                .End();
            response = service.At("pages", "10")
                .WithHeader("X-Deki-Site", "id=wicked")
                .DeleteAsync()
                .Wait();
            Assert.IsTrue(response.IsSuccessful);

            // expect:
            // - deki was queried to get user info
            // - subscription location was updated with new sub
            // - storage was updated with new wiki subscription set
            Assert.IsTrue(Wait.For(() => storageDeleteResetEvent.WaitOne(100, true), 10.Seconds()));
            Assert.IsTrue(Wait.For(() => subscribeResetEvent.WaitOne(100, true), 10.Seconds()));
            Assert.IsTrue(Wait.For(() => dekiUserResetEvent.WaitOne(100, true), 10.Seconds()));
            Assert.AreEqual(dekiAuth.At("current"), dekiCalledUri);
            Assert.AreEqual(1, deleteCalled);
            Assert.AreEqual(subscriptionLocation, subscribeCalledUri);
            Assert.AreEqual(2, subscribePosted["subscription"].ListLength);
            Assert.AreEqual(1, subscribePosted["subscription[channel='event://wicked/deki/pages/create']/recipient"].ListLength);
            Assert.AreEqual("2", subscribePosted["subscription/recipient/@userid"].AsText);
            reset();

            // post a page event
            _log.Debug("posting a final page event");
            dekiCalledUri = null;
            subscribeCalledUri = null;
            emailPosted.Clear();
            channel = "event://wicked/deki/pages/update";
            emailsExpected = 1;
            response = service.At("notify")
                .WithHeader(DreamHeaders.DREAM_EVENT_CHANNEL, channel)
                .WithHeader(DreamHeaders.DREAM_EVENT_RECIPIENT, "http://wicked/user/2")
                .PostAsync(CreateDekiEvent().Elem("channel", channel).Elem("pageid", 10))
                .Wait();
            Assert.IsTrue(response.IsSuccessful);

            // expect:
            // - email service is called for remaining user
            Assert.IsTrue(Wait.For(() => emailResetEvent.WaitOne(100, true), 10.Seconds()));
            Assert.IsNull(dekiCalledUri);
            Assert.IsNull(subscribeCalledUri);
            Assert.AreEqual(1, emailPosted.Count);
            Assert.AreEqual("*****@*****.**", emailPosted[0]["to"].AsText);
            reset();
        }
 public void Providing_externalLuceneUri_posts_dekipubsub_plug_info_on_that_service() {
     var mockLuceneUri = new XUri("http://mock/lucene");
     var mockLucene = MockPlug.Register(mockLuceneUri);
     XDoc pubsubPlugInfo = null;
     mockLucene.Expect()
         .Verb("POST")
         .Uri(mockLuceneUri.At("subscriptions")).RequestDocument(x => { pubsubPlugInfo = x; return true; })
         .Response(DreamMessage.Ok());
     var dekiConfig = new XDoc("config")
             .Elem("apikey", "123")
             .Elem("path", "deki")
             .Elem("sid", "http://services.mindtouch.com/deki/draft/2006/11/dekiwiki")
             .Elem("deki-path", Utils.Settings.DekiPath)
             .Elem("deki-resources-path", Utils.Settings.DekiResourcesPath)
             .Elem("imagemagick-convert-path", Utils.Settings.ImageMagickConvertPath)
             .Elem("imagemagick-identify-path", Utils.Settings.ImageMagickIdentifyPath)
             .Elem("princexml-path", Utils.Settings.PrinceXmlPath)
             .Start("indexer").Attr("src", mockLuceneUri).End()
             .Start("page-subscription")
                 .Elem("accumulation-time", "0")
             .End()
             .Start("wikis")
                 .Start("config")
                     .Attr("id", "default")
                     .Elem("host", "*")
                     .Start("page-subscription")
                         .Elem("from-address", "*****@*****.**")
                     .End()
                     .Elem("db-server", "na")
                     .Elem("db-port", "3306")
                     .Elem("db-catalog", "wikidb")
                     .Elem("db-user", "wikiuser")
                     .Start("db-password").Attr("hidden", "true").Value("password").End()
                     .Elem("db-options", "pooling=true; Connection Timeout=5; Protocol=socket; Min Pool Size=2; Max Pool Size=50; Connection Reset=false;character set=utf8;ProcedureCacheSize=25;Use Procedure Bodies=true;")
                 .End()
             .End();
     var apikey = dekiConfig["apikey"].AsText;
     var hostInfo = DreamTestHelper.CreateRandomPortHost(new XDoc("config").Elem("apikey", apikey));
     hostInfo.Host.Self.At("load").With("name", "mindtouch.deki").Post(DreamMessage.Ok());
     hostInfo.Host.Self.At("load").With("name", "mindtouch.deki.services").Post(DreamMessage.Ok());
     var deki = DreamTestHelper.CreateService(hostInfo, dekiConfig);
     Assert.IsTrue(mockLucene.WaitAndVerify(TimeSpan.FromSeconds(10)), mockLucene.VerificationFailure);
     var pubsubPlug = Plug.New(pubsubPlugInfo["@href"].AsUri);
     foreach(var header in pubsubPlugInfo["header"]) {
         pubsubPlug.WithHeader(header["name"].AsText, header["value"].AsText);
     }
     var setCookies = DreamCookie.ParseAllSetCookieNodes(pubsubPlugInfo["set-cookie"]);
     if(setCookies.Count > 0) {
         pubsubPlug.CookieJar.Update(setCookies, null);
     }
     var subscriptionSet = new XDoc("subscription-set")
          .Elem("uri.owner", mockLuceneUri)
          .Start("subscription")
              .Elem("channel", "event://*/foo")
              .Start("recipient")
                  .Attr("authtoken", apikey)
                  .Elem("uri", mockLuceneUri)
              .End()
          .End();
     var subscription = pubsubPlug.At("subscribers").Post(subscriptionSet);
     Assert.AreEqual(DreamStatus.Created, subscription.Status);
 }
        public void Retrieve_subscriptions_with_wikiid_query_arg() {
            // set up mocks for all the support service calls
            XUri deki = new XUri("http://mock/deki");
            MockPlug.Register(deki, delegate(Plug p, string v, XUri u, DreamMessage r, Result<DreamMessage> r2) {
                _log.DebugFormat("deki: {0}", u);
                DreamMessage msg;
                if(u.Path.StartsWith("/deki/pages")) {
                    msg = DreamMessage.Ok(new XDoc("users").Start("user").Attr("id", "1").End());
                } else {
                    msg = DreamMessage.Ok(new XDoc("user")
                        .Attr("id", "1")
                        .Elem("email", "*****@*****.**")
                        .Elem("language", "en")
                        .Start("permissions.user")
                            .Elem("operations", "READ,SUBSCRIBE,LOGIN")
                        .End());
                }
                msg.Headers.Add("X-Deki-Site", "id=wicked");
                r2.Return(msg);
            });
            XUri subscribe = new XUri("http://mock/sub");
            MockPlug.Register(subscribe, delegate(Plug p, string v, XUri u, DreamMessage r, Result<DreamMessage> r2) {
                if(u == subscribe.At("subscribers")) {
                    _log.Debug("creating subscription");
                    DreamMessage msg = DreamMessage.Ok(new XDoc("x"));
                    msg.Headers.Location = subscribe.At("testsub");
                    r2.Return(msg);
                } else {
                    _log.Debug("updating subscription");
                    r2.Return(DreamMessage.Ok());
                }
            });
            XUri storage = new XUri("http://mock/store");
            MockPlug.Register(storage, delegate(Plug p, string v, XUri u, DreamMessage r, Result<DreamMessage> r2) {
                _log.DebugFormat("storage: {0}", u);
                r2.Return(DreamMessage.Ok(new XDoc("foo")));
            });

            // set up service
            _log.Debug("set up service");
            DreamServiceInfo serviceInfo = DreamTestHelper.CreateService(
                _hostInfo,
                typeof(DekiChangeSubscriptionService),
                "email",
                new XDoc("config")
                    .Elem("uri.emailer", new XUri("http://mock/email"))
                    .Elem("uri.deki", deki)
                    .Elem("uri.pubsub", subscribe)
                    .Elem("uri.storage", storage)
                );
            Plug service = serviceInfo.WithInternalKey().AtLocalHost;

            // post a subscription
            _log.Debug("post page 10 subscription");
            DreamMessage response = service
                .At("pages", "10")
                .With("depth", "infinity")
                .WithHeader("X-Deki-Site", "id=wicked")
                .PostAsync()
                .Wait();
            Assert.IsTrue(response.IsSuccessful);

            // post a subscription
            _log.Debug("post page 11 subscription");
            response = service
                .At("pages", "11")
                .With("siteid", "wicked")
                .PostAsync()
                .Wait();
            Assert.IsTrue(response.IsSuccessful);
            // post a subscription
            _log.Debug("post page 12 subscription");
            response = service
                .At("pages", "12")
                .With("depth", "0")
                .With("siteid", "wicked")
                .PostAsync()
                .Wait();
            Assert.IsTrue(response.IsSuccessful);
            // post a subscription
            _log.Debug("post page 13 subscription");
            response = service
                .At("pages", "13")
                .With("depth", "infinity")
                .With("siteid", "wicked")
                .PostAsync()
                .Wait();
            Assert.IsTrue(response.IsSuccessful);

            _log.Debug("get some subscriptions");
            response = service
                .At("subscriptions")
                .With("pages", "10,12,14,16")
                .With("siteid", "wicked")
                .GetAsync()
                .Wait();
            Assert.IsTrue(response.IsSuccessful);
            XDoc subscriptions = response.ToDocument();
            Assert.AreEqual(2, subscriptions["subscription.page"].ListLength);
            XDoc page10 = subscriptions["subscription.page[@id='10']"];
            Assert.IsFalse(page10.IsEmpty);
            Assert.AreEqual("infinity", page10["@depth"].AsText);
            XDoc page12 = subscriptions["subscription.page[@id='12']"];
            Assert.IsFalse(page12.IsEmpty);
            Assert.AreEqual("0", page12["@depth"].AsText);

            _log.Debug("get all subscriptions");
            response = service
                .At("subscriptions")
                .With("siteid", "wicked")
                .GetAsync()
                .Wait();
            Assert.IsTrue(response.IsSuccessful);
            subscriptions = response.ToDocument();
            Assert.AreEqual(4, subscriptions["subscription.page"].ListLength);

        }
        protected override Yield Start(XDoc config, Result result) {
            yield return Coroutine.Invoke(base.Start, config, new Result());

            // read configuration settings
            _username = config["username"].AsText;
            if (string.IsNullOrEmpty(_username)) {
                throw new ArgumentException(MISSING_FIELD_ERROR, "username");
            }
            _password = config["password"].AsText;
            if (string.IsNullOrEmpty(_password)) {
                throw new ArgumentException(MISSING_FIELD_ERROR, "password");
            }
            _uri = config["jira-uri"].AsUri;
            if (_uri == null) {
                throw new ArgumentException(MISSING_FIELD_ERROR, "jira-uri");
            }

            _jiraTokenDuration = TimeSpan.FromMinutes(config["jira-session-timeout-mins"].AsInt ?? DEFAULT_LOGIN_TTL);

            // initialize web-service
            _jira = new JiraSoapServiceService();
            _jira.Url = _uri.At("rpc", "soap", "jirasoapservice-v2").ToString();
            result.Return();
        }
        public Hashtable PopularPages(
            [DekiExtParam("max results (default: 10)", true)] int? max,
            [DekiExtParam("poll interval (only for js format, default: 30)", true)] int? interval
        ) {
            int maxResults = max ?? 10;
            int resultCount = 0;
            DekiScriptMap env = DreamContext.Current.GetState<DekiScriptMap>();
            DekiScriptLiteral uriLiteral = env.GetAt("site.uri");
            XUri deki = new XUri(uriLiteral.NativeValue.ToString()).At("@api", "deki");
            Hashtable map = new Hashtable(StringComparer.OrdinalIgnoreCase);
            map.Add("interval", _ttl.TotalSeconds);
            ArrayList pages = new ArrayList();
            map.Add("pages", pages);
            int total = 0;
            Dictionary<uint, int> rankLookup = new Dictionary<uint, int>();
            lock(_pageViews) {
                foreach(View view in _pageViews) {
                    if(rankLookup.ContainsKey(view.PageId)) {
                        rankLookup[view.PageId]++;
                    } else {
                        rankLookup[view.PageId] = 1;
                    }
                    total++;
                }
            }
            List<Tuplet<uint, int>> rank = new List<Tuplet<uint, int>>();
            foreach(KeyValuePair<uint, int> kvp in rankLookup) {
                rank.Add(new Tuplet<uint, int>(kvp.Key, kvp.Value));
            }
            rank.Sort(delegate(Tuplet<uint, int> a, Tuplet<uint, int> b) {
                return b.Item2.CompareTo(a.Item2);
            });
            map.Add("total", total);
            foreach(Tuplet<uint, int> page in rank) {
                Hashtable pageMap = new Hashtable(StringComparer.OrdinalIgnoreCase);
                pages.Add(pageMap);

                // BUGBUGBUG (arnec): the AsLocalUri should not be required after bug #5964 is resolved
                pageMap.Add("page", DekiScriptExpression.Constant(deki.At("$page").AsLocalUri(), new[] { DekiScriptExpression.Constant(page.Item1), DekiScriptExpression.Constant(true) }));
                pageMap.Add("views", page.Item2);
                resultCount++;
                if(resultCount >= maxResults) {
                    break;
                }
            }
            return map;
        }
        public void Page_delete_events_skip_page_auth() {
            XUri mockDeki = new XUri("http://mock/deki");
            int dekiCalled = 0;
            MockPlug.Register(mockDeki, delegate(Plug p, string v, XUri u, DreamMessage r, Result<DreamMessage> r2) {
                _log.DebugFormat("mockDeki called at: {0}", u);
                dekiCalled++;
                r2.Return(DreamMessage.BadRequest("shouldn't have called deki"));
            });

            XUri authorizedRecipient = new XUri("http://mock/authorized2");
            AutoResetEvent authorizedResetEvent = new AutoResetEvent(false);
            XDoc authorizedReceived = null;
            MockPlug.Register(authorizedRecipient, delegate(Plug p, string v, XUri u, DreamMessage r, Result<DreamMessage> r2) {
                _log.DebugFormat("authorizedRecipient called at: {0}", u);
                authorizedReceived = r.ToDocument();
                authorizedResetEvent.Set();
                r2.Return(DreamMessage.Ok());
            });

            XUri mockRecipient = new XUri("http://mock/r1");
            int mockRecipientCalled = 0;
            MockPlug.Register(mockRecipient, delegate(Plug p, string v, XUri u, DreamMessage r, Result<DreamMessage> r2) {
                _log.DebugFormat("mockRecipient called at: {0}", u);
                mockRecipientCalled++;
                r2.Return(DreamMessage.Ok());
            });

            XUri dispatcherUri = new XUri("http://mock/dispatcher");
            DekiDispatcher dispatcher = new DekiDispatcher(new DispatcherConfig {
                ServiceUri = dispatcherUri,
                ServiceAccessCookie = new DreamCookie("service-key", "foo", dispatcherUri),
                ServiceConfig = new XDoc("config").Elem("uri.deki", mockDeki).Elem("authtoken", "abc")
            });

            XDoc sub1 = new XDoc("subscription-set")
                .Elem("uri.owner", authorizedRecipient)
                .Start("subscription")
                    .Attr("id", "1")
                    .Elem("channel", "event://default/deki/pages/*")
                    .Start("recipient").Attr("authtoken", "abc").Elem("uri", authorizedRecipient).End()
                .End();

            Thread.Sleep(100);
            _log.DebugFormat("registering sub set 1");
            dispatcher.RegisterSet(sub1);
            XDoc sub2 = new XDoc("subscription-set")
                .Elem("uri.owner", mockRecipient.At("1"))
                .Start("subscription")
                    .Attr("id", "1")
                    .Elem("uri.resource", "deki://default/pages/10")
                    .Elem("channel", "event://default/deki/pages/*")
                    .Start("recipient").Attr("userid", "1").Elem("uri", "http://mock/r1/match").End()
                .End();

            Thread.Sleep(100);
            _log.DebugFormat("registering sub set 2");
            dispatcher.RegisterSet(sub2);

            XDoc evDoc = new XDoc("deki-event")
               .Attr("wikiid", "default")
               .Elem("channel", "event://default/deki/pages/delete")
               .Elem("uri", "deki://default/pages/10")
               .Elem("pageid", "10");
            var ev = new DispatcherEvent(evDoc, new XUri("event://default/deki/pages/delete"), new XUri("deki://default/pages/10"), new XUri("http://default/deki/pages/10"));
            _log.DebugFormat("ready to dispatch event");

            // Meh. Testing multithreaded code is wonky. This 1000ms sleep is required, otherwise the event below never fires
            Thread.Sleep(1000);
            dispatcher.Dispatch(ev);

            // since we're waiting for more than one dekiPageAuthEvent, we give it a chance to finish after the first triggers
            Assert.IsTrue(authorizedResetEvent.WaitOne(5000, false));
            Assert.AreEqual(0, dekiCalled);
            Assert.AreEqual(0, mockRecipientCalled);
            Assert.AreEqual(evDoc, authorizedReceived);
        }
        public void Service_end_to_end_no_deki() {

            // set up mocks for all the support service calls
            string apikey = "abc";
            XUri deki = new XUri("http://mock/deki");
            int dekiCalled = 0;
            MockPlug.Register(deki, delegate(Plug p, string v, XUri u, DreamMessage r, Result<DreamMessage> r2) {
                _log.DebugFormat("deki: {0}:{1}", v, u);
                dekiCalled++;
                r2.Return(DreamMessage.Ok());
            });

            XUri varnish = new XUri("http://mock/varnish");
            AutoResetEvent varnishResetEvent = new AutoResetEvent(false);
            string varnishHeader = "";
            int varnishCalled = 0;
            MockPlug.Register(varnish, delegate(Plug p, string v, XUri u, DreamMessage r, Result<DreamMessage> r2) {
                _log.DebugFormat("varnish: {0}:{1}", v, u);
                if(v == "PURGE") {
                    varnishHeader = r.Headers["X-Purge-Url"];
                    varnishCalled++;
                    varnishResetEvent.Set();
                }
                r2.Return(DreamMessage.Ok());
            });

            XUri subscribe = new XUri("http://mock/sub");
            XUri subscriptionLocation = subscribe.At("testsub");
            AutoResetEvent subscribeResetEvent = new AutoResetEvent(false);
            XUri subscribeCalledUri = null;
            XDoc subscribePosted = null;
            MockPlug.Register(subscribe, delegate(Plug p, string v, XUri u, DreamMessage r, Result<DreamMessage> r2) {
                subscribeCalledUri = u;
                if(u == subscribe.At("subscribers")) {
                    _log.Debug("creating subscription");
                    DreamMessage msg = DreamMessage.Ok(new XDoc("x"));
                    msg.Headers.Location = subscriptionLocation;
                    subscribeResetEvent.Set();
                    r2.Return(msg);
                } else {
                    _log.Debug("updating subscription");
                    subscribePosted = r.ToDocument();
                    subscribeResetEvent.Set();
                    r2.Return(DreamMessage.Ok());
                }
            });

            // set up service
            _log.Debug("set up service");
            DreamServiceInfo serviceInfo = DreamTestHelper.CreateService(
                _hostInfo,
                typeof(VarnishPurgeService),
                "varnish",
                new XDoc("config")
                    .Elem("uri.deki", deki)
                    .Elem("uri.varnish", varnish)
                    .Elem("uri.pubsub", subscribe)
                    .Elem("varnish-purge-delay", 1)
                    .Elem("apikey", apikey)
                );
            Plug service = serviceInfo.WithInternalKey().AtLocalHost;

            // expect:
            // - storage was queried
            // - subscription was created on subscribe
            Assert.IsTrue(subscribeResetEvent.WaitOne(100, true));
            Assert.AreEqual(subscribe.At("subscribers"), subscribeCalledUri);

            // post page varnish event
            service.At("queue").Post(
                new XDoc("deki-event")
                    .Attr("wikiid", "abc")
                    .Elem("channel", "event://abc/deki/pages/create")
                    .Elem("pageid", "1")
                    .Elem("path", "x/y/z"));
            Assert.IsTrue(varnishResetEvent.WaitOne(5000, false));
            Assert.AreEqual(0, dekiCalled);
            Assert.AreEqual(1, varnishCalled);
            Assert.AreEqual("^/((x/y/z|index\\.php\\?title=x/y/z)[\\?&]?|@api/deki/pages/1/?).*$", varnishHeader);
            Assert.IsTrue(Wait.For(() => varnishCalled == 1, 10.Seconds()),"varnish wasn't called");
        }
        protected override Yield Start(XDoc config, Result result) {
            yield return Coroutine.Invoke(base.Start, config, new Result());

            // read configuration settings
            _username = config["username"].AsText;
            if(string.IsNullOrEmpty(_username)) {
                throw new ArgumentException(MISSING_FIELD_ERROR, "username");
            }
            _password = config["password"].AsText;
            if(string.IsNullOrEmpty(_password)) {
                throw new ArgumentException(MISSING_FIELD_ERROR, "password");
            }
            _uri = config["mantis-uri"].AsUri;
            if(_uri == null) {
                throw new ArgumentException(MISSING_FIELD_ERROR, "mantis-uri");
            }

            // initialize web-service
            _service = new MantisWebServices.MantisConnect();
            _service.Url = _uri.At("api", "soap", "mantisconnect.php").ToString();
            result.Return();
        }
        protected override Yield Start(XDoc config, Result result) {
            yield return Coroutine.Invoke(base.Start, config, new Result());

            // read configuration settings
            _username = config["username"].AsText;
            if (string.IsNullOrEmpty(_username)) {
                throw new ArgumentException(MISSING_FIELD_ERROR, "username");
            }
            _password = config["password"].AsText;
            if (string.IsNullOrEmpty(_password)) {
                throw new ArgumentException(MISSING_FIELD_ERROR, "password");
            }
            _uri = config["trac-uri"].AsUri;
            if (_uri == null) {
                throw new ArgumentException(MISSING_FIELD_ERROR, "trac-uri");
            }

            // initialize web-service
            _trac = XmlRpcProxyGen.Create<Trac>();
            _trac.Url = _uri.At("login", "xmlrpc").ToString();
            if (!string.IsNullOrEmpty(_username)) {
                _trac.Credentials = new System.Net.NetworkCredential(_username, _password);
            }

            result.Return();
        }
        public void Subscribers_to_a_single_page_do_not_get_escalated_by_a_subscriber_with_infinite_depth_on_same_page() {
            var mockDekiUri = new XUri("http://mock/deki");
            var mockDeki = MockPlug.Register(mockDekiUri);
            mockDeki.Expect().Verb("GET").Uri(mockDekiUri.At("pages", "10")).Response(DreamMessage.Ok(new XDoc("page")
                    .Attr("id", "10")
                    .Start("page.parent")
                        .Attr("id", "9")
                        .Start("page.parent")
                            .Attr("id", "8")
                            .Start("page.parent")
                                .Attr("id", "7")
                    .EndAll()));
            var dispatcherUri = new XUri("http://mock/dispatcher");
            var dispatcher = new DekiDispatcher(new DispatcherConfig {
                ServiceUri = dispatcherUri,
                ServiceAccessCookie = new DreamCookie("service-key", "foo", dispatcherUri),
                ServiceConfig = new XDoc("config").Elem("uri.deki", mockDekiUri).Elem("authtoken", "abc")
            });
            var combinedSetUpdated = 0;
            dispatcher.CombinedSetUpdated += (o, e) => {
                combinedSetUpdated++;
            };

            // subscribe to page 7 and all children
            var mockHierarchyRecipientUri = new XUri("http://mock/recipient/hierarchy");
            var mockHierarchyRecipient = MockPlug.Register(mockHierarchyRecipientUri);
            mockHierarchyRecipient.Expect().Verb("POST");
            dispatcher.RegisterSet(new XDoc("subscription-set")
                .Elem("uri.owner", mockHierarchyRecipientUri)
                .Start("subscription")
                    .Attr("id", "3")
                    .Elem("uri.resource", "deki://default/pages/7#depth=infinity")
                    .Elem("channel", "event://default/deki/pages/*")
                    .Start("recipient").Attr("authtoken", "abc").Elem("uri", mockHierarchyRecipientUri).End()
                .End());

            // subscribe to only page 7
            var mockPageonlyRecipientUri = new XUri("http://mock/recipient/pageonly");
            var mockPageonlyRecipientCalled = 0;
            MockPlug.Register(mockPageonlyRecipientUri, delegate(Plug p, string v, XUri u, DreamMessage r, Result<DreamMessage> r2) {
                _log.DebugFormat("mockPageonlyRecipient called at: {0}", u);
                mockPageonlyRecipientCalled++;
                r2.Return(DreamMessage.Ok());
            });
            dispatcher.RegisterSet(new XDoc("subscription-set")
                .Elem("uri.owner", mockPageonlyRecipientUri)
                .Start("subscription")
                    .Attr("id", "3")
                    .Elem("uri.resource", "deki://default/pages/7")
                    .Elem("channel", "event://default/deki/pages/*")
                    .Start("recipient").Attr("authtoken", "abc").Elem("uri", mockPageonlyRecipientUri).End()
                .End());

            // wait for subscriptions to be set up
            _log.DebugFormat("wait for subscriptions to be updated");
            Assert.IsTrue(Wait.For(() => combinedSetUpdated == 2, TimeSpan.FromSeconds(10)), "timeout waiting for subscriptions");

            // fire page change for page 10 (sub-child of page 7)
            XDoc evDoc = new XDoc("deki-event")
               .Attr("wikiid", "default")
               .Elem("channel", "event://default/deki/pages/update")
               .Elem("uri", "deki://default/pages/10")
               .Elem("pageid", "10");
            var ev = new DispatcherEvent(evDoc, new XUri("event://default/deki/pages/update"), new XUri("deki://default/pages/10"), new XUri("http://default/deki/pages/10"));
            _log.DebugFormat("ready to dispatch event");
            dispatcher.Dispatch(ev);

            // wait for deki to have been called
            _log.DebugFormat("wait for deki call");
            Assert.IsTrue(mockDeki.WaitAndVerify(TimeSpan.FromSeconds(10)), mockDeki.VerificationFailure);

            // wait for recipients to be notified
            _log.DebugFormat("wait for hierarchy notification");
            Assert.IsTrue(mockHierarchyRecipient.WaitAndVerify(TimeSpan.FromSeconds(10)), mockHierarchyRecipient.VerificationFailure);

            // only 'hierarchy' subscriber should have been notified
            _log.DebugFormat("make sure page only doesn't get called");
            Assert.IsFalse(Wait.For(() => mockPageonlyRecipientCalled > 0, TimeSpan.FromSeconds(5)));
        }