Esempio n. 1
0
        /// <summary>
        /// Constructs a new <see cref="InteractiveAuthenticationProvider"/>
        /// </summary>
        /// <param name="publicClientApplication">A <see cref="IPublicClientApplication"/> to pass to <see cref="InteractiveAuthenticationProvider"/> for authentication.</param>
        /// <param name="scopes">Scopes required to access Microsoft Graph. This defaults to https://graph.microsoft.com/.default when none is set.</param>
        /// <param name="prompt">Designed interactive experience for the user. Defaults to <see cref="Prompt.SelectAccount"/>.</param>
        /// <param name="window">Object containing a reference to the parent window/activity.</param>
        /// <param name="pointer">Object containing a reference to the parent window/activity.</param>
        public InteractiveAuthenticationProvider(
            IPublicClientApplication publicClientApplication,
            IEnumerable <string> scopes = null,
            Prompt?prompt = null,
            System.Windows.Forms.IWin32Window window = null,
            IntPtr pointer = default(IntPtr))
        {
            Scopes = scopes ?? new List <string> {
                AuthConstants.DefaultScopeUrl
            };
            if (Scopes.Count() == 0)
            {
                throw new AuthenticationException(
                          new Error {
                    Code = ErrorConstants.Codes.InvalidRequest, Message = ErrorConstants.Message.EmptyScopes
                },
                          new ArgumentException());
            }

            ClientApplication = publicClientApplication ?? throw new AuthenticationException(
                                          new Error
            {
                Code    = ErrorConstants.Codes.InvalidRequest,
                Message = string.Format(ErrorConstants.Message.NullValue, nameof(publicClientApplication))
            });
            Prompt        = prompt ?? Prompt.SelectAccount;
            ParentWindow  = window;
            ParentPointer = pointer;
        }
        public async Task <Unit> Handle(DeletePromptCommand request, CancellationToken cancellationToken = default)
        {
            if (!_currentUserService.TryGetCurrentUser(out GetUserViewModel? user))
            {
                throw new DeletePromptUserUnauthorizedException();
            }

            Prompt?prompt = await _dbContext.Prompts.Include(e => e.Children)
                            .FirstOrDefaultAsync(e => e.Id == request.Id);

            if (prompt == null)
            {
                throw new DeletePromptDoesNotExistException();
            }

            if (prompt.OwnerId != user !.Id && (user.Role & RoleEnum.Delete) == 0)
            {
                throw new DeletePromptUserUnauthorizedException();
            }

            _dbContext.Prompts.Remove(prompt);
            await RemoveAllChildren(prompt.Children);

            await _dbContext.SaveChangesAsync(cancellationToken);

            return(Unit.Value);
        }
        public async Task Handle_DoesNotReturnPromptsWithParents(int subScenarioAmount)
        {
            //arrange
            var query = new SearchPromptsQuery {
                PageSize = 20, IncludeDrafts = true
            };
            List <Prompt>?prompts = GeneratePromptData();
            Prompt?       parent  = prompts[0];

            for (var i = 0; i < subScenarioAmount; i++)
            {
                prompts.Add(new Prompt {
                    Parent = parent
                });
            }

            DbContext.Prompts.AddRange(prompts);
            DbContext.Prompts.Add(new Prompt {
                IsDraft = true
            });
            await DbContext.SaveChangesAsync();

            //act
            SearchPromptsViewModel?actual = await _handler.Handle(query);

            //assert
            Assert.Equal(16, actual.Results.Count);
        }
        /// <summary>
        ///  If this parameter contains none with any other value, then it is not valid.
        /// </summary>
        /// <param name="prompt">The <see cref="Prompt"/> value to be validated</param>
        /// <returns></returns>
        public static bool IsValid(this Prompt?prompt)
        {
            if (!prompt.HasValue)
            {
                return(true);
            }

            if ((prompt & Prompt.None) != Prompt.None)
            {
                return(true);
            }
            if ((prompt & Prompt.Consent) == Prompt.Consent)
            {
                return(false);
            }
            if ((prompt & Prompt.Login) == Prompt.Login)
            {
                return(false);
            }
            if ((prompt & Prompt.SelectAccount) == Prompt.SelectAccount)
            {
                return(false);
            }

            return(true);
        }
