/// <summary>
        /// Initializes a new instance of the <see cref="DnsDomainUpdateConfiguration"/> class with
        /// the specified values.
        /// </summary>
        /// <param name="domain">The domain to update.</param>
        /// <param name="timeToLive">The time-to-live for the domain. If this value is <c>null</c>, the existing value for the domain is not updated.</param>
        /// <param name="emailAddress">The email address associated with the domain. If this value is <c>null</c>, the existing value for the domain is not updated.</param>
        /// <param name="comment">An optional comment associated with the domain. If this value is <c>null</c>, the existing value for the domain is not updated.</param>
        /// <exception cref="ArgumentNullException">If <paramref name="domain"/> is <c>null</c>.</exception>
        public DnsDomainUpdateConfiguration(DnsDomain domain, string emailAddress = null, string comment = null, TimeSpan? timeToLive = null)
        {
            if (domain == null)
                throw new ArgumentNullException("domain");

            _id = domain.Id;
            _emailAddress = emailAddress;
            _comment = comment;
            _timeToLive = timeToLive.HasValue ? (int?)timeToLive.Value.TotalSeconds : null;
        }
Пример #2
0
        /// <summary>
        /// Gets all existing subdomains for a particular domain through a series of
        /// asynchronous operations, each of which requests a subset of the available
        /// subdomains.
        /// </summary>
        /// <remarks>
        /// Each of the returned tasks is executed asynchronously but sequentially. This
        /// method will not send concurrent requests to the DNS service.
        /// <para>
        /// Due to the way the list end is detected, the final task may return an empty
        /// collection of <see cref="DnsSubdomain"/> instances.
        /// </para>
        /// </remarks>
        /// <param name="provider">The DNS service.</param>
        /// <param name="limit">The maximum number of <see cref="DnsSubdomain"/> objects to return from a single task. If this value is <c>null</c>, a provider-specific default is used.</param>
        /// <param name="detailed"><c>true</c> to return detailed information for each subdomain; otherwise, <c>false</c>.</param>
        /// <returns>
        /// A collections of <see cref="Task{TResult}"/> objects, each of which
        /// represents an asynchronous operation to gather a subset of the available
        /// subdomains.
        /// </returns>
        private static IEnumerable<DnsSubdomain> ListAllSubdomains(IDnsService provider, DomainId domainId, int? limit, CancellationToken cancellationToken)
        {
            if (limit <= 0)
                throw new ArgumentOutOfRangeException("limit");

            int index = 0;
            int previousIndex;
            int? totalEntries = null;

            do
            {
                previousIndex = index;
                Task<Tuple<IEnumerable<DnsSubdomain>, int?>> subdomains = provider.ListSubdomainsAsync(domainId, index, limit, cancellationToken);
                totalEntries = subdomains.Result.Item2;
                foreach (DnsSubdomain subdomain in subdomains.Result.Item1)
                {
                    index++;
                    yield return subdomain;
                }

                if (limit == null)
                {
                    // this service will return a 400 error if offset is not a multiple of limit,
                    // or if the limit is not specified
                    limit = index;
                }
            } while (index > previousIndex && (totalEntries == null || index < totalEntries));
        }
Пример #3
0
 private IndexOperation Unpublish(DomainId id)
 {
     return(Op(id, new ContentStatusChanged {
         Status = Status.Draft
     }));
 }
Пример #4
0
 private IndexOperation CreateDraft(DomainId id)
 {
     return(Op(id, new ContentDraftCreated()));
 }
Пример #5
0
 private static IAppEntity CreateApp(DomainId appId)
 {
     return(Mocks.App(NamedId.Of(appId, "my-app")));
 }
