Code Monkey home page Code Monkey logo

jmap-client-ts's Introduction

jmap-client-ts

A Jmap Client written in Typescript.

How to use it

Import in in your project (in you package.json), you can reference a commit or a branch to use snapshot versions.

Create the client

import { Client } from "jmap-client-ts";
import { FetchTransport } from "jmap-client-ts/lib/utils/fetch-transport";

let client = new Client({
    accessToken: 'myToken',
    sessionUrl: 'http://jmap.example.com/session',
    transport: new FetchTransport(fetch)
});

Fetch the session, it will return a promise (of void), the result will be stored on the client and can be accesed later with getSession()

client.fetchSession();

When the fetchSession() promise resolves, you can make requests, the name of the method corresponds to the names in the specs, with lowerCamelCase, and the / replaced by a _, it will return a promise.

client.mailbox_get({
    accountId: Object.keys(jmapClient.getSession().accounts)[0],
    ids: null,
})

jmap-client-ts's People

Contributors

alagane avatar andrevka avatar fabienmoyon avatar hucario avatar jeserkin avatar qyb avatar rykilleen avatar tanandy avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

jmap-client-ts's Issues

Create Gitter

To have a way to have direct discussions with the community

Manage method errors

Currently the lib does nothing where errors are encountered.
The response type is incorrectly typed if it happens.

See https://jmap.io/spec-core.html#errors

A solution is to catch them and throw them to make the promise reject instead of resolve so the response type will be unchanged.

Create a method to get changes until hasMoreChanges is false

As a library user (eg: OpenPaas)
I want to get all the Mailbox changes using only one method
So that I do not have to worry about the hasMoreChanges mechanic which will be handled by the library

When I get all the changes
And the response property hasMoreChanges is false
Then the behaviour is the same than mailbox_changes

When I get all the changes
And the response property hasMoreChanges is true
Then more requests are made and merged until hasMoreChanges is false
And the client should get a response like if it was done in one request (but it will take more time)

Merging responses would be:

  • union of created (minus those in destroyed) -> if a Mailbox was created and destroyed, no need to fetch it
  • union of updated (minus those on created and those on destroyed) -> if a mailbox was created and updated, it will be fetched because in created; if a mailbox was updated and destroyed, no need to fetch it
  • union of destroyed (minus those on created) -> if a Mailbox was created and destroyed, no need to remove it

getFirstAccountId need to be implemented as getPersonalAccountId

The spec does not guarantee the personal(primary mail) account is the first element of session.accounts. I'm not sure whether JamesServer supports account/mailbox sharing. But cyrus-imapd seems that the personal account always is the last element of session.accounts.

I suggest that we add a getPersonalAccountId() method, and keep getFirstAccountId until 2.0 release.

Support push notifications for Mailbox

Push documentation https://jmap.io/spec-core.html#push

We should be able to open a communication channel using websockets to receive StateChange notifications.
The library should take care of the authentication.

For an authentication and request example using ticket on James: https://github.com/linagora/tmail-backend/pull/78/files#diff-652928d50d74bf6233709ab2fb095e4909b554d8e6b3ede18785277964803795

  • Get the ticket (using the url on the session)
  • Use the ticket to open a websocket connection (query parameter)
  • Filter to get only mailboxes ones (will probably need rxjs observables) this is about filtering them when different entities will be subscribed, we will need to be able to get one observable for each kind but only one websocket opened

Given a client with session fetched
When call the method to get push notifications
And there is an error getting the ticket (Authentication)
Then the returned observable throws an error

Given a client with session fetched
When call the method to get push notifications
And there is an error opening the websocket (with ticket)
Then the returned observable throws an error

Given a client with session fetched
When call the method to get Mailbox push notifications
And a Mailbox is added
Then the returned observable emits the StateChange for the Mailbox
TODO How do we know the observable is ready to receive StateChange when the Mailbox is added?

Given a client with session fetched
When call the method to get Mailbox push notifications
And an Email is added
Then the returned observable does not emit the StateChange for the Email
TODO How do we know the observable is ready to receive StateChange when the Mailbox is added?