Esempio n. 5
0
        public async Task Handle_DoesNotChangePublishDate_WhenItHasValue(bool initialDraftStatus,
                                                                         bool updatedDraftStatus)
        {
            //arrange
            DateTime expectedDate = DateTime.UtcNow;
            var      owner        = new User {
                Username = "******"
            };
            var prompt = new Prompt {
                IsDraft = initialDraftStatus, Owner = owner, PublishDate = expectedDate
            };

            DbContext.Prompts.Add(prompt);
            await DbContext.SaveChangesAsync();

            var user = new GetUserViewModel {
                Id = owner.Id, Role = RoleEnum.FieldEdit
            };

            _mockUserService.Setup(e => e.TryGetCurrentUser(out user)).Returns(true);
            var command = new UpdatePromptCommand {
                Id = prompt.Id, SaveDraft = updatedDraftStatus
            };

            //act
            await _handler.Handle(command);

            //assert
            Prompt?actual = DbContext.Prompts.Find(prompt.Id);

            Assert.Equal(expectedDate, actual.PublishDate);
        }
Esempio n. 6
0
        public async Task Handle_LeavesPublishNull_WhenSaveDraftIsTrue_AndPromptHasNoPublishDate_AndWasDraft()
        {
            //arrange
            var owner = new User {
                Username = "******"
            };
            var prompt = new Prompt {
                IsDraft = true, Owner = owner
            };

            DbContext.Prompts.Add(prompt);
            await DbContext.SaveChangesAsync();

            var user = new GetUserViewModel {
                Id = owner.Id, Role = RoleEnum.FieldEdit
            };

            _mockUserService.Setup(e => e.TryGetCurrentUser(out user)).Returns(true);
            var command = new UpdatePromptCommand {
                Id = prompt.Id, SaveDraft = true
            };

            //act
            await _handler.Handle(command);

            //assert
            Prompt?actual = DbContext.Prompts.Find(prompt.Id);

            Assert.Null(actual.PublishDate);
        }
Esempio n. 7
0
        public async Task Handle_SetsAPromptWithScriptZipSetToNull()
        {
            //arrange
            var owner = new User {
                Username = "******"
            };
            var prompt = new Prompt {
                Owner = owner
            };

            DbContext.Prompts.Add(prompt);
            await DbContext.SaveChangesAsync();

            var user = new GetUserViewModel {
                Id = owner.Id
            };

            _mockUserService.Setup(e => e.TryGetCurrentUser(out user)).Returns(true);
            var command = new UpdatePromptCommand {
                Id = prompt.Id, OwnerId = user.Id, ScriptZip = null
            };

            //act
            await _handler.Handle(command);

            //assert
            Prompt?actualPrompt = await DbContext.Prompts.FindAsync(prompt.Id);

            Assert.Null(actualPrompt.ScriptZip);
        }
Esempio n. 8
0
        public async Task Handle_SetsIsDraftToTrue_WhenSaveDraftIsTrue(bool initialDraftStatus)
        {
            //arrange
            var owner = new User {
                Username = "******"
            };
            var prompt = new Prompt {
                IsDraft = initialDraftStatus, Owner = owner
            };

            DbContext.Prompts.Add(prompt);
            await DbContext.SaveChangesAsync();

            var user = new GetUserViewModel {
                Id = owner.Id
            };

            _mockUserService.Setup(e => e.TryGetCurrentUser(out user)).Returns(true);
            var command = new UpdatePromptCommand {
                Id = prompt.Id, SaveDraft = true
            };

            //act
            await _handler.Handle(command);

            //assert
            Prompt?actual = DbContext.Prompts.Find(prompt.Id);

            Assert.True(actual.IsDraft);
        }
Esempio n. 9
0
        public async Task Handle_UpdatesAPromptWithNovelAiScenarioSetToExpectedValue()
        {
            //arrange
            const string?expectedString = null;
            var          owner          = new User {
                Username = "******"
            };
            var prompt = new Prompt {
                Owner = owner
            };

            DbContext.Prompts.Add(prompt);
            await DbContext.SaveChangesAsync();

            var user = new GetUserViewModel {
                Id = owner.Id
            };

            _mockUserService.Setup(e => e.TryGetCurrentUser(out user)).Returns(true);
            var command = new UpdatePromptCommand {
                Id = prompt.Id, OwnerId = user.Id, NovelAiScenario = expectedString
            };

            //act
            await _handler.Handle(command);

            //assert
            Prompt?actualPrompt = await DbContext.Prompts.FindAsync(prompt.Id);

            Assert.Equal(expectedString, actualPrompt.NovelAiScenario);
        }