Пример #6
0
        public static object Data(IContentEntity content, DomainId refId = default, DomainId assetId = default)
        {
            var result = new Dictionary <string, object>
            {
                ["gql_2Numbers"] = new
                {
                    iv = 22.0
                },
                ["gql_2Numbers2"] = new
                {
                    iv = 23.0
                },
                ["content"] = new
                {
                    iv = 24.0
                },
                ["myString"] = new
                {
                    de = "value"
                },
                ["myString2"] = new
                {
                    iv = (object?)null
                },
                ["myNumber"] = new
                {
                    iv = 1.0
                },
                ["myNumber2"] = new
                {
                    iv = (object?)null
                },
                ["myBoolean"] = new
                {
                    iv = true
                },
                ["myDatetime"] = new
                {
                    iv = content.LastModified.ToString()
                },
                ["myJson"] = new
                {
                    iv = new
                    {
                        value = 1
                    }
                },
                ["myGeolocation"] = new
                {
                    iv = new
                    {
                        latitude  = 10,
                        longitude = 20
                    }
                },
                ["myTags"] = new
                {
                    iv = new[]
                    {
                        "tag1",
                        "tag2"
                    }
                },
                ["myLocalized"] = new
                {
                    de_DE = "de-DE"
                },
                ["myArray"] = new
                {
                    iv = new[]
                    {
                        new
                        {
                            nestedNumber  = 10.0,
                            nestedNumber2 = (object?)null,
                            nestedBoolean = true
                        },
                        new
                        {
                            nestedNumber  = 20.0,
                            nestedNumber2 = (object?)null,
                            nestedBoolean = false
                        }
                    }
                }
            };

            if (refId != default)
            {
                result["myReferences"] = new
                {
                    iv = new[]
                    {
                        refId
                    }
                };

                result["myUnion"] = new
                {
                    iv = new[]
                    {
                        refId
                    }
                };
            }

            if (assetId != default)
            {
                result["myAssets"] = new
                {
                    iv = new[]
                    {
                        assetId
                    }
                };
            }

            return(result);
        }
Пример #7
0
        public async Task Should_replace_asset_url_in_content()
        {
            var me = RefToken.User("123");

            var newAssetsUrl    = "https://new.squidex.com/api/assets";
            var newAssetsUrlApp = "https://old.squidex.com/api/assets/my-new-app";

            var oldAssetsUrl    = "https://old.squidex.com/api/assets";
            var oldAssetsUrlApp = "https://old.squidex.com/api/assets/my-old-app";

            var reader = A.Fake <IBackupReader>();

            A.CallTo(() => urlGenerator.AssetContentBase())
            .Returns(newAssetsUrl);

            A.CallTo(() => urlGenerator.AssetContentBase(appId.Name))
            .Returns(newAssetsUrlApp);

            A.CallTo(() => reader.ReadJsonAsync <BackupContents.Urls>(A <string> ._, ct))
            .Returns(new BackupContents.Urls
            {
                Assets    = oldAssetsUrl,
                AssetsApp = oldAssetsUrlApp
            });

            var data =
                new ContentData()
                .AddField("asset",
                          new ContentFieldData()
                          .AddLocalized("en", $"Asset: {oldAssetsUrlApp}/my-asset.jpg.")
                          .AddLocalized("it", $"Asset: {oldAssetsUrl}/my-asset.jpg."))
                .AddField("assetsInArray",
                          new ContentFieldData()
                          .AddLocalized("iv",
                                        JsonValue.Array(
                                            $"Asset: {oldAssetsUrlApp}/my-asset.jpg.")))
                .AddField("assetsInObj",
                          new ContentFieldData()
                          .AddLocalized("iv",
                                        JsonValue.Object()
                                        .Add("asset", $"Asset: {oldAssetsUrlApp}/my-asset.jpg.")));

            var updateData =
                new ContentData()
                .AddField("asset",
                          new ContentFieldData()
                          .AddLocalized("en", $"Asset: {newAssetsUrlApp}/my-asset.jpg.")
                          .AddLocalized("it", $"Asset: {newAssetsUrl}/my-asset.jpg."))
                .AddField("assetsInArray",
                          new ContentFieldData()
                          .AddLocalized("iv",
                                        JsonValue.Array(
                                            $"Asset: {newAssetsUrlApp}/my-asset.jpg.")))
                .AddField("assetsInObj",
                          new ContentFieldData()
                          .AddLocalized("iv",
                                        JsonValue.Object()
                                        .Add("asset", $"Asset: {newAssetsUrlApp}/my-asset.jpg.")));

            var context = new RestoreContext(appId.Id, new UserMapping(me), reader, DomainId.NewGuid());

            await sut.RestoreEventAsync(Envelope.Create(new AppCreated
            {
                Name = appId.Name
            }), context, ct);

            await sut.RestoreEventAsync(Envelope.Create(new ContentUpdated
            {
                Data = data
            }), context, ct);

            Assert.Equal(updateData, data);
        }
