From 64ff595553501d1bac6b0010723ffcbb48c9b72c Mon Sep 17 00:00:00 2001 From: "D. Ror" Date: Mon, 15 May 2023 10:14:36 -0400 Subject: [PATCH 1/8] Automatically en-/dis-able definitions --- Backend.Tests/Services/WordServiceTests.cs | 26 ++++ Backend/Controllers/LiftController.cs | 7 ++ Backend/Controllers/WordController.cs | 17 +++ Backend/Interfaces/ILiftService.cs | 1 + Backend/Interfaces/IWordService.cs | 1 + Backend/Services/LiftService.cs | 9 ++ Backend/Services/WordService.cs | 9 ++ src/api/api/word-api.ts | 114 ++++++++++++++++++ src/backend/index.ts | 6 + src/components/Project/ProjectActions.ts | 12 ++ .../ProjectSettingsComponent.tsx | 27 +++-- 11 files changed, 217 insertions(+), 12 deletions(-) diff --git a/Backend.Tests/Services/WordServiceTests.cs b/Backend.Tests/Services/WordServiceTests.cs index 2a01fbfbca..2420750080 100644 --- a/Backend.Tests/Services/WordServiceTests.cs +++ b/Backend.Tests/Services/WordServiceTests.cs @@ -107,5 +107,31 @@ public void TestFindContainingWordSameVernEmptySensesSameDoms() var dupId = _wordService.FindContainingWord(newWord).Result; Assert.That(dupId, Is.EqualTo(oldWord.Id)); } + + [Test] + public void TestDoesProjectHaveDefinitionsFalse() + { + var word = Util.RandomWord(ProjId); + var sense = Util.RandomSense(); + sense.Definitions.Clear(); + word.Senses = new List { sense }; + var _ = _wordRepo.Create(word).Result; + + var hasDefs = _wordService.DoesProjectHaveDefinitions(ProjId).Result; + Assert.That(hasDefs, Is.False); + } + + [Test] + public void TestDoesProjectHaveDefinitionsTrue() + { + var word = Util.RandomWord(ProjId); + var sense = Util.RandomSense(); + sense.Definitions.Add(Util.RandomDefinition()); + word.Senses.Add(sense); + var _ = _wordRepo.Create(word).Result; + + var hasDefs = _wordService.DoesProjectHaveDefinitions(ProjId).Result; + Assert.That(hasDefs, Is.True); + } } } diff --git a/Backend/Controllers/LiftController.cs b/Backend/Controllers/LiftController.cs index 22216ef2a9..7f86008d06 100644 --- a/Backend/Controllers/LiftController.cs +++ b/Backend/Controllers/LiftController.cs @@ -149,6 +149,7 @@ public async Task UploadLiftFile(string projectId, [FromForm] Fil int liftParseResult; // Sets the projectId of our parser to add words to that project var liftMerger = _liftService.GetLiftImporterExporter(projectId, _wordRepo); + var doesImportHaveDefinitions = false; try { // Add character set to project from ldml file @@ -166,6 +167,11 @@ public async Task UploadLiftFile(string projectId, [FromForm] Fil // Import words from .lift file liftParseResult = parser.ReadLiftFile(extractedLiftFiles.First()); + + // Check if there are any definitions in the imported words + // before they are deleted by SaveImportEntries. + doesImportHaveDefinitions = liftMerger.DoesImportHaveDefinitions(); + await liftMerger.SaveImportEntries(); } catch (Exception e) @@ -183,6 +189,7 @@ public async Task UploadLiftFile(string projectId, [FromForm] Fil return NotFound(projectId); } + project.DefinitionsEnabled = doesImportHaveDefinitions; project.LiftImported = true; await _projRepo.Update(projectId, project); diff --git a/Backend/Controllers/WordController.cs b/Backend/Controllers/WordController.cs index f2b2fbb7ce..97d956eab6 100644 --- a/Backend/Controllers/WordController.cs +++ b/Backend/Controllers/WordController.cs @@ -124,6 +124,23 @@ public async Task IsFrontierNonempty(string projectId) return Ok(await _wordRepo.IsFrontierNonempty(projectId)); } + /// Checks if project has any s in its frontier. + [HttpGet("doesprojecthavedefinitions", Name = "DoesProjectHaveDefinitions")] + [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(bool))] + public async Task DoesProjectHaveDefinitions(string projectId) + { + if (!await _permissionService.HasProjectPermission(HttpContext, Permission.WordEntry)) + { + return Forbid(); + } + var project = await _projRepo.GetProject(projectId); + if (project is null) + { + return NotFound(projectId); + } + return Ok(await _wordService.DoesProjectHaveDefinitions(projectId)); + } + /// Returns all Frontier in specified . [HttpGet("frontier", Name = "GetProjectFrontierWords")] [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(List))] diff --git a/Backend/Interfaces/ILiftService.cs b/Backend/Interfaces/ILiftService.cs index c1f308e328..a01f3a336f 100644 --- a/Backend/Interfaces/ILiftService.cs +++ b/Backend/Interfaces/ILiftService.cs @@ -21,6 +21,7 @@ public interface ILiftService public interface ILiftMerger : ILexiconMerger { + bool DoesImportHaveDefinitions(); Task> SaveImportEntries(); } } diff --git a/Backend/Interfaces/IWordService.cs b/Backend/Interfaces/IWordService.cs index 7df1234dd9..b195d81cf1 100644 --- a/Backend/Interfaces/IWordService.cs +++ b/Backend/Interfaces/IWordService.cs @@ -10,5 +10,6 @@ public interface IWordService Task FindContainingWord(Word word); Task Delete(string projectId, string wordId, string fileName); Task DeleteFrontierWord(string projectId, string wordId); + Task DoesProjectHaveDefinitions(string projectId); } } diff --git a/Backend/Services/LiftService.cs b/Backend/Services/LiftService.cs index 9b1b52d842..6cc4940e20 100644 --- a/Backend/Services/LiftService.cs +++ b/Backend/Services/LiftService.cs @@ -542,6 +542,15 @@ public LiftMerger(string projectId, IWordRepository wordRepo) _wordRepo = wordRepo; } + /// + /// Check for any Definitions in the private field + /// + /// A boolean: true if at least one word has a definition. + public bool DoesImportHaveDefinitions() + { + return _importEntries.Any(w => w.Senses.Any(s => s.Definitions.Count > 0)); + } + /// /// s are added to the private field /// during lift import. This saves the contents of _importEntries to the database. diff --git a/Backend/Services/WordService.cs b/Backend/Services/WordService.cs index d089a59c6e..c03516980a 100644 --- a/Backend/Services/WordService.cs +++ b/Backend/Services/WordService.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using BackendFramework.Interfaces; using BackendFramework.Models; @@ -139,5 +140,13 @@ public async Task Update(string projectId, string wordId, Word word) var duplicatedWord = frontier.Find(w => w.Contains(word)); return duplicatedWord?.Id; } + + + /// Checks if project has any s in its frontier. + public async Task DoesProjectHaveDefinitions(string projectId) + { + var frontier = await _wordRepo.GetFrontier(projectId); + return frontier.Any(w => (w.Senses.Any(s => s.Definitions.Count > 0))); + } } } diff --git a/src/api/api/word-api.ts b/src/api/api/word-api.ts index cf0b8e3a41..90a355cd71 100644 --- a/src/api/api/word-api.ts +++ b/src/api/api/word-api.ts @@ -195,6 +195,52 @@ export const WordApiAxiosParamCreator = function ( options: localVarRequestOptions, }; }, + /** + * + * @param {string} projectId + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + doesProjectHaveDefinitions: async ( + projectId: string, + options: any = {} + ): Promise => { + // verify required parameter 'projectId' is not null or undefined + assertParamExists("doesProjectHaveDefinitions", "projectId", projectId); + const localVarPath = + `/v1/projects/{projectId}/words/doesprojecthavedefinitions`.replace( + `{${"projectId"}}`, + encodeURIComponent(String(projectId)) + ); + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { + method: "GET", + ...baseOptions, + ...options, + }; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + setSearchParams(localVarUrlObj, localVarQueryParameter, options.query); + let headersFromBaseOptions = + baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = { + ...localVarHeaderParameter, + ...headersFromBaseOptions, + ...options.headers, + }; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, /** * * @param {string} projectId @@ -644,6 +690,30 @@ export const WordApiFp = function (configuration?: Configuration) { configuration ); }, + /** + * + * @param {string} projectId + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async doesProjectHaveDefinitions( + projectId: string, + options?: any + ): Promise< + (axios?: AxiosInstance, basePath?: string) => AxiosPromise + > { + const localVarAxiosArgs = + await localVarAxiosParamCreator.doesProjectHaveDefinitions( + projectId, + options + ); + return createRequestFunction( + localVarAxiosArgs, + globalAxios, + BASE_PATH, + configuration + ); + }, /** * * @param {string} projectId @@ -885,6 +955,20 @@ export const WordApiFactory = function ( .deleteProjectWords(projectId, options) .then((request) => request(axios, basePath)); }, + /** + * + * @param {string} projectId + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + doesProjectHaveDefinitions( + projectId: string, + options?: any + ): AxiosPromise { + return localVarFp + .doesProjectHaveDefinitions(projectId, options) + .then((request) => request(axios, basePath)); + }, /** * * @param {string} projectId @@ -1056,6 +1140,20 @@ export interface WordApiDeleteProjectWordsRequest { readonly projectId: string; } +/** + * Request parameters for doesProjectHaveDefinitions operation in WordApi. + * @export + * @interface WordApiDoesProjectHaveDefinitionsRequest + */ +export interface WordApiDoesProjectHaveDefinitionsRequest { + /** + * + * @type {string} + * @memberof WordApiDoesProjectHaveDefinitions + */ + readonly projectId: string; +} + /** * Request parameters for getDuplicateId operation in WordApi. * @export @@ -1262,6 +1360,22 @@ export class WordApi extends BaseAPI { .then((request) => request(this.axios, this.basePath)); } + /** + * + * @param {WordApiDoesProjectHaveDefinitionsRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof WordApi + */ + public doesProjectHaveDefinitions( + requestParameters: WordApiDoesProjectHaveDefinitionsRequest, + options?: any + ) { + return WordApiFp(this.configuration) + .doesProjectHaveDefinitions(requestParameters.projectId, options) + .then((request) => request(this.axios, this.basePath)); + } + /** * * @param {WordApiGetDuplicateIdRequest} requestParameters Request parameters. diff --git a/src/backend/index.ts b/src/backend/index.ts index 571136dbde..b0355cc9ba 100644 --- a/src/backend/index.ts +++ b/src/backend/index.ts @@ -622,6 +622,12 @@ export async function deleteFrontierWord(wordId: string): Promise { return (await wordApi.deleteFrontierWord(params, defaultOptions())).data; } +export async function doesProjHaveDefs(projectId?: string): Promise { + const params = { projectId: projectId ?? LocalStorage.getProjectId() }; + return (await wordApi.doesProjectHaveDefinitions(params, defaultOptions())) + .data; +} + export async function getAllWords(): Promise { const projectId = LocalStorage.getProjectId(); return (await wordApi.getProjectWords({ projectId }, defaultOptions())).data; diff --git a/src/components/Project/ProjectActions.ts b/src/components/Project/ProjectActions.ts index 7e7db766cf..4d2ff3bbdf 100644 --- a/src/components/Project/ProjectActions.ts +++ b/src/components/Project/ProjectActions.ts @@ -5,6 +5,7 @@ import { ProjectAction, ProjectActionType, } from "components/Project/ProjectReduxTypes"; +import { StoreState } from "types"; import { StoreStateDispatch } from "types/Redux/actions"; export function setCurrentProject(payload?: Project): ProjectAction { @@ -49,3 +50,14 @@ export function setNewCurrentProject(project?: Project) { dispatch(setCurrentProject(project)); }; } + +export function setDefinitionsEnabled(definitionsEnabled: boolean) { + return async (dispatch: StoreStateDispatch, getState: () => StoreState) => { + const proj: Project = { + ...getState().currentProjectState.project, + definitionsEnabled, + }; + await updateProject(proj); + dispatch(setCurrentProject(proj)); + }; +} diff --git a/src/components/ProjectSettings/ProjectSettingsComponent.tsx b/src/components/ProjectSettings/ProjectSettingsComponent.tsx index ff27707ecb..e840ef3ac7 100644 --- a/src/components/ProjectSettings/ProjectSettingsComponent.tsx +++ b/src/components/ProjectSettings/ProjectSettingsComponent.tsx @@ -1,6 +1,5 @@ import { Archive, - Assignment, CalendarMonth, CloudUpload, Edit, @@ -21,10 +20,12 @@ import * as backend from "backend"; import { getCurrentUser } from "backend/localStorage"; import history, { Path } from "browserHistory"; import BaseSettingsComponent from "components/BaseSettings/BaseSettingsComponent"; -import { asyncRefreshCurrentProjectUsers } from "components/Project/ProjectActions"; +import { + asyncRefreshCurrentProjectUsers, + setDefinitionsEnabled, +} from "components/Project/ProjectActions"; import ExportButton from "components/ProjectExport/ExportButton"; import ProjectAutocomplete from "components/ProjectSettings/ProjectAutocomplete"; -import ProjectDefinitions from "components/ProjectSettings/ProjectDefinitions"; import ProjectImport from "components/ProjectSettings/ProjectImport"; import ProjectLanguages from "components/ProjectSettings/ProjectLanguages"; import ProjectName from "components/ProjectSettings/ProjectName"; @@ -37,6 +38,9 @@ import { StoreState } from "types"; import { useAppDispatch, useAppSelector } from "types/hooks"; export default function ProjectSettingsComponent() { + const definitionsEnabled = useAppSelector( + (state: StoreState) => state.currentProjectState.project.definitionsEnabled + ); const projectId = useAppSelector( (state: StoreState) => state.currentProjectState.project.id ); @@ -67,6 +71,14 @@ export default function ProjectSettingsComponent() { } }, [permissions, dispatch]); + useEffect(() => { + backend.doesProjHaveDefs().then(async (hasDefs) => { + if (hasDefs !== definitionsEnabled) { + await dispatch(setDefinitionsEnabled(hasDefs)); + } + }); + }, [definitionsEnabled, dispatch]); + function archiveUpdate() { toast.success(t("projectSettings.user.archiveToastSuccess")); setTimeout(() => { @@ -134,15 +146,6 @@ export default function ProjectSettingsComponent() { body={} /> - {/* Definitions toggle */} - {permissions.includes(Permission.DeleteEditSettingsAndUsers) && ( - } - title={t("projectSettings.definitions.label")} - body={} - /> - )} - {/* See current users in project */} {permissions.includes(Permission.DeleteEditSettingsAndUsers) && ( Date: Wed, 17 May 2023 12:24:44 -0400 Subject: [PATCH 2/8] Revert changes that will be replaced by a single database update --- Backend.Tests/Services/WordServiceTests.cs | 26 ---- Backend/Controllers/WordController.cs | 17 --- Backend/Interfaces/IWordService.cs | 1 - Backend/Services/WordService.cs | 9 -- src/api/api/word-api.ts | 114 ------------------ src/backend/index.ts | 6 - .../ProjectSettingsComponent.tsx | 27 ++--- 7 files changed, 12 insertions(+), 188 deletions(-) diff --git a/Backend.Tests/Services/WordServiceTests.cs b/Backend.Tests/Services/WordServiceTests.cs index 2420750080..2a01fbfbca 100644 --- a/Backend.Tests/Services/WordServiceTests.cs +++ b/Backend.Tests/Services/WordServiceTests.cs @@ -107,31 +107,5 @@ public void TestFindContainingWordSameVernEmptySensesSameDoms() var dupId = _wordService.FindContainingWord(newWord).Result; Assert.That(dupId, Is.EqualTo(oldWord.Id)); } - - [Test] - public void TestDoesProjectHaveDefinitionsFalse() - { - var word = Util.RandomWord(ProjId); - var sense = Util.RandomSense(); - sense.Definitions.Clear(); - word.Senses = new List { sense }; - var _ = _wordRepo.Create(word).Result; - - var hasDefs = _wordService.DoesProjectHaveDefinitions(ProjId).Result; - Assert.That(hasDefs, Is.False); - } - - [Test] - public void TestDoesProjectHaveDefinitionsTrue() - { - var word = Util.RandomWord(ProjId); - var sense = Util.RandomSense(); - sense.Definitions.Add(Util.RandomDefinition()); - word.Senses.Add(sense); - var _ = _wordRepo.Create(word).Result; - - var hasDefs = _wordService.DoesProjectHaveDefinitions(ProjId).Result; - Assert.That(hasDefs, Is.True); - } } } diff --git a/Backend/Controllers/WordController.cs b/Backend/Controllers/WordController.cs index 97d956eab6..f2b2fbb7ce 100644 --- a/Backend/Controllers/WordController.cs +++ b/Backend/Controllers/WordController.cs @@ -124,23 +124,6 @@ public async Task IsFrontierNonempty(string projectId) return Ok(await _wordRepo.IsFrontierNonempty(projectId)); } - /// Checks if project has any s in its frontier. - [HttpGet("doesprojecthavedefinitions", Name = "DoesProjectHaveDefinitions")] - [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(bool))] - public async Task DoesProjectHaveDefinitions(string projectId) - { - if (!await _permissionService.HasProjectPermission(HttpContext, Permission.WordEntry)) - { - return Forbid(); - } - var project = await _projRepo.GetProject(projectId); - if (project is null) - { - return NotFound(projectId); - } - return Ok(await _wordService.DoesProjectHaveDefinitions(projectId)); - } - /// Returns all Frontier in specified . [HttpGet("frontier", Name = "GetProjectFrontierWords")] [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(List))] diff --git a/Backend/Interfaces/IWordService.cs b/Backend/Interfaces/IWordService.cs index b195d81cf1..7df1234dd9 100644 --- a/Backend/Interfaces/IWordService.cs +++ b/Backend/Interfaces/IWordService.cs @@ -10,6 +10,5 @@ public interface IWordService Task FindContainingWord(Word word); Task Delete(string projectId, string wordId, string fileName); Task DeleteFrontierWord(string projectId, string wordId); - Task DoesProjectHaveDefinitions(string projectId); } } diff --git a/Backend/Services/WordService.cs b/Backend/Services/WordService.cs index c03516980a..d089a59c6e 100644 --- a/Backend/Services/WordService.cs +++ b/Backend/Services/WordService.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using System.Linq; using System.Threading.Tasks; using BackendFramework.Interfaces; using BackendFramework.Models; @@ -140,13 +139,5 @@ public async Task Update(string projectId, string wordId, Word word) var duplicatedWord = frontier.Find(w => w.Contains(word)); return duplicatedWord?.Id; } - - - /// Checks if project has any s in its frontier. - public async Task DoesProjectHaveDefinitions(string projectId) - { - var frontier = await _wordRepo.GetFrontier(projectId); - return frontier.Any(w => (w.Senses.Any(s => s.Definitions.Count > 0))); - } } } diff --git a/src/api/api/word-api.ts b/src/api/api/word-api.ts index 90a355cd71..cf0b8e3a41 100644 --- a/src/api/api/word-api.ts +++ b/src/api/api/word-api.ts @@ -195,52 +195,6 @@ export const WordApiAxiosParamCreator = function ( options: localVarRequestOptions, }; }, - /** - * - * @param {string} projectId - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - doesProjectHaveDefinitions: async ( - projectId: string, - options: any = {} - ): Promise => { - // verify required parameter 'projectId' is not null or undefined - assertParamExists("doesProjectHaveDefinitions", "projectId", projectId); - const localVarPath = - `/v1/projects/{projectId}/words/doesprojecthavedefinitions`.replace( - `{${"projectId"}}`, - encodeURIComponent(String(projectId)) - ); - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { - method: "GET", - ...baseOptions, - ...options, - }; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - setSearchParams(localVarUrlObj, localVarQueryParameter, options.query); - let headersFromBaseOptions = - baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = { - ...localVarHeaderParameter, - ...headersFromBaseOptions, - ...options.headers, - }; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, /** * * @param {string} projectId @@ -690,30 +644,6 @@ export const WordApiFp = function (configuration?: Configuration) { configuration ); }, - /** - * - * @param {string} projectId - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async doesProjectHaveDefinitions( - projectId: string, - options?: any - ): Promise< - (axios?: AxiosInstance, basePath?: string) => AxiosPromise - > { - const localVarAxiosArgs = - await localVarAxiosParamCreator.doesProjectHaveDefinitions( - projectId, - options - ); - return createRequestFunction( - localVarAxiosArgs, - globalAxios, - BASE_PATH, - configuration - ); - }, /** * * @param {string} projectId @@ -955,20 +885,6 @@ export const WordApiFactory = function ( .deleteProjectWords(projectId, options) .then((request) => request(axios, basePath)); }, - /** - * - * @param {string} projectId - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - doesProjectHaveDefinitions( - projectId: string, - options?: any - ): AxiosPromise { - return localVarFp - .doesProjectHaveDefinitions(projectId, options) - .then((request) => request(axios, basePath)); - }, /** * * @param {string} projectId @@ -1140,20 +1056,6 @@ export interface WordApiDeleteProjectWordsRequest { readonly projectId: string; } -/** - * Request parameters for doesProjectHaveDefinitions operation in WordApi. - * @export - * @interface WordApiDoesProjectHaveDefinitionsRequest - */ -export interface WordApiDoesProjectHaveDefinitionsRequest { - /** - * - * @type {string} - * @memberof WordApiDoesProjectHaveDefinitions - */ - readonly projectId: string; -} - /** * Request parameters for getDuplicateId operation in WordApi. * @export @@ -1360,22 +1262,6 @@ export class WordApi extends BaseAPI { .then((request) => request(this.axios, this.basePath)); } - /** - * - * @param {WordApiDoesProjectHaveDefinitionsRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof WordApi - */ - public doesProjectHaveDefinitions( - requestParameters: WordApiDoesProjectHaveDefinitionsRequest, - options?: any - ) { - return WordApiFp(this.configuration) - .doesProjectHaveDefinitions(requestParameters.projectId, options) - .then((request) => request(this.axios, this.basePath)); - } - /** * * @param {WordApiGetDuplicateIdRequest} requestParameters Request parameters. diff --git a/src/backend/index.ts b/src/backend/index.ts index b0355cc9ba..571136dbde 100644 --- a/src/backend/index.ts +++ b/src/backend/index.ts @@ -622,12 +622,6 @@ export async function deleteFrontierWord(wordId: string): Promise { return (await wordApi.deleteFrontierWord(params, defaultOptions())).data; } -export async function doesProjHaveDefs(projectId?: string): Promise { - const params = { projectId: projectId ?? LocalStorage.getProjectId() }; - return (await wordApi.doesProjectHaveDefinitions(params, defaultOptions())) - .data; -} - export async function getAllWords(): Promise { const projectId = LocalStorage.getProjectId(); return (await wordApi.getProjectWords({ projectId }, defaultOptions())).data; diff --git a/src/components/ProjectSettings/ProjectSettingsComponent.tsx b/src/components/ProjectSettings/ProjectSettingsComponent.tsx index e840ef3ac7..ff27707ecb 100644 --- a/src/components/ProjectSettings/ProjectSettingsComponent.tsx +++ b/src/components/ProjectSettings/ProjectSettingsComponent.tsx @@ -1,5 +1,6 @@ import { Archive, + Assignment, CalendarMonth, CloudUpload, Edit, @@ -20,12 +21,10 @@ import * as backend from "backend"; import { getCurrentUser } from "backend/localStorage"; import history, { Path } from "browserHistory"; import BaseSettingsComponent from "components/BaseSettings/BaseSettingsComponent"; -import { - asyncRefreshCurrentProjectUsers, - setDefinitionsEnabled, -} from "components/Project/ProjectActions"; +import { asyncRefreshCurrentProjectUsers } from "components/Project/ProjectActions"; import ExportButton from "components/ProjectExport/ExportButton"; import ProjectAutocomplete from "components/ProjectSettings/ProjectAutocomplete"; +import ProjectDefinitions from "components/ProjectSettings/ProjectDefinitions"; import ProjectImport from "components/ProjectSettings/ProjectImport"; import ProjectLanguages from "components/ProjectSettings/ProjectLanguages"; import ProjectName from "components/ProjectSettings/ProjectName"; @@ -38,9 +37,6 @@ import { StoreState } from "types"; import { useAppDispatch, useAppSelector } from "types/hooks"; export default function ProjectSettingsComponent() { - const definitionsEnabled = useAppSelector( - (state: StoreState) => state.currentProjectState.project.definitionsEnabled - ); const projectId = useAppSelector( (state: StoreState) => state.currentProjectState.project.id ); @@ -71,14 +67,6 @@ export default function ProjectSettingsComponent() { } }, [permissions, dispatch]); - useEffect(() => { - backend.doesProjHaveDefs().then(async (hasDefs) => { - if (hasDefs !== definitionsEnabled) { - await dispatch(setDefinitionsEnabled(hasDefs)); - } - }); - }, [definitionsEnabled, dispatch]); - function archiveUpdate() { toast.success(t("projectSettings.user.archiveToastSuccess")); setTimeout(() => { @@ -146,6 +134,15 @@ export default function ProjectSettingsComponent() { body={} /> + {/* Definitions toggle */} + {permissions.includes(Permission.DeleteEditSettingsAndUsers) && ( + } + title={t("projectSettings.definitions.label")} + body={} + /> + )} + {/* See current users in project */} {permissions.includes(Permission.DeleteEditSettingsAndUsers) && ( Date: Wed, 17 May 2023 12:26:12 -0400 Subject: [PATCH 3/8] ... one more --- src/components/Project/ProjectActions.ts | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/components/Project/ProjectActions.ts b/src/components/Project/ProjectActions.ts index 4d2ff3bbdf..7e7db766cf 100644 --- a/src/components/Project/ProjectActions.ts +++ b/src/components/Project/ProjectActions.ts @@ -5,7 +5,6 @@ import { ProjectAction, ProjectActionType, } from "components/Project/ProjectReduxTypes"; -import { StoreState } from "types"; import { StoreStateDispatch } from "types/Redux/actions"; export function setCurrentProject(payload?: Project): ProjectAction { @@ -50,14 +49,3 @@ export function setNewCurrentProject(project?: Project) { dispatch(setCurrentProject(project)); }; } - -export function setDefinitionsEnabled(definitionsEnabled: boolean) { - return async (dispatch: StoreStateDispatch, getState: () => StoreState) => { - const proj: Project = { - ...getState().currentProjectState.project, - definitionsEnabled, - }; - await updateProject(proj); - dispatch(setCurrentProject(proj)); - }; -} From 86fbbf0589d2f9e91c7a20668df9dd2fe54092a0 Mon Sep 17 00:00:00 2001 From: "D. Ror" Date: Wed, 17 May 2023 12:29:17 -0400 Subject: [PATCH 4/8] Remove ProjectDefinitions setting --- .../ProjectSettings/ProjectDefinitions.tsx | 52 ------------------- .../ProjectSettingsComponent.tsx | 11 ---- 2 files changed, 63 deletions(-) delete mode 100644 src/components/ProjectSettings/ProjectDefinitions.tsx diff --git a/src/components/ProjectSettings/ProjectDefinitions.tsx b/src/components/ProjectSettings/ProjectDefinitions.tsx deleted file mode 100644 index d4f4a638b9..0000000000 --- a/src/components/ProjectSettings/ProjectDefinitions.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import { HelpOutline } from "@mui/icons-material"; -import { - Grid, - MenuItem, - Select, - SelectChangeEvent, - Tooltip, -} from "@mui/material"; -import { useTranslation } from "react-i18next"; - -import { saveChangesToProject } from "components/Project/ProjectActions"; -import { StoreState } from "types"; -import { useAppDispatch, useAppSelector } from "types/hooks"; - -export default function ProjectDefinitions() { - const project = useAppSelector( - (state: StoreState) => state.currentProjectState.project - ); - const dispatch = useAppDispatch(); - const { t } = useTranslation(); - - return ( - - - - - - - - - - - ); -} diff --git a/src/components/ProjectSettings/ProjectSettingsComponent.tsx b/src/components/ProjectSettings/ProjectSettingsComponent.tsx index ff27707ecb..34c3cb1a15 100644 --- a/src/components/ProjectSettings/ProjectSettingsComponent.tsx +++ b/src/components/ProjectSettings/ProjectSettingsComponent.tsx @@ -1,6 +1,5 @@ import { Archive, - Assignment, CalendarMonth, CloudUpload, Edit, @@ -24,7 +23,6 @@ import BaseSettingsComponent from "components/BaseSettings/BaseSettingsComponent import { asyncRefreshCurrentProjectUsers } from "components/Project/ProjectActions"; import ExportButton from "components/ProjectExport/ExportButton"; import ProjectAutocomplete from "components/ProjectSettings/ProjectAutocomplete"; -import ProjectDefinitions from "components/ProjectSettings/ProjectDefinitions"; import ProjectImport from "components/ProjectSettings/ProjectImport"; import ProjectLanguages from "components/ProjectSettings/ProjectLanguages"; import ProjectName from "components/ProjectSettings/ProjectName"; @@ -134,15 +132,6 @@ export default function ProjectSettingsComponent() { body={} /> - {/* Definitions toggle */} - {permissions.includes(Permission.DeleteEditSettingsAndUsers) && ( - } - title={t("projectSettings.definitions.label")} - body={} - /> - )} - {/* See current users in project */} {permissions.includes(Permission.DeleteEditSettingsAndUsers) && ( Date: Wed, 17 May 2023 12:40:47 -0400 Subject: [PATCH 5/8] Add tests --- .../Controllers/LiftControllerTests.cs | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/Backend.Tests/Controllers/LiftControllerTests.cs b/Backend.Tests/Controllers/LiftControllerTests.cs index 20b0f6512a..189cc2de10 100644 --- a/Backend.Tests/Controllers/LiftControllerTests.cs +++ b/Backend.Tests/Controllers/LiftControllerTests.cs @@ -419,6 +419,70 @@ public void TestRoundtrip(RoundTripObj roundTripObj) } } + [Test] + public void TestHasNoDefinitions() + { + // This test assumes you have the starting .zip (filename) included in your project files. + var filename = "SingleEntryLiftWithSound.zip"; + var pathToStartZip = Path.Combine(Util.AssetsDir, filename); + Assert.IsTrue(File.Exists(pathToStartZip)); + + // Init the project the .zip info is added to. + var proj1 = _projRepo.Create(Util.RandomProject()).Result; + + // Upload the zip file. + // Generate api parameter with filestream. + using (var stream = File.OpenRead(pathToStartZip)) + { + var fileUpload = InitFile(stream, filename); + + // Make api call. + var result = _liftController.UploadLiftFile(proj1!.Id, fileUpload).Result; + Assert.That(result is OkObjectResult); + } + + proj1 = _projRepo.GetProject(proj1.Id).Result; + if (proj1 is null) + { + Assert.Fail(); + return; + } + + Assert.That(proj1.DefinitionsEnabled, Is.False); + } + + [Test] + public void TestHasDefinitions() + { + // This test assumes you have the starting .zip (filename) included in your project files. + var filename = "Natqgu.zip"; + var pathToStartZip = Path.Combine(Util.AssetsDir, filename); + Assert.IsTrue(File.Exists(pathToStartZip)); + + // Init the project the .zip info is added to. + var proj1 = _projRepo.Create(Util.RandomProject()).Result; + + // Upload the zip file. + // Generate api parameter with filestream. + using (var stream = File.OpenRead(pathToStartZip)) + { + var fileUpload = InitFile(stream, filename); + + // Make api call. + var result = _liftController.UploadLiftFile(proj1!.Id, fileUpload).Result; + Assert.That(result is OkObjectResult); + } + + proj1 = _projRepo.GetProject(proj1.Id).Result; + if (proj1 is null) + { + Assert.Fail(); + return; + } + + Assert.That(proj1.DefinitionsEnabled, Is.True); + } + private class MockLogger : ILogger { public IDisposable BeginScope(TState state) From 5d3d3241738fd48bf9d1b7f9750b955317c6daa0 Mon Sep 17 00:00:00 2001 From: "D. Ror" Date: Wed, 17 May 2023 12:57:58 -0400 Subject: [PATCH 6/8] Remove number --- .../Controllers/LiftControllerTests.cs | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/Backend.Tests/Controllers/LiftControllerTests.cs b/Backend.Tests/Controllers/LiftControllerTests.cs index 189cc2de10..aca89a9a70 100644 --- a/Backend.Tests/Controllers/LiftControllerTests.cs +++ b/Backend.Tests/Controllers/LiftControllerTests.cs @@ -428,7 +428,9 @@ public void TestHasNoDefinitions() Assert.IsTrue(File.Exists(pathToStartZip)); // Init the project the .zip info is added to. - var proj1 = _projRepo.Create(Util.RandomProject()).Result; + var proj = Util.RandomProject(); + proj.VernacularWritingSystem.Bcp47 = "qaa"; + proj = _projRepo.Create(proj).Result; // Upload the zip file. // Generate api parameter with filestream. @@ -437,18 +439,18 @@ public void TestHasNoDefinitions() var fileUpload = InitFile(stream, filename); // Make api call. - var result = _liftController.UploadLiftFile(proj1!.Id, fileUpload).Result; + var result = _liftController.UploadLiftFile(proj!.Id, fileUpload).Result; Assert.That(result is OkObjectResult); } - proj1 = _projRepo.GetProject(proj1.Id).Result; - if (proj1 is null) + proj = _projRepo.GetProject(proj.Id).Result; + if (proj is null) { Assert.Fail(); return; } - Assert.That(proj1.DefinitionsEnabled, Is.False); + Assert.That(proj.DefinitionsEnabled, Is.False); } [Test] @@ -460,7 +462,9 @@ public void TestHasDefinitions() Assert.IsTrue(File.Exists(pathToStartZip)); // Init the project the .zip info is added to. - var proj1 = _projRepo.Create(Util.RandomProject()).Result; + var proj = Util.RandomProject(); + proj.VernacularWritingSystem.Bcp47 = "qaa"; + proj = _projRepo.Create(proj).Result; // Upload the zip file. // Generate api parameter with filestream. @@ -469,18 +473,18 @@ public void TestHasDefinitions() var fileUpload = InitFile(stream, filename); // Make api call. - var result = _liftController.UploadLiftFile(proj1!.Id, fileUpload).Result; + var result = _liftController.UploadLiftFile(proj!.Id, fileUpload).Result; Assert.That(result is OkObjectResult); } - proj1 = _projRepo.GetProject(proj1.Id).Result; - if (proj1 is null) + proj = _projRepo.GetProject(proj.Id).Result; + if (proj is null) { Assert.Fail(); return; } - Assert.That(proj1.DefinitionsEnabled, Is.True); + Assert.That(proj.DefinitionsEnabled, Is.True); } private class MockLogger : ILogger From a3d3f8990a5874874979251a0fa0a251c3b281dc Mon Sep 17 00:00:00 2001 From: "D. Ror" Date: Thu, 18 May 2023 10:27:28 -0400 Subject: [PATCH 7/8] Combine like tests --- .../Controllers/LiftControllerTests.cs | 59 +++++++------------ 1 file changed, 21 insertions(+), 38 deletions(-) diff --git a/Backend.Tests/Controllers/LiftControllerTests.cs b/Backend.Tests/Controllers/LiftControllerTests.cs index aca89a9a70..ece9844baa 100644 --- a/Backend.Tests/Controllers/LiftControllerTests.cs +++ b/Backend.Tests/Controllers/LiftControllerTests.cs @@ -165,6 +165,18 @@ public RoundTripObj( } } + public class DefinitionImportObj + { + public string Filename { get; } + public bool HasDefinitions { get; } + + public DefinitionImportObj(string filename, bool hasDefinitions) + { + Filename = filename; + HasDefinitions = hasDefinitions; + } + } + /// Read all of the bytes from a Stream into byte array. private static byte[] ReadAllBytes(Stream stream) { @@ -419,46 +431,17 @@ public void TestRoundtrip(RoundTripObj roundTripObj) } } - [Test] - public void TestHasNoDefinitions() + private static DefinitionImportObj[] _defImportCases = { - // This test assumes you have the starting .zip (filename) included in your project files. - var filename = "SingleEntryLiftWithSound.zip"; - var pathToStartZip = Path.Combine(Util.AssetsDir, filename); - Assert.IsTrue(File.Exists(pathToStartZip)); - - // Init the project the .zip info is added to. - var proj = Util.RandomProject(); - proj.VernacularWritingSystem.Bcp47 = "qaa"; - proj = _projRepo.Create(proj).Result; - - // Upload the zip file. - // Generate api parameter with filestream. - using (var stream = File.OpenRead(pathToStartZip)) - { - var fileUpload = InitFile(stream, filename); - - // Make api call. - var result = _liftController.UploadLiftFile(proj!.Id, fileUpload).Result; - Assert.That(result is OkObjectResult); - } - - proj = _projRepo.GetProject(proj.Id).Result; - if (proj is null) - { - Assert.Fail(); - return; - } - - Assert.That(proj.DefinitionsEnabled, Is.False); - } + new("qaa-x-stc-natqgu", true), + new("SingleEntryLiftWithSound.zip", false) + }; - [Test] - public void TestHasDefinitions() + [TestCaseSource(nameof(_defImportCases))] + public void TestHasDefinitions(DefinitionImportObj defImportObj) { // This test assumes you have the starting .zip (filename) included in your project files. - var filename = "Natqgu.zip"; - var pathToStartZip = Path.Combine(Util.AssetsDir, filename); + var pathToStartZip = Path.Combine(Util.AssetsDir, defImportObj.Filename); Assert.IsTrue(File.Exists(pathToStartZip)); // Init the project the .zip info is added to. @@ -470,7 +453,7 @@ public void TestHasDefinitions() // Generate api parameter with filestream. using (var stream = File.OpenRead(pathToStartZip)) { - var fileUpload = InitFile(stream, filename); + var fileUpload = InitFile(stream, defImportObj.Filename); // Make api call. var result = _liftController.UploadLiftFile(proj!.Id, fileUpload).Result; @@ -484,7 +467,7 @@ public void TestHasDefinitions() return; } - Assert.That(proj.DefinitionsEnabled, Is.True); + Assert.AreEqual(proj.DefinitionsEnabled, defImportObj.HasDefinitions); } private class MockLogger : ILogger From aa47938d1ba634a23dfb8b6184bd68d3a56c43ce Mon Sep 17 00:00:00 2001 From: "D. Ror" Date: Thu, 18 May 2023 10:31:24 -0400 Subject: [PATCH 8/8] Fix filename --- Backend.Tests/Controllers/LiftControllerTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Backend.Tests/Controllers/LiftControllerTests.cs b/Backend.Tests/Controllers/LiftControllerTests.cs index ece9844baa..be0795d72d 100644 --- a/Backend.Tests/Controllers/LiftControllerTests.cs +++ b/Backend.Tests/Controllers/LiftControllerTests.cs @@ -433,7 +433,7 @@ public void TestRoundtrip(RoundTripObj roundTripObj) private static DefinitionImportObj[] _defImportCases = { - new("qaa-x-stc-natqgu", true), + new("Natqgu.zip", true), new("SingleEntryLiftWithSound.zip", false) };