Esempio n. 10
0
        public async Task Handle_SetsDraftToFalse_WhenThereIsAParentId(bool intitialStatus)
        {
            //arrange
            var owner = new User {
                Username = "******"
            };

            DbContext.Users.Add(owner);

            var parent = new Prompt {
                Owner = owner
            };

            DbContext.Prompts.Add(parent);

            await DbContext.SaveChangesAsync();

            var user = new GetUserViewModel {
                Id = owner.Id
            };

            _mockUserService.Setup(e => e.TryGetCurrentUser(out user)).Returns(true);

            var command = new CreatePromptCommand {
                OwnerId = user.Id, SaveDraft = intitialStatus, ParentId = parent.Id
            };

            //act
            var actual = await _handler.Handle(command);

            //assert
            Prompt?actualPrompt = DbContext.Prompts.Find(actual);

            Assert.False(actualPrompt.IsDraft);
        }
Esempio n. 11
0
        /// <summary>
        /// Space delimited, case sensitive list of ASCII string values that specifies whether the Authorization Server prompts the End-OidcUser for reauthentication and consent
        /// </summary>
        /// <param name="prompt">The <see cref="Prompt"/> value to be formatted</param>
        /// <returns>Returns the space delimited, case sensitive list of ASCII string values contained by the <param name="prompt"></param></returns>
        public static string Format(this Prompt?prompt)
        {
            if (!prompt.HasValue)
            {
                return(string.Empty);
            }
            if ((prompt & Prompt.None) == Prompt.None)
            {
                return("none");
            }

            var result = "";

            if ((prompt & Prompt.Consent) == Prompt.Consent)
            {
                result += "consent ";
            }
            if ((prompt & Prompt.Login) == Prompt.Login)
            {
                result += "login ";
            }
            if ((prompt & Prompt.SelectAccount) == Prompt.SelectAccount)
            {
                result += "select_account ";
            }

            return(result.TrimEnd());
        }
Esempio n. 12
0
        public async Task Handle_CreatesAPromptWithTheParentId_WhenParentIdIsGiven_AndTheCurrentUserIsTheSameId()
        {
            //arrange
            var owner = new User {
                Username = "******"
            };

            DbContext.Users.Add(owner);
            await DbContext.SaveChangesAsync();

            var parent = new Prompt {
                OwnerId = owner.Id
            };

            DbContext.Prompts.Add(parent);
            await DbContext.SaveChangesAsync();

            var user = new GetUserViewModel {
                Id = owner.Id
            };

            _mockUserService.Setup(e => e.TryGetCurrentUser(out user)).Returns(true);
            var command = new CreatePromptCommand {
                ParentId = parent.Id, OwnerId = owner.Id
            };

            //act
            var actual = await _handler.Handle(command);

            //assert
            Prompt?actualPrompt = await DbContext.Prompts.FindAsync(actual);

            Assert.NotNull(actualPrompt);
            Assert.Equal(parent.Id, actualPrompt.ParentId);
        }
