Skip to content
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

Trait-db v0 #722

Merged
merged 55 commits into from
Feb 4, 2025
Merged

Trait-db v0 #722

merged 55 commits into from
Feb 4, 2025

Conversation

Blodir
Copy link
Contributor

@Blodir Blodir commented Jan 28, 2025

This branch includes changes to three projects (my bad):

  • Laji-api-client-b (small) (reviewer: Olli)
  • Laji-ui stuff (reviewer: Meeri)
    • note: lu-form-string-list has no styling yet, but the code can be reviewed
  • Trait-db (reviewer: Robert)
    • there are no translation keys yet
    • trait-search-filters are missing many filters and datatable columns (I'm looking into generating these from typescript AST)
    • trait-db-dataset html doesnt need to be checked, the code should be
    • TraitDbNewTraitComponent doesn't need to be checked

Each could be reviewed separately by different people

TODO

  • Hide in production
  • Resolve conflicts
  • Write this description x)
  • I need to check all the changes and cleanup any clearly in progress stuff if necessary
  • strict types
  • lint
  • Remove old laji-api-client?

@Blodir Blodir marked this pull request as ready for review January 30, 2025 08:07
@aorin aorin self-assigned this Jan 30, 2025
@rpulkka rpulkka self-requested a review January 30, 2025 08:56
@rpulkka rpulkka self-assigned this Jan 30, 2025
@rpulkka rpulkka requested review from aorin and olzraiti January 30, 2025 08:58
Copy link
Contributor

@aorin aorin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looked through the laji-ui project, looks good, left some small improvement suggestions

projects/laji-ui/src/lib/mixins/_fieldset.scss Outdated Show resolved Hide resolved
@@ -0,0 +1,5 @@
<input type="text" (blur)="onTouched(value)" #textInput>
<ul>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like this is about the same as the pill-list (in shared-modules). Having almost the same functionality in many places makes the project more complex and harder to keep the styles uniform. Maybe not in this PR but in the future it would be good to check if it's easy to refactor it so that all the places that use this functionality would use the same component.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

string-list implements ControlValueAccessor, while pill-list is literally just a list (non-semantic html one at that) where you can remove items. So they are different. In the future I'd like to make an enum-list that would replace pill-list in many (all?) places e.g. taxon search in observation form. The 'pills/tags' sort of UI also works better for enums than for arbitrary strings.

Btw, I think string-list could probably use a rename, maybe string-list-input and enum-list-input or something? Or input-string-list and input-enum-list

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good. I'd prefer the name string-list-input instead of enum-list-input since they are not always basic enums like the taxon search

Copy link
Contributor Author

@Blodir Blodir Jan 31, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean string-list-input and enum-list-input would be different components. string-list-input for arbitrary strings and enum-list-input for, well, enums.

projects/laji-ui/src/lib/datatable/datatable.component.ts Outdated Show resolved Hide resolved
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, EventEmitter, Inject, Input,
OnChanges, OnInit, Output, QueryList, Renderer2, RendererStyleFlags2, SimpleChanges, TemplateRef, ViewChild, ViewChildren } from '@angular/core';

type Keyable = string | number | symbol;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could consider splitting the code into multiple files at least in the future since it's quite big already and probably going to get bigger if this datatable is going to be used in all places. For example the models could go to their own file and sorting functions to their own and the pagination to it's own component.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've extracted the paginator into its own component. However the rest of the stuff I have to disagree with, imo splitting files is unnecessary when it doesn't come naturally. Eg. the type defs are directly describing the component so there's no reason they should be namespaced somewhere else.

Btw, I'm hoping that the datatable won't get any (or at least much) bigger.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah it's a matter of preference. The reason why I like having the types elsewhere is that it's easier to see the example usage comment or the inputs when they are at the top of the file and that's what I'm usually interested about

Copy link
Contributor

@rpulkka rpulkka left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed Trait-db, and found no issues. Great work!

Comment on lines 56 to 66
fetch<
T extends keyof paths & string,
U extends keyof paths[T] & string,
Responses extends WithResponses<paths[T][U]>['responses']
>(
endpoint: T,
method: U,
params: Parameters<paths[T][U]>,
requestBody?: ExtractRequestBodyIfExists<paths[T][U]>,
cacheInvalidationMs = 86400000 // 1day in ms
): Observable<ExtractContentIfExists<Responses[IntersectUnionTypes<keyof Responses, HttpSuccessCodes>]>> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

path would be more accurate term than endpoint. Endpoint would be method + baseUrl + path.

It takes some mental effort to read paths[T][U]. It could help if the generic names would be P and M or even Path and Method.

params could be left optional to simplify usage.

U allows now btw the parameters property of the paths... Not really a practical problem, but could be tightened.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I renamed the types and tightened the type bounds for the method type. It now accepts only keys that are not undefined or never, and excludes "parameters".

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we have params as optional, then you could theoretically give erroneous input if the openapi schema requires that params are present. It's quite annoying that you have to add the argument to each call even if the type of params is undefined, but wcyd 🤷

Copy link
Member

@olzraiti olzraiti Feb 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image
I tested how openapi-fetch works. In the image you can see that it works correct: I tested with GET /collections and GET /documents: the latter throws an error because it has a required parameter. We could maybe look at how it works.

But I'm ok with approving this, and we can continue improving the client as we go.

@Blodir Blodir requested a review from aorin January 31, 2025 10:18
Copy link
Contributor

@aorin aorin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The changes looks good, thanks!

<ng-container
*ngTemplateOutlet="columns[colIdx]['cellTemplate'];
context: columns[colIdx].prop
? { $implicit: row, prop: getNestedValue(row, columns[colIdx]['prop']) }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prop is a little confusing name, since prop is also the property name (=columns[colIdx].prop)

@Blodir Blodir requested a review from olzraiti February 3, 2025 16:21
@Blodir Blodir added this to the Release 79 milestone Feb 4, 2025
Copy link
Member

@olzraiti olzraiti left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left one more change request. Otherwise looking good 👍

}
```

Queries are cached by default based on the path and the query params. Whenever a new non-get request is sent to the same path, the cache is flushed automatically.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is out of date. If it's hard to document, maybe put a link to the review discussion that explains it? Or just remove it?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or idk... it's not entirely out of date. It says "based on the path", which is correct. I mean it doesn't mention that the path params affect it also. Maybe we can update the README after we develop the other changes (like method wrappers get() put() etc)

// resolve path parameters (eg. replace '/{personToken}' with params.path.personToken)
for (let i = 1; i < segments.length; i++) {
const segment = segments[i];
segments[i] = segment.replace(/\{([^}]+)\}/, (match, p1) => (params as any)['path'][p1] ?? '');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a bit worried about this. An empty string path param would cause a wrong endpoint being called, which would be a disaster. Could we throw an error for undefined values and empty strings, instead of doing ?? ''?

I think undefined values is also good to check, because we have so many cases of using non-null assertion for instance variables.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In practice the ?? '' bit should never be reached though, because of the strict typing in params 🤔

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The strict typing doesn't protect against an empty string. An empty string will cause a wrong endpoint being called (for example trying to call GET /a/:id but actually calls GET /a/, which equals GET /a)

And I think undefined is also still quite possible to reach here, as there are so many non-null assertions etc in the repo

@Blodir Blodir mentioned this pull request Feb 4, 2025
2 tasks
@Blodir Blodir merged commit 575bae1 into development Feb 4, 2025
1 check passed
@Blodir Blodir deleted the trait-db-186748322 branch February 4, 2025 15:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants