-
-
Notifications
You must be signed in to change notification settings - Fork 2.6k
feat(content-provider): supports search cards and access card details by id #20175
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
base: main
Are you sure you want to change the base?
Conversation
david-allison
left a comment
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 ContentProvider needs heavy testing as it's our public API
Please add tests for all additional functionality
I'd strongly suggest splitting this PR into two parts:
- Adding the public
/cards/endpoints: easy - Adding additional properties to
/cards: harder
Specifically for (2), I would like to see the source/docs of upstream Anki Desktop AnkiConnect, and how the data is modelled there. This is how people are used to consuming our data.
| /** | ||
| * Due date for this card (day for review, timestamp for learning). | ||
| */ | ||
| public const val DUE: String = "due" |
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 is a bad abstraction in the database, we probably want to expose something more useable publicly
| /** | ||
| * Interval in days since last review. | ||
| */ | ||
| public const val INTERVAL: String = "interval" |
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.
ditto: this is optional
| public const val INTERVAL: String = "interval" | ||
|
|
||
| /** | ||
| * Ease factor (0-2500, divide by 1000 for actual ease). |
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.
An output of 2500 is confusing
No explanation of what this does under FSRS
| val cardIds = col.findCards(query) | ||
|
|
||
| // Fast path: Only if _id is requested | ||
| val onlyRequestingId = columns.size == 1 && columns[0] == FlashCardsContract.Card._ID |
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.
.singleOrNull
| // Search for cards using Anki browser syntax | ||
| val columns = projection ?: FlashCardsContract.Card.DEFAULT_PROJECTION | ||
| val query = selection ?: "" | ||
| val cardIds = col.findCards(query) |
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 can throw on bad syntax: and throws (I believe).
Ensure we throw consistent exceptions from the API, and that exceptions are appropriately tested and documented
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.
returns empty list on a bad query
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 isn't documented in the tests.
I also don't think it's correct, looking at the implementation of findCards
If it is correct, it doesn't appear to be consistent with our other endpoints.
Regardless
- add a test, with a search of
and - update the KDoc on
findCards
For this, separate PR would be better |
|
Not sure how to read the comment, but just to confirm: Content Provider PRs need to include tests. It's not okay to submit them later |
This comment was marked as resolved.
This comment was marked as resolved.
|
Two PRs |
david-allison
left a comment
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.
Thanks! Much easier to get through.
The returned columns aren't asserted on
| // Search for cards using Anki browser syntax | ||
| val columns = projection ?: FlashCardsContract.Card.DEFAULT_PROJECTION | ||
| val query = selection ?: "" | ||
| val cardIds = col.findCards(query) |
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 isn't documented in the tests.
I also don't think it's correct, looking at the implementation of findCards
If it is correct, it doesn't appear to be consistent with our other endpoints.
Regardless
- add a test, with a search of
and - update the KDoc on
findCards
| "999999999", | ||
| ) | ||
|
|
||
| assertThrows<BackendNotFoundException> { |
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 is asserted in other places
We'll probably want to document it, and tighten this down later. RuntimeException was previously asserted
| } | ||
|
|
||
| @Test | ||
| fun testSearchCards_returnsMatchingCards() { |
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 intention of this test is unclear:
- Are you adding a note with one, or two cards?
The test would pass with both, you might want to disambiguate this into two tests
|
|
||
| /** | ||
| * The content:// style URI for cards. Can be used to search for cards or access specific cards. | ||
| * For examples on how to use the URI for queries see class description. |
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.
consider linking the class description
| */ | ||
| public object Card { | ||
| /** | ||
| * This is the ID of the card in the Anki database. |
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 is the ID of the card in the Anki database. | |
| * The ID of the card in the Anki database. |
| val cardId = uri.pathSegments[1].toLong() | ||
| val columns = projection ?: FlashCardsContract.Card.DEFAULT_PROJECTION | ||
| val rv = MatrixCursor(columns, 1) | ||
| val card = col.getCard(cardId) |
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.
Update the documentation on getCard: @throws
| private const val CARDS = 1100 | ||
| private const val CARD_ID = 1101 |
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.
Why are you reusing the 1 prefix?
Purpose / Description
Expand CardContentProvider to return card IDs and cardinfo details
Fixes
Approach
I copy-pasted the proposed solution by @joaquintoral. And understood how it works on external app like AnkiConnectAndroid and test it properly.
How Has This Been Tested?
FindCards - /cards
response in logcat:
FindCardByID - /cards/#
response in logcat:
Learning (optional, can help others)
This was completely new for me. I haven’t worked on scenarios where an external app consumes our APIs before, so exploring this is really interesting
Checklist
Please, go through these checks before submitting the PR.