-
Notifications
You must be signed in to change notification settings - Fork 4
ILEX-136 replace elasticsearch with entity-check #131
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR replaces the elasticsearch-based term matching system with a new entity-check API to identify potential term matches. When no exact matches are found through entity-check, the system falls back to elasticsearch for searching terms that can be selected for editing.
- Replace elasticsearch search with entity-check API for finding potential matches
- Enable term editing flow by making sidebar term items clickable
- Add fallback to elasticsearch when no exact matches found via entity-check
Reviewed Changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| src/constants/types.js | Adds term type constants and exports for OWL classes and properties |
| src/config.js | Adds CHECK_ENTITY endpoint configuration for the new API |
| src/components/TermEditor/newTerm/SecondStepContent.jsx | Updates component to accept searchTerm prop and use it in predicate initialization |
| src/components/TermEditor/newTerm/FirstStepContent.jsx | Major refactor to use entity-check API with elasticsearch fallback and enable editing mode |
| src/components/TermEditor/newTerm/AddNewTermDialog.jsx | Adds editing state management and switches between create/edit modes |
| src/components/TermEditor/NewTermSidebar.jsx | Improves loading states with skeletons and makes non-exact match items clickable |
| src/api/endpoints/apiService.ts | Adds checkPotentialMatches function and refactors createNewEntity to handle redirects properly |
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
| const matchList = Array.isArray(matches) ? matches : [matches]; | ||
| return matchList.map(match => ({ | ||
| ilx: termUri.split('/').pop(), | ||
| label: match.object, |
Copilot
AI
Sep 15, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The code assumes match has an object property when creating the label field, but this property might not exist. This could result in undefined labels for exact matches.
| label: match.object, | |
| label: match.object ?? termUri.split('/').pop() ?? "Unknown", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Aiga115 take this into account as well, thanks!
| '&:hover': { backgroundColor: "transparent" } | ||
| }} | ||
| onClick={() => onResultAction(result)} | ||
| onClick={() => window.location.href = `/${user.groupname}/${result.ilx}`} |
Copilot
AI
Sep 15, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using window.location.href for navigation breaks the React Router pattern and causes a full page reload. Consider using useNavigate hook from React Router instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Aiga115 I agree with copilot suggestion here, thanks!
src/api/endpoints/apiService.ts
Outdated
|
|
||
| return new Promise((resolve, reject) => { | ||
| const xhr = new XMLHttpRequest(); | ||
|
|
||
| xhr.open('POST', endpoint, true); | ||
| xhr.setRequestHeader('Content-Type', 'application/json'); | ||
| xhr.withCredentials = true; | ||
|
|
||
| xhr.onreadystatechange = function() { | ||
| if (xhr.readyState === XMLHttpRequest.HEADERS_RECEIVED) { | ||
| if (xhr.status === 303) { | ||
| const redirectUrl = xhr.getResponseHeader('x-redirect-location') || xhr.getResponseHeader('Location'); | ||
| if (redirectUrl) { | ||
| const tmpMatch = redirectUrl.match(/tmp_\d{9}/); | ||
| console.log("Found tmp ID:", tmpMatch?.[0]); | ||
|
|
||
| if (tmpMatch) { | ||
| resolve({ | ||
| term: { | ||
| id: tmpMatch[0] | ||
| } | ||
| }); | ||
| return; | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // Otherwise, return response as-is | ||
| return response; | ||
| } catch (error) { | ||
| if (error?.response.status === 409) { | ||
| const match = error?.response?.data?.existing?.[0]; | ||
| if (match) { | ||
| return { | ||
| term: { | ||
| id: `${match}`, | ||
| }, | ||
| raw: error?.response, | ||
| status: error?.response?.status, | ||
| }; | ||
| if (xhr.readyState === XMLHttpRequest.DONE) { | ||
| if (xhr.status === 0) { | ||
| // Try to find tmp ID in the response text | ||
| const tmpMatch = xhr.responseText?.match(/tmp_\d{9}/); | ||
| if (tmpMatch) { | ||
| resolve({ | ||
| term: { | ||
| id: tmpMatch[0] | ||
| }, | ||
| raw: xhr.responseText, | ||
| status: 303 | ||
| }); | ||
| return; | ||
| } | ||
| } | ||
|
|
||
| // Handle 409 Conflict | ||
| if (xhr.status === 409) { | ||
| try { | ||
| const responseData = JSON.parse(xhr.responseText); | ||
| const match = responseData?.existing?.[0]; | ||
| if (match) { | ||
| resolve({ | ||
| term: { | ||
| id: match | ||
| }, | ||
| raw: responseData, | ||
| status: xhr.status | ||
| }); | ||
| return; | ||
| } | ||
| } catch (e) { | ||
| console.error("Error parsing 409 response:", e); | ||
| } | ||
| } | ||
|
|
||
| resolve({ | ||
| raw: xhr.responseText, | ||
| status: xhr.status | ||
| }); | ||
| } | ||
| } | ||
| }; | ||
|
|
||
| return { | ||
| raw: error?.response, | ||
| status: error?.response?.status, | ||
| xhr.onerror = function() { | ||
| console.error("XHR Error:", xhr.status, xhr.statusText); | ||
| reject(new Error('Network request failed')); | ||
| }; | ||
| } | ||
|
|
||
| }; | ||
| xhr.send(JSON.stringify(data)); | ||
| }); |
Copilot
AI
Sep 15, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using XMLHttpRequest directly instead of the existing createPostRequest function breaks consistency with the rest of the codebase. Consider modifying createPostRequest to handle redirects instead of implementing a custom XHR solution.
| const navigate = useNavigate(); | ||
|
|
||
| const isCreateButtonDisabled = hasExactMatch || termValue === ""; | ||
| const isCreateButtonDisabled = hasExactMatch || termValue === "" || (isEditing && termValue === selectedTermValue); |
Copilot
AI
Sep 15, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] The button disabled logic is complex and could be extracted into a separate function or useMemo for better readability and testability.
src/api/endpoints/apiService.ts
Outdated
| const endpoint = `/${group}${API_CONFIG.REAL_API.CREATE_NEW_ENTITY}`; | ||
|
|
||
| return new Promise((resolve, reject) => { | ||
| const xhr = new XMLHttpRequest(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Aiga115 why do not parametrise the content-type of createPostRequest but re-implement the entire request inside this function?
src/api/endpoints/apiService.ts
Outdated
|
|
||
| xhr.onreadystatechange = function() { | ||
| if (xhr.readyState === XMLHttpRequest.HEADERS_RECEIVED) { | ||
| if (xhr.status === 303) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Following the comment above, we should be able to handle these cases as part of the response of the createPostRequest rather than re-implementing ex novo the request on a case basis, so that all the code in common across requests will be handled by a single function and we avoid to duplicate code.
| }; | ||
|
|
||
| export const checkPotentialMatches = async (group: string, data: any) => { | ||
| return createPostRequest<any, any>(`/${group}${API_CONFIG.REAL_API.CHECK_ENTITY}`, { "Content-Type": "application/json" })(data); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍🏽
| '&:hover': { backgroundColor: "transparent" } | ||
| }} | ||
| onClick={() => onResultAction(result)} | ||
| onClick={() => window.location.href = `/${user.groupname}/${result.ilx}`} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Aiga115 I agree with copilot suggestion here, thanks!
| const matchList = Array.isArray(matches) ? matches : [matches]; | ||
| return matchList.map(match => ({ | ||
| ilx: termUri.split('/').pop(), | ||
| label: match.object, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Aiga115 take this into account as well, thanks!
Interlex.webm@Aiga115 when selecting a potential match from the sidebar the sidebar itself disappear (check the recording), this is not expected, the user might also have selected the wrong item, the sidebar needs to stay there and visible. |
|
@Aiga115 upon triggering the debounce, the elasticsearch endpoint is hit a second time that is unnecessary since the query term requested is the same, please fix this as well. Thanks Screencast.from.15-09-25.23.09.57.webm |
src/api/endpoints/apiActions.ts
Outdated
| }); | ||
|
|
||
| // Handle 303 redirect | ||
| if (response.status === 303) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This logic should be injected from whatever component calls the createPostRequest and wants to handle this differently, modifying this into this call implies that all the components in the application that are using the createPostRequest will have to behave the same way the add new term component needs to. Please fix this.
src/api/endpoints/apiActions.ts
Outdated
|
|
||
| // Handle 409 Conflict | ||
| if (response.status === 409) { | ||
| const responseData = await response.json(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same here
Issue #ILEX-136
Problem: replace elasticsearch with entity-check
Solution:
Note: We don't have api call for editing so now editTerm function just logs the message. Has to be connected later
Result:
Recording.2025-09-09.141349.mp4
Recording.2025-09-09.141502.mp4