Consider Travis CI

Since this is purely frontend library and Jest should be responsible only for unit testing, not E2E testing, then I recommend using Travis CI, which is integrated in Github environment as far as I know.

Originally posted by @jeserkin in #17 (comment)

docs: update quickstart to include Transport example

Right now it's not clear from the readme that passing a transport is required when initializing a client, nor is it clear how to use several of the transports exposed in utils

import { Client } from "jmap-client-ts";
import { FetchTransport } from "jmap-client-ts/lib/utils/fetch-transport";

let client = new Client({
  accessToken:
    "....",
  sessionUrl: "https://api.example.com/jmap/session",
  transport: new FetchTransport(fetch),
});

Have tests

No tests sounds like a bad idea*:

I would suggest to use linagora/james-memorydocker image to start a JMAP server against which to test the cient...

Forbid focused tests

We may forget to remove .only or the leading f on tests, and if not seen it can compromise every tests running in the future.

We should not be able to commit/merge focused tests.

Support data downloading/uploading

Mainly issue is regarding -> https://jmap.io/spec-core.html#binary-data
In session response we have a bunch of different URLs including

{
  "downloadUrl": "http://localhost/download/{accountId}/{blobId}/?type={type}&name={name}",
  "uploadUrl": "http://localhost/upload/{accountId}"
}

So as far as I can tell, what can be done on jmap-client-ts level is:

  • return URL with accountId replaced, since it is present in session response as well.
  • For upload, there could be a type definition for response as mentioned here https://jmap.io/spec-core.html#uploading-binary-data
  • Also for upload, there could be an entire method present, that would allow data upload. End-user should provide only body value, e.g. Blob

If there are any other suggestions on how to proceed with it, then I would love to hear about it.

Specifying properties for jmapClient.email_get seems to be an issue

Given

getEmailsWithSpecificProperties(emailIds: string[], properties: ?): Observable<IEmailGetResponse> {
    return from(this.fetchSession())
      .pipe(
        mergeMap(session => from(this.jmapClient.email_get({
          accountId: Object.keys(session.accounts)[0],
          ids: emailIds,
          fetchAllBodyValues: true,
          properties
        })))
      );
  }

What would properties type be, to accommodate provided method?

Based on the question, it seems to me, that properties?: (keyof Properties)[]; is not the desired way of specifying type.

Check properties on types

Since I used this not up to date (it was written for Jmap draft) page https://jmap.io/server.html to make them (not entirely though), there might be some properties that do not exist, like the deleted property on IMailboxProperties.

Transferring uploaded attachment between draft versions

Problem:
When uploading Blob and then attaching to email with Email/set it provides attachment with new ID, that is related to email object. e.g. Email -> blobId: 'XXXXX', then attachment in that email will be e.g. partId: '5', blobId: 'XXXXX_5'.

When creating new email version (e.g. sender changed email subject), then it will not be created if I pass same list of attachments with it. I assume, that issue is that new version will have e.g. blobId: 'YYYYY' and it has no information about blobId: 'XXXXX'. Therefore it will not be able to get attachment, that has blobId: 'XXXXX_5'.