Esempio n. 13
0
        public async Task Handle_DoesNotChangeDraftStatus_WhenUserIsNotOwner(bool expectedDraftStatus)
        {
            //arrange
            var owner = new User {
                Username = "******"
            };
            var prompt = new Prompt {
                IsDraft = expectedDraftStatus, Owner = owner
            };

            DbContext.Prompts.Add(prompt);
            await DbContext.SaveChangesAsync();

            var user = new GetUserViewModel {
                Id = int.MaxValue, Role = RoleEnum.FieldEdit
            };

            _mockUserService.Setup(e => e.TryGetCurrentUser(out user)).Returns(true);
            var command = new UpdatePromptCommand {
                Id = prompt.Id, SaveDraft = !expectedDraftStatus
            };

            //act
            await _handler.Handle(command);

            //assert
            Prompt?actual = DbContext.Prompts.Find(prompt.Id);

            Assert.Equal(expectedDraftStatus, actual.IsDraft);
        }
        private async Task <AuthenticationResult> RunAtiAsync(IPublicClientApplication pca)
        {
            string loginHint = GetLoginHint();

            if (!string.IsNullOrEmpty(loginHint) && cbxAccount.SelectedIndex > 0)
            {
                throw new InvalidOperationException("[TEST APP FAILURE] Please use either the login hint or the account, but not both");
            }

            AuthenticationResult result = null;
            var scopes  = GetScopes();
            var guid    = Guid.NewGuid();
            var builder = pca.AcquireTokenInteractive(scopes)
                          .WithUseEmbeddedWebView(true)
                          //.WithExtraQueryParameters("domain_hint=live.com") -- will force AAD login with browser
                          //.WithExtraQueryParameters("msafed=0")             -- will force MSA login with browser
                          .WithEmbeddedWebViewOptions(
                new EmbeddedWebViewOptions()
            {
                Title = "Hello world",
            })
                          .WithParentActivityOrWindow(this.Handle);

            if (cbxPOP.Checked)
            {
                builder = builder.WithProofOfPossession(_popNonce, System.Net.Http.HttpMethod.Get, new Uri(pca.Authority));
            }

            Prompt?prompt = GetPrompt();

            if (prompt.HasValue)
            {
                builder = builder.WithPrompt(prompt.Value);
            }

            if (!string.IsNullOrEmpty(loginHint))
            {
                Log($"ATI WithLoginHint  {loginHint}");
                builder = builder.WithLoginHint(loginHint);
            }
            else if (cbxAccount.SelectedIndex > 0)
            {
                var acc = (cbxAccount.SelectedItem as AccountModel).Account;
                Log($"ATI WithAccount for account {acc?.Username ?? "null" }");
                builder = builder.WithAccount(acc);
            }
            else
            {
                Log($"ATI without login_hint or account. It should display the account picker");
            }

            if (cbxBackgroundThread.Checked)
            {
                await Task.Delay(500).ConfigureAwait(false);
            }
            result = await builder.ExecuteAsync().ConfigureAwait(false);

            return(result);
        }
        private async Task <AuthenticationResult> RunAtiAsync(IPublicClientApplication pca)
        {
            string loginHint = GetLoginHint();

            if (!string.IsNullOrEmpty(loginHint) && cbxAccount.SelectedIndex > 0)
            {
                throw new InvalidOperationException("[TEST APP FAILURE] Please use either the login hint or the account, but not both");
            }

            AuthenticationResult result = null;
            var scopes = GetScopes();

            var builder = pca.AcquireTokenInteractive(scopes)
                          .WithUseEmbeddedWebView(true)
                          .WithEmbeddedWebViewOptions(
                new EmbeddedWebViewOptions()
            {
                Title = "Hello world",
            })
                          .WithParentActivityOrWindow(this.Handle);


            Prompt?prompt = GetPrompt();

            if (prompt.HasValue)
            {
                builder = builder.WithPrompt(prompt.Value);
            }

            if (!string.IsNullOrEmpty(loginHint))
            {
                Log($"ATI WithLoginHint  {loginHint}");
                builder = builder.WithLoginHint(loginHint);
            }
            else if (cbxAccount.SelectedIndex > 0)
            {
                var acc = (cbxAccount.SelectedItem as AccountModel).Account;
                Log($"ATI WithAccount for account {acc?.Username ?? "null" }");
                builder = builder.WithAccount(acc);
            }
            else
            {
                Log($"ATI without login_hint or account. It should display the account picker");
            }


            await Task.Delay(500).ConfigureAwait(false);

            result = await builder.ExecuteAsync().ConfigureAwait(false);


            return(result);
        }
Esempio n. 16
0
        public async Task Handle_SetsPromptToDraft_WhenSaveDraftIsTrue()
        {
            //arrange
            var user = new User {
                Username = "******"
            };

            DbContext.Users.Add(user);
            await DbContext.SaveChangesAsync();

            var command = new CreatePromptCommand {
                OwnerId = user.Id, SaveDraft = true
            };

            //act
            var actual = await _handler.Handle(command);

            //assert
            Prompt?prompt = DbContext.Prompts.Find(actual);

            Assert.True(prompt.IsDraft);
        }
Esempio n. 17
0
        public async Task Handle_CreatesAPromptWithScriptZipSetToNull()
        {
            //arrange
            var user = new User {
                Username = "******"
            };

            DbContext.Users.Add(user);
            await DbContext.SaveChangesAsync();

            var command = new CreatePromptCommand {
                OwnerId = user.Id, ScriptZip = null
            };

            //act
            var actualId = await _handler.Handle(command);

            //assert
            Prompt?actualPrompt = await DbContext.Prompts.FindAsync(actualId);

            Assert.Null(actualPrompt.ScriptZip);
        }