Пример #8
0
        public async Task Should_restore_states_for_all_contents()
        {
            var me = RefToken.User("123");

            var schemaId1 = NamedId.Of(DomainId.NewGuid(), "my-schema1");
            var schemaId2 = NamedId.Of(DomainId.NewGuid(), "my-schema2");

            var contentId1 = DomainId.NewGuid();
            var contentId2 = DomainId.NewGuid();
            var contentId3 = DomainId.NewGuid();

            var context = new RestoreContext(appId.Id, new UserMapping(me), A.Fake <IBackupReader>(), DomainId.NewGuid());

            await sut.RestoreEventAsync(ContentEvent(new ContentCreated
            {
                ContentId = contentId1,
                SchemaId = schemaId1
            }), context, ct);

            await sut.RestoreEventAsync(ContentEvent(new ContentCreated
            {
                ContentId = contentId2,
                SchemaId = schemaId1
            }), context, ct);

            await sut.RestoreEventAsync(ContentEvent(new ContentCreated
            {
                ContentId = contentId3,
                SchemaId = schemaId2
            }), context, ct);

            await sut.RestoreEventAsync(ContentEvent(new ContentDeleted
            {
                ContentId = contentId2,
                SchemaId = schemaId1
            }), context, ct);

            await sut.RestoreEventAsync(Envelope.Create(new SchemaDeleted
            {
                SchemaId = schemaId2
            }), context, ct);

            var rebuildContents = new HashSet <DomainId>();

            A.CallTo(() => rebuilder.InsertManyAsync <ContentDomainObject, ContentDomainObject.State>(A <IEnumerable <DomainId> > ._, A <int> ._, ct))
            .Invokes(x => rebuildContents.AddRange(x.GetArgument <IEnumerable <DomainId> >(0) !));

            await sut.RestoreAsync(context, ct);

            Assert.Equal(new HashSet <DomainId>
            {
                DomainId.Combine(appId.Id, contentId1),
                DomainId.Combine(appId.Id, contentId2)
            }, rebuildContents);
        }
Пример #9
0
 public Task RebuildAsync(DomainId appId, HashSet <DomainId> rules)
 {
     return(Index(appId).RebuildAsync(rules));
 }
Пример #10
0
        public void Should_cleanup_deleted_ids()
        {
            var id1 = DomainId.NewGuid();
            var id2 = DomainId.NewGuid();

            var source =
                new ContentData()
                .AddField("references",
                          new ContentFieldData()
                          .AddInvariant(JsonValue.Array(id1, id2)))
                .AddField("assets1",
                          new ContentFieldData()
                          .AddInvariant(JsonValue.Array(id1)))
                .AddField("array",
                          new ContentFieldData()
                          .AddInvariant(
                              JsonValue.Array(
                                  JsonValue.Object()
                                  .Add("nestedAssets", JsonValue.Array(id1, id2))
                                  .Add("nestedComponent",
                                       JsonValue.Object()
                                       .Add("references",
                                            JsonValue.Array(id1, id2))
                                       .Add(Component.Discriminator, DomainId.Empty))
                                  .Add("nestedComponents",
                                       JsonValue.Array(
                                           JsonValue.Object()
                                           .Add("references",
                                                JsonValue.Array(id1, id2))
                                           .Add(Component.Discriminator, DomainId.Empty))))))
                .AddField("component",
                          new ContentFieldData()
                          .AddInvariant(
                              JsonValue.Object()
                              .Add("references",
                                   JsonValue.Array(id1, id2))
                              .Add("assets1",
                                   JsonValue.Array(id1))
                              .Add("array",
                                   JsonValue.Array(
                                       JsonValue.Object()
                                       .Add("nestedAssets", JsonValue.Array(id1, id2))))
                              .Add(Component.Discriminator, DomainId.Empty)))
                .AddField("components",
                          new ContentFieldData()
                          .AddInvariant(
                              JsonValue.Array(
                                  JsonValue.Object()
                                  .Add("references",
                                       JsonValue.Array(id1, id2))
                                  .Add("assets1",
                                       JsonValue.Array(id1))
                                  .Add("array",
                                       JsonValue.Array(
                                           JsonValue.Object()
                                           .Add("nestedAssets", JsonValue.Array(id1, id2))))
                                  .Add(Component.Discriminator, DomainId.Empty))));

            var expected =
                new ContentData()
                .AddField("references",
                          new ContentFieldData()
                          .AddInvariant(JsonValue.Array(id2)))
                .AddField("assets1",
                          new ContentFieldData()
                          .AddInvariant(JsonValue.Array()))
                .AddField("array",
                          new ContentFieldData()
                          .AddInvariant(
                              JsonValue.Array(
                                  JsonValue.Object()
                                  .Add("nestedAssets", JsonValue.Array(id2))
                                  .Add("nestedComponent",
                                       JsonValue.Object()
                                       .Add("references",
                                            JsonValue.Array(id2))
                                       .Add(Component.Discriminator, DomainId.Empty))
                                  .Add("nestedComponents",
                                       JsonValue.Array(
                                           JsonValue.Object()
                                           .Add("references",
                                                JsonValue.Array(id2))
                                           .Add(Component.Discriminator, DomainId.Empty))))))
                .AddField("component",
                          new ContentFieldData()
                          .AddInvariant(
                              JsonValue.Object()
                              .Add("references",
                                   JsonValue.Array(id2))
                              .Add("assets1",
                                   JsonValue.Array())
                              .Add("array",
                                   JsonValue.Array(
                                       JsonValue.Object()
                                       .Add("nestedAssets", JsonValue.Array(id2))))
                              .Add(Component.Discriminator, DomainId.Empty)))
                .AddField("components",
                          new ContentFieldData()
                          .AddInvariant(
                              JsonValue.Array(
                                  JsonValue.Object()
                                  .Add("references",
                                       JsonValue.Array(id2))
                                  .Add("assets1",
                                       JsonValue.Array())
                                  .Add("array",
                                       JsonValue.Array(
                                           JsonValue.Object()
                                           .Add("nestedAssets", JsonValue.Array(id2))))
                                  .Add(Component.Discriminator, DomainId.Empty))));

            var converter =
                FieldConverters.ForValues(components,
                                          ValueReferencesConverter.CleanReferences(new HashSet <DomainId> {
                id2
            }));

            var actual = source.Convert(schema, converter);

            Assert.Equal(expected, actual);
        }
