proper js API#11
proper js API#11johan-bjareholt merged 6 commits intoActivityWatch:masterfrom mathieudutour:proper-js-api
Conversation
johan-bjareholt
left a comment
There was a problem hiding this comment.
Awesome! :)
This PR makes it pretty clear that it's our first time making a API library in JavaScript :p
Just a few minor comments on the change
Before merging this we need to fix compatibility with aw-webui and aw-watcher-vscode. Hopefully the API is good enough after this that we won't have to break compatibility.
| @@ -1,24 +1,25 @@ | |||
| { | |||
| "name": "aw-client-js", | |||
| "name": "activity-watch-client", | |||
There was a problem hiding this comment.
All other activitywatch libraries are named "aw-x", please don't rename it
There was a problem hiding this comment.
Indeed, name should be aw-client
There was a problem hiding this comment.
just to explain a bit more why I renamed it: I keep reading aw-client as aws-client which also have AWSClient class. I was tempted to rename it to just activity-watch (same reason as for dropping the js). I understand that the other libraries are aw-* but they are mostly internals. I think using a more explicit name would help a lot to improve code readability of third party code where aw-* could co-exist with aws-* (and let's be honest, aws is a giant beast and much more likely to appear ;) )
That being said, happy to change it back if the consensus is to keep aw-client
There was a problem hiding this comment.
aws-client is extremely unlikely to appear in the same codebase as aw-client. aw-client is what we're going with.
| "scripts": { | ||
| "compile": "tsc", | ||
| "test": "./test.sh" | ||
| "compile": "(rm -rf out || true) && tsc", |
There was a problem hiding this comment.
No need to rm the dir, so don't.
| } = {}; | ||
|
|
||
| constructor(clientname: string, testing: boolean, baseurl: string | undefined = undefined) { | ||
| constructor(clientname: string, options?: {testing?: boolean, baseURL?: string}) { |
There was a problem hiding this comment.
Why options object instead?
There was a problem hiding this comment.
I think this is a good change, see: https://stackoverflow.com/questions/12826977/multiple-arguments-vs-options-object
There was a problem hiding this comment.
Aha, so it's rather that the limitation of not being of able to use kwargs in JS can make it hard to read since you cannot annotate the args in a nice manner. In that case it seems sane.
There was a problem hiding this comment.
yeah, as a rule of thumb, if you have more than 2 arguments (especially if they are optional), a options object will be more readable
|
|
||
| createBucket(bucket_id: string, type: string, hostname: string) { | ||
| return this.req.post('/0/buckets/' + bucket_id, { | ||
| ensureBucket(bucketId: string, type: string, hostname: string): Promise<{ alreadyExist: boolean }> { |
| }); | ||
| type, | ||
| hostname, | ||
| }).then(() => undefined); |
There was a problem hiding this comment.
What's the point of .then(() => undefined)?
There was a problem hiding this comment.
otherwise it's returning the axios response which is an implementation detail and shouldn't be exposed
It's fine, I'd preferably want a official one named aw-client or aw-client-js later though. |
It's fine that you did that given the namechange, but I'm not fine with you being an owner of the official package. I registered a package with the proper name (aw-client): https://www.npmjs.com/package/aw-client |
| ```sh | ||
| npm install | ||
| npm run compile | ||
| npm install activity-watch-client |
There was a problem hiding this comment.
Change to npm install aw-client
|
|
||
| ```javascript | ||
| const aw_client = require('aw-client'); | ||
| const { AWClient } = require('activity-watch-client'); |
There was a problem hiding this comment.
Again, change to require('aw-client')
| const aw_client = require('aw-client'); | ||
| const { AWClient } = require('activity-watch-client'); | ||
|
|
||
| const client = new AWCLient(myClientName) |
There was a problem hiding this comment.
Spelling error, should be new AWClient (notice lowercase L).
| @@ -1,24 +1,25 @@ | |||
| { | |||
| "name": "aw-client-js", | |||
| "name": "activity-watch-client", | |||
There was a problem hiding this comment.
Indeed, name should be aw-client
| "test": "./test.sh" | ||
| "compile": "(rm -rf out || true) && tsc", | ||
| "test": "./test.sh", | ||
| "prepare": "npm run compile && npm run test" |
| pulsetime: number; | ||
| onSuccess: (heartbeat: Heartbeat) => void; | ||
| onError: (err: AxiosError) => void; | ||
| pulseTime: number; |
There was a problem hiding this comment.
We don't camelCase pulsetime anywhere else, so probably shouldn't here either for consistency.
| constructor(clientname: string, options?: {testing?: boolean, baseURL?: string}) { | ||
| if (!options) { | ||
| options = {} | ||
| } |
There was a problem hiding this comment.
Assign a default object to the options parameter instead.
Like this:
constructor(clientname: string, options: {testing?: boolean, baseURL?: string} = {})There was a problem hiding this comment.
it's not exactly the same: if options is null, the default parameter will not be set. Only when it's undefined.
The default parameter is compiled down to if (typeof options === 'undefined') { options = {} }
There was a problem hiding this comment.
Why would options ever be null? Passing null to it shouldn't be valid so not a problem.
There was a problem hiding this comment.
you never know, it's going to be used by third party devs. Tbh, we should do a proper argument check: verify that the clientname is indeed a string, that the options is indeed an object, and same for all methods and provide useful error message instead of returning the error from the server.
But that's another PR ;)
There was a problem hiding this comment.
If third party devs pass an invalid argument to a function that's their problem. The types are clearly specified, they should know better than to pass in null (and why would they? Makes no sense).
There was a problem hiding this comment.
95% of JS devs don't use types. Believe me, JS makes you do crazy stuff, I've been bitten by it a few time already: you wrap the API with another function that returns null in some cases and boom, you end up looking for 2h why it works in some cases and no others. Not every js library is being nice about null vs undefined.
It's a simple change that doesn't impact us in any way: the code is as readable either way and it can save some headaches so let's do it?
There was a problem hiding this comment.
I really think it's unnecessary. Why would you construct an options object by calling a function? Seems like overengineering around the flaws of JavaScript for an obscure case where a developer does something that would be extremely uncommon and bad practice anyway.
Just use the default parameter syntax, it's really not worth the 3 extra lines in this case (but I do appreciate the larger point you're trying to make).
There was a problem hiding this comment.
Why would you construct an options object by calling a function?
Why wouldn't you? I don't think you get to decide what someone is going to do in its code haha.
bad practice anyway
Yes it's bad practice to mix null and undefined but in the past, a lot of libraries didn't really care about the difference and it's not unlikely that someone is going to use one.
it's really not worth the 3 extra lines in this case
You know that it's going to get compiled to something longer in case of the default parameter, right? haha
I really don't think it's a good idea to optimise for code size over developer experience but whatever, it's a not hill I want to die on
|
Just wanted to say: Huge thanks for making this PR! A lot of great changes! ❤️ |
This should be done after merging and publishing, right? I see that now they are using it as a submodules. It would be better to use it as an npm package I believe. |
|
We shouldn't need to fix the compatibility before merging even if they they are used as submodules. |
|
Ready to merge? |
jssince we know it's an npm package)Dateoptionsobject argument instead of a list of argumentsensureBucketmethod and let thecreatemethod fails if the bucket already existsinfotogetInfoto be consistent withgetBucketInfoqueryto accept an array of{start, end}objects instead of a letting the user do the job of creating the string themselvesI took the liberty of publishing the package on npm (I don't think you would mind since it's a different name than the one before). Happy to give you all access to it.