Expected behaviour:
Version 1 - it can find attachment by ID and copy it with new id, that will be linked to new Email version. (Version is based on the ability to copy attachment between accounts - https://jmap.io/spec-core.html#blobcopy)
Version 2 - it can provide original attachment id (the one, that is received in response of blob upload), so that it can be provided for new email version

NB!! Might be James and not JMAP related issue. Might be both.

upgrade test container & more overridenUrl settings?

First, James 3.7.0 have implemented Thread/Get (JAMES-3516). It's an important feature and I'd like to push my patch that had been working well with cyrus-imapd.

Second, current constructor has an optional overriddenApiUrl argument. I thought we need it when the real James server serves behind a proxy with rewritten path/port (or our test container's port mapping ). But apiUrl wasn't the only URL in jmap session response. downloadUrl, uploadUrl, eventSourceUrl, ... all need to be replaced in this scenario. Should we use overriddenUrl-object instead of it?

Binary data uploading blobId issue

It feels like a bug, but I am not sure.

So the use case is editing one draft multiple times. Since emails are immutable to a certain degree in James, the only way at the moment, that I was able to find is to create new email with Email/set -> create and remove previous version with Email/set -> destroy.

Now moving and focusing on attachments.
When uploading binary data successfully one does receive response in a form:

{
  "accountId":"5a20d72b9605345c1b526c6cba0565f699dfbde43f735d68c689dada5b3bfb85",
  "blobId":"lxcmnvkkfkudryrqttcj",
  "type":"text/css",
  "size":29
}

Adding this response with additional name property as attachment for Email/set -> create works fine, but attachments, that come back with Email/get have following structure:

{
  "charset": "us-ascii",
  "disposition": "attachment",
  "size": 8480,
  "partId": "5",
  "blobId": "0207d9f0-9757-11eb-a1b2-658a285e665b_5",
  "name": "Book1.xlsx",
  "type": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
}

Here blobId looks differently.

Now the issue itself is with doing Email/set -> create with attachments, where blobId is e.g. "0207d9f0-9757-11eb-a1b2-658a285e665b_5"
Response is negative invalidArguments ... Attachment not found: 0207d9f0-9757-11eb-a1b2-658a285e665b_5

So I guess the question is, whether it is James configuration issue or whether it is flawed logic on my part or something else?

Is example for Email/query accurate?

I was looking through the code and found my old comment in Email/query. At the moment sorting is resolved through

comparator: [{
  property: 'receivedAt',
  isAscending: false
}]

even though there is no such property in any documentation (don't remember where it was found).

On the other hand first example on this page https://jmap.io/spec-mail.html#examples shows something different. It shows, that there should be sort property. It is present in documentation, but it is not working.

So the question is, is documentation correct regarding this or it needs to be updated to reflect the actual state?

Support back-references

Simple question but it would be really nice to use back-reference to chain JMAP calls and reduce server roundtrips.

(For someone like me based in Vietnam this is actually critical!)

You could:

start with a mailbox query to retrieve the guy INBOX
Then back-reference the INBOX in the email/query
And finally back-reference the ids in the Email/get

Doing all of that with a single network roundtrip, with a single API call.

Is there planned support for back-references in jmap-client-ts?

I would advise the maintainer to implement it early, in order to avoid generating technical debt.

See https://jmap.io/spec-core.html#references-to-previous-method-results

[[ "Foo/changes", {
    "accountId": "A1",
    "sinceState": "abcdef"
}, "t0" ],
[ "Foo/get", {
    "accountId": "A1",
    "#ids": {
        "resultOf": "t0",
        "name": "Foo/changes",
        "path": "/created"
    }
}, "t1" ]]

Configure test James to provide correct API URLs

When I run tests, I don't want to use the overridden...Url parameter on jmap-client-ts, the only configurable URL for the client should be the session one, and other URLs are provided by the session.

Since the docker has a dynamic port, the URL is not know before the container is started.

It seems that those URLs are defined in jmap.properties file (see https://james.apache.org/server/config-jmap.html)

Does James support setting those URLs after is is started? With the file? With some commands?

If this is not possible, we can try to set up a proxy to redirect to James, setting proxy destination after it is started, but the proxy should have a constant URL?

Setup linter

Setup a linter to ensure a consistent code style.

Provide response object in Error

I'm getting a 400 response but only getting a generic error message indicating the http response status, which makes it difficult to figure out what went wrong

Separate low and high level API

Why?

To easily know which methods are low and high level.

How?

Separate files?

// Low level
client.lowLevel.mailbox_get(...);

// High level
client.getAllMailboxes(...);

About mailbox_changes

Issue #45
What about the case of mailbox_changes to get changes until hasMoreChanges is false?
Is it low level because its response is the same, and it just mimics the simple mailbox_changes using the same data structure?
Or is it high level because it does more than just making a simple JMAP request?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.