Пример #11
0
 private static HashSet <DomainId> RandomIds()
 {
     return(HashSet.Of(DomainId.NewGuid()));
 }
Пример #12
0
 private static AppPatternAdded CreateInitialPattern(DomainId id, AppPattern pattern)
 {
     return(new AppPatternAdded {
         PatternId = id, Name = pattern.Name, Pattern = pattern.Pattern, Message = pattern.Message
     });
 }
Пример #13
0
 private IAppUISettingsGrain GetGrain(DomainId appId, string?userId)
 {
     return(grainFactory.GetGrain <IAppUISettingsGrain>(GetKey(appId, userId)));
 }
Пример #14
0
 public Task SetAsync(DomainId appId, string?userId, JsonObject settings)
 {
     return(GetGrain(appId, userId).SetAsync(settings.AsJ()));
 }
Пример #15
0
 public Task SetAsync(DomainId appId, string?userId, string path, IJsonValue value)
 {
     return(GetGrain(appId, userId).SetAsync(path, value.AsJ()));
 }
Пример #16
0
 public Task RemoveAsync(DomainId appId, string?userId, string path)
 {
     return(GetGrain(appId, userId).RemoveAsync(path));
 }
 private ContentEntity CreateContent()
 {
     return(new ContentEntity {
         AppId = appId, Id = DomainId.NewGuid(), SchemaId = schemaId, Version = 13
     });
 }
Пример #18
0
 private IRulesByAppIndexGrain Index(DomainId appId)
 {
     return(grainFactory.GetGrain <IRulesByAppIndexGrain>(appId.ToString()));
 }
Пример #19
0
        private Envelope <ContentEvent> ContentEvent(ContentEvent @event)
        {
            @event.AppId = appId;

            return(Envelope.Create(@event).SetAggregateId(DomainId.Combine(appId.Id, @event.ContentId)));
        }
Пример #20
0
 public Task <IReadOnlyList <(DomainId SchemaId, DomainId Id, Status Status)> > QueryIdsAsync(DomainId appId, HashSet <DomainId> ids, SearchScope scope,
                                                                                              CancellationToken ct = default)
 {
     if (scope == SearchScope.All)
     {
         return(collectionAll.QueryIdsAsync(appId, ids, ct));
     }
     else
     {
         return(collectionPublished.QueryIdsAsync(appId, ids, ct));
     }
 }