Esempio n. 18
0
        public async Task Handle_CreatesAPromptWithAPublishDate_WhenItIsNotADraft()
        {
            //arrange
            var user = new User {
                Username = "******"
            };

            DbContext.Users.Add(user);
            await DbContext.SaveChangesAsync();

            var command = new CreatePromptCommand {
                OwnerId = user.Id, SaveDraft = false
            };

            //act
            var actual = await _handler.Handle(command);

            //assert
            Prompt?actualPrompt = await DbContext.Prompts.FindAsync(actual);

            Assert.NotNull(actualPrompt.PublishDate);
        }
Esempio n. 19
0
        public async Task Handle_CreatesAPromptWithNovelAiScenarioSetToExpectedValue()
        {
            //arrange
            const string?expectedString = "{\"test\":\"test\"}";
            var          user           = new User {
                Username = "******"
            };

            DbContext.Users.Add(user);
            await DbContext.SaveChangesAsync();

            var command = new CreatePromptCommand {
                OwnerId = user.Id, NovelAiScenario = expectedString
            };

            //act
            var actualId = await _handler.Handle(command);

            //assert
            Prompt?actualPrompt = await DbContext.Prompts.FindAsync(actualId);

            Assert.Equal(expectedString, actualPrompt.NovelAiScenario);
        }
        public async Task <Unit> Handle(UpdatePromptCommand request, CancellationToken cancellationToken = default)
        {
            if (!_currentUserService.TryGetCurrentUser(out GetUserViewModel? user))
            {
                return(Unit.Value);
            }

            Prompt?prompt = await _dbContext.Prompts
                            .Include(e => e.PromptTags)
                            .Include(e => e.WorldInfos)
                            .FirstOrDefaultAsync(e => e.Id == request.Id, cancellationToken);

            var isOwner      = user !.Id == prompt.OwnerId;
            var canEditField = isOwner ? isOwner : (user.Role & RoleEnum.FieldEdit) != 0;
            var canEditTags  = isOwner ? isOwner : (user.Role & RoleEnum.TagEdit) != 0;

            if (canEditField)
            {
                var isDraft = !prompt.ParentId.HasValue &&
                              (isOwner
                                                      ? request.SaveDraft
                                                      : prompt.IsDraft);
                prompt.AuthorsNote   = request.AuthorsNote?.Replace("\r\n", "\n");
                prompt.DateEdited    = DateTime.UtcNow;
                prompt.Memory        = request.Memory?.Replace("\r\n", "\n");
                prompt.Nsfw          = request.Nsfw;
                prompt.PromptContent = request.PromptContent.Replace("\r\n", "\n");
                prompt.Quests        = request.Quests?.Replace("\r\n", "\n");
                prompt.Title         = request.Title.Replace("\r\n", "\n");
                prompt.Description   = request.Description?.Replace("\r\n", "\n");
                prompt.WorldInfos    = new List <WorldInfo>();
                prompt.IsDraft       = isDraft;
                prompt.PublishDate ??= isDraft ? null : (DateTime?)DateTime.UtcNow;
                prompt.ScriptZip = isOwner
                                        ? request.ScriptZip ?? prompt.ScriptZip
                                        : prompt.ScriptZip;
                prompt.NovelAiScenario = string.IsNullOrWhiteSpace(request.NovelAiScenario) ? null : request.NovelAiScenario;
                prompt.HoloAiScenario  = string.IsNullOrWhiteSpace(request.HoloAiScenario) ? null :  request.HoloAiScenario;

                foreach (var worldInfo in request.WorldInfos)
                {
                    if (string.IsNullOrWhiteSpace(worldInfo.Entry) || string.IsNullOrWhiteSpace(worldInfo.Keys))
                    {
                        continue;
                    }

                    prompt.WorldInfos.Add(new WorldInfo
                    {
                        DateCreated = DateTime.UtcNow,
                        Entry       = worldInfo.Entry.Replace("\r\n", "\n"),
                        Keys        = worldInfo.Keys.Replace("\r\n", "\n"),
                        Prompt      = prompt
                    });
                }
            }

            if (canEditTags)
            {
                prompt.PromptTags = new List <PromptTag>();
                var promptTags =
                    request.PromptTags.Split(',').Select(p => p.Trim().ToLower()).Distinct();
                foreach (var promptTag in promptTags)
                {
                    if (string.IsNullOrWhiteSpace(promptTag))
                    {
                        continue;
                    }

                    if (string.Equals(promptTag, "nsfw", StringComparison.OrdinalIgnoreCase))
                    {
                        prompt.Nsfw = true;
                        continue;
                    }

                    Tag?tag = await _dbContext.Tags.FirstOrDefaultAsync(e =>
                                                                        EF.Functions.ILike(e.Name, NpgsqlHelper.SafeIlike(promptTag), NpgsqlHelper.EscapeChar));

                    if (tag == null)
                    {
                        prompt.PromptTags.Add(new PromptTag {
                            Prompt = prompt, Tag = new Tag {
                                Name = promptTag
                            }
                        });
                    }
                    else
                    {
                        prompt.PromptTags.Add(new PromptTag {
                            Prompt = prompt, Tag = tag
                        });
                    }
                }
            }

            _dbContext.Prompts.Update(prompt);
            await _dbContext.SaveChangesAsync(cancellationToken);

            return(Unit.Value);
        }
        public async Task <int> Handle(CreatePromptCommand request, CancellationToken cancellationToken = default)
        {
            if (request.ParentId.HasValue)
            {
                if (!_currentUserService.TryGetCurrentUser(out GetUserViewModel? user))
                {
                    throw new CreatePromptUnauthorizedParentException();
                }

                Prompt?parent = await _dbContext.Prompts.FindAsync(request.ParentId);

                if (parent.OwnerId != user !.Id)
                {
                    throw new CreatePromptUnauthorizedParentException();
                }
            }

            var isDraft = !request.ParentId.HasValue && request.SaveDraft;

            var prompt = new Prompt
            {
                AuthorsNote     = request.AuthorsNote?.Replace("\r\n", "\n"),
                DateCreated     = DateTime.UtcNow,
                DateEdited      = null,
                Memory          = request.Memory?.Replace("\r\n", "\n"),
                Nsfw            = request.Nsfw,
                PromptContent   = request.PromptContent.Replace("\r\n", "\n"),
                Quests          = request.Quests?.Replace("\r\n", "\n"),
                Title           = request.Title.Replace("\r\n", "\n"),
                Description     = request.Description?.Replace("\r\n", "\n"),
                OwnerId         = request.OwnerId,
                Upvote          = 0,
                Views           = 0,
                IsDraft         = isDraft,
                PublishDate     = isDraft ? null : (DateTime?)DateTime.UtcNow,
                ParentId        = request.ParentId,
                ScriptZip       = request.ScriptZip,
                NovelAiScenario = string.IsNullOrWhiteSpace(request.NovelAiScenario) ? null : request.NovelAiScenario,
                HoloAiScenario  = string.IsNullOrWhiteSpace(request.HoloAiScenario) ? null :  request.HoloAiScenario
            };

            foreach (var worldInfo in request.WorldInfos)
            {
                if (string.IsNullOrWhiteSpace(worldInfo.Entry) || string.IsNullOrWhiteSpace(worldInfo.Keys))
                {
                    continue;
                }

                prompt.WorldInfos.Add(new WorldInfo
                {
                    DateCreated = DateTime.UtcNow,
                    Entry       = worldInfo.Entry.Replace("\r\n", "\n"),
                    Keys        = worldInfo.Keys.Replace("\r\n", "\n"),
                    Prompt      = prompt
                });
            }

            var promptTags = request.PromptTags.Split(',').Select(p => p.Trim().ToLower()).Distinct();

            foreach (var promptTag in promptTags)
            {
                if (string.IsNullOrWhiteSpace(promptTag))
                {
                    continue;
                }

                if (string.Equals(promptTag, "nsfw", StringComparison.OrdinalIgnoreCase))
                {
                    prompt.Nsfw = true;
                    continue;
                }

                Tag?tag = await _dbContext.Tags.FirstOrDefaultAsync(e =>
                                                                    EF.Functions.ILike(e.Name, NpgsqlHelper.SafeIlike(promptTag), NpgsqlHelper.EscapeChar), cancellationToken : cancellationToken);

                if (tag == null)
                {
                    prompt.PromptTags.Add(new PromptTag {
                        Prompt = prompt, Tag = new Tag {
                            Name = promptTag
                        }
                    });
                }
                else
                {
                    prompt.PromptTags.Add(new PromptTag {
                        Prompt = prompt, Tag = tag
                    });
                }
            }

            _dbContext.Prompts.Add(prompt);
            await _dbContext.SaveChangesAsync(cancellationToken);

            return(prompt.Id);
        }