Пример #21
0
        public async Task <IResultList <IAssetFolderEntity> > QueryAsync(DomainId appId, DomainId parentId,
                                                                         CancellationToken ct = default)
        {
            using (Profiler.TraceMethod <MongoAssetFolderRepository>("QueryAsyncByQuery"))
            {
                var filter = BuildFilter(appId, parentId);

                var assetFolderEntities =
                    await Collection.Find(filter).SortBy(x => x.FolderName)
                    .ToListAsync(ct = default);
Пример #22
0
 public Task ResetScheduledAsync(DomainId documentId,
                                 CancellationToken ct = default)
 {
     return(collectionAll.ResetScheduledAsync(documentId, ct));
 }
Пример #23
0
        public static IEnrichedContentEntity Create(NamedId <DomainId> appId, NamedId <DomainId> schemaId, DomainId id, DomainId refId, DomainId assetId, ContentData?data = null)
        {
            var now = SystemClock.Instance.GetCurrentInstant();

            data ??=
            new ContentData()
            .AddField("my-string",
                      new ContentFieldData()
                      .AddLocalized("de", "value"))
            .AddField("my-string2",
                      new ContentFieldData()
                      .AddInvariant(null))
            .AddField("my-assets",
                      new ContentFieldData()
                      .AddInvariant(JsonValue.Array(assetId.ToString())))
            .AddField("2_numbers",
                      new ContentFieldData()
                      .AddInvariant(22))
            .AddField("2-numbers",
                      new ContentFieldData()
                      .AddInvariant(23))
            .AddField("content",
                      new ContentFieldData()
                      .AddInvariant(24))
            .AddField("my-number",
                      new ContentFieldData()
                      .AddInvariant(1.0))
            .AddField("my_number",
                      new ContentFieldData()
                      .AddInvariant(null))
            .AddField("my-boolean",
                      new ContentFieldData()
                      .AddInvariant(true))
            .AddField("my-datetime",
                      new ContentFieldData()
                      .AddInvariant(now))
            .AddField("my-tags",
                      new ContentFieldData()
                      .AddInvariant(JsonValue.Array("tag1", "tag2")))
            .AddField("my-references",
                      new ContentFieldData()
                      .AddInvariant(JsonValue.Array(refId.ToString())))
            .AddField("my-union",
                      new ContentFieldData()
                      .AddInvariant(JsonValue.Array(refId.ToString())))
            .AddField("my-geolocation",
                      new ContentFieldData()
                      .AddInvariant(JsonValue.Object().Add("latitude", 10).Add("longitude", 20)))
            .AddField("my-json",
                      new ContentFieldData()
                      .AddInvariant(JsonValue.Object().Add("value", 1)))
            .AddField("my-localized",
                      new ContentFieldData()
                      .AddLocalized("de-DE", "de-DE"))
            .AddField("my-array",
                      new ContentFieldData()
                      .AddInvariant(JsonValue.Array(
                                        JsonValue.Object()
                                        .Add("nested-number", 10)
                                        .Add("nested_number", null)
                                        .Add("nested-boolean", true),
                                        JsonValue.Object()
                                        .Add("nested-number", 20)
                                        .Add("nested_number", null)
                                        .Add("nested-boolean", false))));

            var content = new ContentEntity
            {
                Id             = id,
                AppId          = appId,
                Version        = 1,
                Created        = now,
                CreatedBy      = RefToken.User("user1"),
                LastModified   = now,
                LastModifiedBy = RefToken.User("user2"),
                Data           = data,
                SchemaId       = schemaId,
                Status         = Status.Draft,
                StatusColor    = "red"
            };

            return(content);
        }
Пример #24
0
 public Task <IReadOnlyList <(DomainId SchemaId, DomainId Id, Status Status)> > QueryIdsAsync(DomainId appId, DomainId schemaId, FilterNode <ClrValue> filterNode,
                                                                                              CancellationToken ct = default)
 {
     return(collectionAll.QueryIdsAsync(appId, schemaId, filterNode, ct));
 }
Пример #25
0
        public ContentsQueryFixture()
        {
            mongoDatabase = mongoClient.GetDatabase("QueryTests");

            SetupJson();

            var contentRepository =
                new MongoContentRepository(
                    mongoDatabase,
                    CreateAppProvider());

            Task.Run(async() =>
            {
                await contentRepository.InitializeAsync();

                await mongoDatabase.RunCommandAsync <BsonDocument>("{ profile : 0 }");
                await mongoDatabase.DropCollectionAsync("system.profile");

                var collections = contentRepository.GetInternalCollections();

                foreach (var collection in collections)
                {
                    var contentCount = await collection.Find(new BsonDocument()).CountDocumentsAsync();

                    if (contentCount == 0)
                    {
                        var batch = new List <MongoContentEntity>();

                        async Task ExecuteBatchAsync(MongoContentEntity? entity)
                        {
                            if (entity != null)
                            {
                                batch.Add(entity);
                            }

                            if ((entity == null || batch.Count >= 1000) && batch.Count > 0)
                            {
                                await collection.InsertManyAsync(batch);

                                batch.Clear();
                            }
                        }

                        foreach (var appId in AppIds)
                        {
                            foreach (var schemaId in SchemaIds)
                            {
                                for (var i = 0; i < numValues; i++)
                                {
                                    var data =
                                        new ContentData()
                                        .AddField("field1",
                                                  new ContentFieldData()
                                                  .AddJsonValue(JsonValue.Create(i)))
                                        .AddField("field2",
                                                  new ContentFieldData()
                                                  .AddJsonValue(JsonValue.Create(Lorem.Paragraph(200, 20))));

                                    var content = new MongoContentEntity
                                    {
                                        DocumentId      = DomainId.NewGuid(),
                                        AppId           = appId,
                                        Data            = data,
                                        IndexedAppId    = appId.Id,
                                        IndexedSchemaId = schemaId.Id,
                                        IsDeleted       = false,
                                        SchemaId        = schemaId,
                                        Status          = Status.Published
                                    };

                                    await ExecuteBatchAsync(content);
                                }
                            }
                        }

                        await ExecuteBatchAsync(null);
                    }
                }

                await mongoDatabase.RunCommandAsync <BsonDocument>("{ profile : 2 }");
            }).Wait();

            ContentRepository = contentRepository;
        }
Пример #26
0
 public IAsyncEnumerable <IContentEntity> StreamAll(DomainId appId, HashSet <DomainId>?schemaIds,
                                                    CancellationToken ct = default)
 {
     return(collectionAll.StreamAll(appId, schemaIds, ct));
 }
Пример #27
0
 private IndexOperation Publish(DomainId id)
 {
     return(Op(id, new ContentStatusChanged {
         Status = Status.Published
     }));
 }
Пример #28
0
 public Task <IContentEntity?> FindContentAsync(IAppEntity app, ISchemaEntity schema, DomainId id, SearchScope scope,
                                                CancellationToken ct = default)
 {
     if (scope == SearchScope.All)
     {
         return(collectionAll.FindContentAsync(schema, id, ct));
     }
     else
     {
         return(collectionPublished.FindContentAsync(schema, id, ct));
     }
 }
Пример #29
0
 private IndexOperation Delete(DomainId id)
 {
     return(Op(id, new ContentDeleted()));
 }
Пример #30
0
 private static object CreateCacheKey(DomainId appId, string etag)
 {
     return($"GraphQLModel_{appId}_{etag}");
 }
Пример #31
0
        private async Task <IResultList <IContentEntity> > QueryAsync(ClrQuery clrQuery, int take = 1000, int skip = 100, DomainId reference = default)
        {
            if (clrQuery.Take == long.MaxValue)
            {
                clrQuery.Take = take;
            }

            if (clrQuery.Skip == 0)
            {
                clrQuery.Skip = skip;
            }

            if (clrQuery.Sort.Count == 0)
            {
                clrQuery.Sort = new List <SortNode>
                {
                    new SortNode("LastModified", SortOrder.Descending)
                };
            }

            var q =
                Q.Empty
                .WithQuery(clrQuery)
                .WithReference(reference);

            var contents = await _.ContentRepository.QueryAsync(_.RandomApp(), _.RandomSchema(), q, SearchScope.All);

            return(contents);
        }
Пример #32
0
        public async Task <JsonObject> GetAsync(DomainId appId, string?userId)
        {
            var result = await GetGrain(appId, userId).GetAsync();

            return(result.Value);
        }