├── index.js
├── .npmignore
├── .gitignore
├── .travis.yml
├── .jsdocrc
├── .eslintrc
├── lib
├── promises
│ ├── ES6PromiseProvider.js
│ ├── QPromiseProvider.js
│ └── PromiseProvider.js
├── utils
│ ├── Constants.js
│ └── Utils.js
├── errors
│ ├── TransportError.js
│ └── JmapError.js
├── client
│ └── Client.js
├── transport
│ ├── JQueryTransport.js
│ ├── RequestTransport.js
│ └── Transport.js
└── API.js
├── bower.json
├── test
├── frontend
│ ├── require.js
│ └── transport
│ │ └── JQueryTransport.js
├── common
│ ├── errors
│ │ ├── TransportError.js
│ │ └── JmapError.js
│ └── utils
│ │ └── Utils.js
├── config
│ └── karma.conf.js
└── backend
│ └── transport
│ └── RequestTransport.js
├── LICENSE
├── package.json
├── README.md
├── CONTRIBUTING.md
├── CHANGELOG.md
└── Gruntfile.js
/index.js:
--------------------------------------------------------------------------------
1 | module.exports = require('./dist/jmap-client.min.js');
2 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | .*
2 | node_modules
3 | test/
4 | Gruntfile.js
5 | coverage
6 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | dist
2 | .tmp
3 | node_modules
4 | tmp/
5 | *.log
6 | .idea
7 | doc/api
8 | coverage
9 | package-lock.json
10 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - '10'
4 | before_script:
5 | - npm install -g bower grunt-cli
6 | script: grunt coverage
7 |
--------------------------------------------------------------------------------
/.jsdocrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": [
3 | "plugins/markdown",
4 | "plugins/underscore"
5 | ],
6 | "templates": {
7 | "default": {
8 | "includeDate": false
9 | }
10 | }
11 | }
12 |
13 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "linagora-esn",
3 | "parserOptions": {
4 | "ecmaVersion": 6,
5 | "sourceType": "module"
6 | },
7 | "rules": {
8 | "class-methods-use-this": "off",
9 | "prefer-destructuring": "off"
10 | }
11 | }
--------------------------------------------------------------------------------
/lib/promises/ES6PromiseProvider.js:
--------------------------------------------------------------------------------
1 | import PromiseProvider from './PromiseProvider';
2 |
3 | /**
4 | * A {@link PromiseProvider} implementation creating native ES6 Promises.
5 | * This class supposes that the `Promise` class is available.
6 | *
7 | * @class ES6PromiseProvider
8 | *
9 | * @see PromiseProvider
10 | */
11 | export default class ES6PromiseProvider extends PromiseProvider {
12 | newPromise(resolver) {
13 | return new Promise(resolver);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/lib/promises/QPromiseProvider.js:
--------------------------------------------------------------------------------
1 | import PromiseProvider from './PromiseProvider';
2 |
3 | /**
4 | * A {@link PromiseProvider} implementation creating [Q]{@link https://github.com/kriskowal/q} promises.
5 | * This class requires `Q` to be installed as dependency.
6 | *
7 | * @class QPromiseProvider
8 | *
9 | * @see PromiseProvider
10 | */
11 | export default class QPromiseProvider extends PromiseProvider {
12 | newPromise(resolver) {
13 | return require('q').Promise(resolver);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/lib/utils/Constants.js:
--------------------------------------------------------------------------------
1 | /**
2 | * The _Constants_ module exports a single object that is a collection of useful constants.
3 | *
4 | * @property VERSION {String} The version of this library
5 | *
6 | * @module Constants
7 | */
8 | export default {
9 | VERSION: '__VERSION__',
10 | CLIENT_NAME: 'jmap-client (https://github.com/linagora/jmap-client)',
11 | SUCCESS_RESPONSE_CODES: [200, 201],
12 | ERROR: 'error',
13 | CORE_CAPABILITIES_URI: 'http://jmap.io/spec-core.html',
14 | MAIL_CAPABILITIES_URI: 'http://jmap.io/spec-mail.html'
15 | };
16 |
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "jmap-client",
3 | "version": "0.1.1-dev",
4 | "authors": [
5 | "Linagora Folks "
6 | ],
7 | "description": "A Javascript library to connect to a JMAP mail server",
8 | "main": "dist/jmap-client.min.js",
9 | "moduleType": [
10 | "globals",
11 | "node"
12 | ],
13 | "keywords": [
14 | "jmap",
15 | "library",
16 | "mail",
17 | "es6"
18 | ],
19 | "license": "MIT",
20 | "homepage": "https://open-paas.org",
21 | "ignore": [
22 | "**/.*",
23 | "node_modules",
24 | "bower_components",
25 | "test",
26 | "coverage",
27 | "samples"
28 | ]
29 | }
30 |
--------------------------------------------------------------------------------
/test/frontend/require.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /* global jmap: false, chai: false, Q: false, sinon: false */
4 |
5 | window.require = function(name) {
6 | if (/jmap-client/.test(name)) {
7 | return jmap;
8 | }
9 |
10 | if (name === 'chai') {
11 | return chai;
12 | }
13 |
14 | if (name === 'q') {
15 | return Q;
16 | }
17 |
18 | if (name === 'sinon') {
19 | return sinon;
20 | }
21 |
22 | // chai plugins are self-registering in the browser
23 | // we're returning a noop function so that the call to chai.use() does nothing
24 | if (name === 'chai-datetime' || name === 'chai-shallow-deep-equal' || name === 'sinon-chai') {
25 | return function() {};
26 | }
27 | };
28 |
--------------------------------------------------------------------------------
/test/common/errors/TransportError.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var expect = require('chai').expect,
4 | jmap = require('../../../dist/jmap-client');
5 |
6 | describe('The TransportError class', function() {
7 |
8 | it('should be an Error', function() {
9 | expect(new jmap.TransportError()).to.be.a.instanceof(Error);
10 | });
11 |
12 | it('should default cause, statusCode and responseText', function() {
13 | var error = new jmap.TransportError();
14 |
15 | expect(error.cause).to.equal(null);
16 | expect(error.statusCode).to.equal(0);
17 | expect(error.responseText).to.equal(null);
18 | });
19 |
20 | it('should expose cause, statusCode and responseText as members', function() {
21 | var error = new jmap.TransportError('a', 1, 'b');
22 |
23 | expect(error.cause).to.equal('a');
24 | expect(error.statusCode).to.equal(1);
25 | expect(error.responseText).to.equal('b');
26 | });
27 |
28 | });
29 |
--------------------------------------------------------------------------------
/lib/errors/TransportError.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | export default class TransportError extends Error {
4 | /**
5 | * A `TransportError` is a custom `Error` thrown when a request to a remote server fails.
6 | *
7 | * @constructor
8 | *
9 | * @param [cause=null] {*} The underlying cause of this `TransportError`. Though this is usually an {@link Error}, this
10 | * might actually be anything and depends on the chosen {@link Transport} implementation.
11 | * @param [statusCode=0] {Number} The HTTP status code sent by the server, if the request reached the server.
12 | * @param [responseText=null] {String} The HTTP response sent by the server, if any.
13 | *
14 | * @see Transport
15 | * @see Error
16 | */
17 | constructor(cause, statusCode, responseText) {
18 | super();
19 |
20 | this.cause = cause || null;
21 | this.statusCode = statusCode || 0;
22 | this.responseText = responseText || null;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/lib/client/Client.js:
--------------------------------------------------------------------------------
1 | import ES6PromiseProvider from '../promises/ES6PromiseProvider';
2 | import Utils from '../utils/Utils';
3 |
4 | export default class Client {
5 | /**
6 | * The {@link Client} class is the main entry point for sending JMAP requests to a remote server.
7 | * It uses a fluent API so that it's easy to chain calls. JMAP requests are sent using one of the _getXXX_ methods
8 | * that map to their equivalent in the JMAP specification. For instance, if you want to do a _getAccounts_ request,
9 | * you'll use the {@link Client#getAccounts} method.
10 | *
11 | * @param transport {Transport} The {@link Transport} instance used to send HTTP requests.
12 | * @param [promiseProvider={@link ES6PromiseProvider}] {PromiseProvider} The {@link PromiseProvider} implementation to use.
13 | */
14 | constructor(transport, promiseProvider) {
15 | Utils.assertRequiredParameterIsPresent(transport, 'transport');
16 |
17 | this.promiseProvider = promiseProvider || new ES6PromiseProvider();
18 | this.transport = transport;
19 | this.transport.promiseProvider = this.promiseProvider;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Linagora
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/lib/transport/JQueryTransport.js:
--------------------------------------------------------------------------------
1 | import Transport from './Transport';
2 | import TransportError from '../errors/TransportError';
3 |
4 | export default class JQueryTransport extends Transport {
5 | /**
6 | * A {@link Transport} implementation for [jQuery]{@link https://jquery.com/}.
7 | * This class supposes that the `jQuery` global object is available.
8 | *
9 | * @constructor
10 | *
11 | * @param [promiseProvider=null] {PromiseProvider} A {@link PromiseProvider} implementation.
12 | *
13 | * @see Transport
14 | */
15 | constructor(promiseProvider) {
16 | super();
17 |
18 | this.promiseProvider = promiseProvider;
19 | }
20 |
21 | post(url, headers, data, raw) {
22 | return this.promiseProvider.newPromise(function(resolve, reject) {
23 | jQuery.ajax({
24 | url: url,
25 | method: 'POST',
26 | headers: headers,
27 | data: raw ? data : JSON.stringify(data),
28 | dataType: raw ? undefined : 'json',
29 | processData: false,
30 | jsonp: false
31 | })
32 | .done(resolve)
33 | .fail((xhr, statusText, err) => reject(new TransportError(err, xhr.status, xhr.responseText)));
34 | });
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/lib/transport/RequestTransport.js:
--------------------------------------------------------------------------------
1 | import Constants from '../utils/Constants';
2 | import Transport from './Transport';
3 | import TransportError from '../errors/TransportError';
4 |
5 | export default class RequestTransport extends Transport {
6 | /**
7 | * A {@link Transport} implementation for [Request]{@link https://github.com/request/request}.
8 | * This class requires `request` to be installed as dependency.
9 | *
10 | * @constructor
11 | *
12 | * @param [promiseProvider=null] {PromiseProvider} A {@link PromiseProvider} implementation.
13 | *
14 | * @see Transport
15 | */
16 | constructor(promiseProvider) {
17 | super();
18 |
19 | this.promiseProvider = promiseProvider;
20 | }
21 |
22 | post(url, headers, data, raw) {
23 | return this.promiseProvider.newPromise(function(resolve, reject) {
24 | require('request')({
25 | url: url,
26 | headers: headers,
27 | method: 'POST',
28 | body: data,
29 | json: !raw
30 | }, function(err, res, body) {
31 | if (err || Constants.SUCCESS_RESPONSE_CODES.indexOf(res.statusCode) < 0) {
32 | return reject(new TransportError(err, res && res.statusCode, body));
33 | }
34 |
35 | resolve(body);
36 | });
37 | });
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/test/common/errors/JmapError.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var expect = require('chai').expect,
4 | jmap = require('../../../dist/jmap-client');
5 |
6 | describe('The JmapError class', function() {
7 |
8 | it('should require payload', function() {
9 | expect(function() {
10 | new jmap.JmapError();
11 | }).to.throw(Error);
12 | });
13 |
14 | it('should require payload.type', function() {
15 | expect(function() {
16 | new jmap.JmapError({});
17 | }).to.throw(Error);
18 | });
19 |
20 | it('should be an Error', function() {
21 | expect(new jmap.JmapError({ type: 'invalidArguments' })).to.be.a.instanceof(Error);
22 | });
23 |
24 | it('should expose type, description, method and payload as members', function() {
25 | var error = new jmap.JmapError({ type: 'invalidArguments', description: 'The `date` parameter is not supported' }, 'setMessages');
26 |
27 | expect(error.payload).to.deep.equal({
28 | type: 'invalidArguments',
29 | description: 'The `date` parameter is not supported'
30 | });
31 | expect(error.type).to.equal('invalidArguments');
32 | expect(error.description).to.equal('The `date` parameter is not supported');
33 | expect(error.method).to.equal('setMessages');
34 | });
35 |
36 | it('should default to null for description and method', function() {
37 | var error = new jmap.JmapError({ type: 'invalidArguments' });
38 |
39 | expect(error.description).to.equal(null);
40 | expect(error.method).to.equal(null);
41 | });
42 |
43 | });
44 |
--------------------------------------------------------------------------------
/test/config/karma.conf.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = function(config) {
4 | config.set({
5 | basePath: '../../',
6 |
7 | singleRun: true,
8 | browsers: ['PhantomJS'],
9 | frameworks: ['mocha'],
10 | reporters: ['spec', 'coverage'],
11 |
12 | coverageReporter: {
13 | dir: 'coverage/frontend',
14 | reporters: [
15 | { type: 'lcov', subdir: '.' },
16 | { type: 'text-summary' }
17 | ]
18 | },
19 |
20 | preprocessors: {
21 | 'dist/*.js': ['coverage']
22 | },
23 |
24 | plugins: [
25 | 'karma-phantomjs-launcher',
26 | 'karma-chrome-launcher',
27 | 'karma-firefox-launcher',
28 | 'karma-mocha',
29 | 'karma-spec-reporter',
30 | 'karma-coverage'
31 | ],
32 | colors: true,
33 | autoWatch: true,
34 | files: [
35 | 'node_modules/chai/chai.js',
36 | 'node_modules/chai-datetime/chai-datetime.js',
37 | 'node_modules/chai-shallow-deep-equal/chai-shallow-deep-equal.js',
38 | 'node_modules/q/q.js',
39 | 'node_modules/phantomjs-polyfill/bind-polyfill.js',
40 | 'node_modules/jquery/dist/jquery.js',
41 | 'node_modules/jquery-mockjax/dist/jquery.mockjax.js',
42 | 'node_modules/sinon/pkg/sinon.js',
43 | 'node_modules/sinon-chai/lib/sinon-chai.js',
44 | 'dist/jmap-client.js',
45 | // This must be here, before the actual tests
46 | 'test/frontend/require.js',
47 | // Tests
48 | 'test/common/**/*.js',
49 | 'test/frontend/transport/JQueryTransport.js'
50 | ]
51 | });
52 | };
53 |
--------------------------------------------------------------------------------
/lib/utils/Utils.js:
--------------------------------------------------------------------------------
1 | export default class Utils {
2 | /**
3 | * This class contains some useful utility methods.
4 | * The Utils class cannot be instantiated (its constructor will throw if called), all its methods are static.
5 | *
6 | * @constructor
7 | */
8 | constructor() {
9 | throw new Error('The Utils class cannot be instantiated.');
10 | }
11 |
12 | /**
13 | * Check is the `parameter` is not undefined and not null.
14 | *
15 | * @param parameter {*} The parameter to check.
16 | *
17 | * @return {Boolean} True if `parameter` is not undefined and not null.
18 | */
19 | static isDefined(parameter) {
20 | return typeof parameter !== 'undefined' && parameter !== null;
21 | }
22 |
23 | /**
24 | * Asserts that the given `parameter` is present (read: truthy).
25 | * This method is intended to be called when you need to validate input parameters of functions.
26 | *
27 | * @param parameter {*} The parameter to validate.
28 | * @param name {String} The name of the parameter, as given to the calling function.
29 | * This is used to format the error message contained by the thrown {@link Error}.
30 | * @param message {String} Optionnal alternative message to display when error
31 | *
32 | * @return {*} The validated parameter, as-is.
33 | *
34 | * @throws {Error} If the parameter is not defined.
35 | */
36 | static assertRequiredParameterIsPresent(parameter, name, message = null) {
37 | message = message || `The ${name} parameter is required`;
38 | if (!Utils.isDefined(parameter)) {
39 | throw new Error(message);
40 | }
41 |
42 | return parameter;
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/lib/errors/JmapError.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import Utils from '../utils/Utils';
4 |
5 | export default class JmapError extends Error {
6 | /**
7 | * A `JmapError` is a custom `Error` thrown when a request is rejected by the JMAP backend.
8 | * The _type_ property holds the type of error that happened. Refer to the JMAP [spec](http://jmap.io/spec-core.html#errors)
9 | * for details on the available errors.
10 | * Other properties may be present with further information; these are detailed in the method descriptions where appropriate.
11 | *
12 | * @constructor
13 | *
14 | * @param payload {Object} The error payload, from which detailed information about the error can be retrieved.
15 | * @param [payload.type] {String} The type of this `JmapError`.
16 | * @param [payload.description=null] {String} The description, if any, of this `JmapError`.
17 | * @param [method=null] {String} The JMAP method that triggered this `JmapError`.
18 | *
19 | * @see Error
20 | */
21 | constructor(payload, method) {
22 | super();
23 |
24 | Utils.assertRequiredParameterIsPresent(payload, 'payload');
25 | Utils.assertRequiredParameterIsPresent(payload.type, 'payload.type');
26 |
27 | this.payload = payload;
28 | this.type = payload.type;
29 | this.description = payload.description || null;
30 |
31 | this.method = method || null;
32 | }
33 |
34 | /**
35 | * Returns a {@link String} representation of this `JmapError`.
36 | *
37 | * @returns {String} The human-readable representation of this `JmapError`.
38 | */
39 | toString() {
40 | return `JmapError{type=${this.type},description=${this.description},method=${this.method}}`;
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/lib/promises/PromiseProvider.js:
--------------------------------------------------------------------------------
1 | /**
2 | * The {@link PromiseProvider} class is the base class for providers of {@link Promise} instances.
3 | * A concrete implementation is required to implement {@link PromiseProvider#newPromise} so that this method
4 | * returns a {@link Promise} that will be used by the library to do JMAP requests and other asynchronous things.
5 | *
6 | * This level of abstraction allows users of the library to plug in their own implementation in order to use their
7 | * favorite {@link Promise} library. Implementations for [Q]{@link https://github.com/kriskowal/q}
8 | * and native [ES6 Promises]{@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise} are provided.
9 | *
10 | * @abstract
11 | * @class PromiseProvider
12 | *
13 | * @see ES6PromiseProvider
14 | * @see QPromiseProvider
15 | */
16 | export default class PromiseProvider {
17 | /**
18 | * This method must be implemented by concrete {@link PromiseProvider} implementations in such a way that:
19 | * * A {@link Promise} is created from the `resolver` argument and is returned.
20 | * * The {@link Promise} will be fulfilled when the `resolve` function of the resolver is called.
21 | * * The {@link Promise} will be rejected when the `reject` function of the resolver is called.
22 | *
23 | * @abstract
24 | *
25 | * @param resolver {Function} A {@link Function} with two arguments `resolve` and `reject`, both functions.
26 | * The first argument fulfills the promise, the second argument rejects it.
27 | *
28 | * @return {Promise}
29 | */
30 | newPromise() {
31 | throw new Error('PromiseProvider is an abstract class. Please use a concrete implementation.');
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/lib/API.js:
--------------------------------------------------------------------------------
1 | /**
2 | * The _API_ module is the entry point of the library.
3 | * It exports a single {@link Object} that is exposed as, either:
4 | * * A global `jmap` variable when jmap-client included in a web page through a `script` tag
5 | * * A NodeJS module when jmap-client is `require`'d in a NodeJS application
6 | *
7 | * When extending the library with new models, utility classes, etc. don't forget to update this module
8 | * so that your new code gets exposed in the public API.
9 | *
10 | * The exported object has the following properties:
11 | *
12 | * @property Client {Client} The {@link Client} class
13 | * @property JSONBuilder {JSONBuilder} The {@link JSONBuilder} class helping to serialize model to json
14 | * @property PromiseProvider {PromiseProvider} The {@link PromiseProvider} class
15 | * @property ES6PromiseProvider { ES6PromiseProvider} The {@link ES6PromiseProvider} class
16 | * @property QPromiseProvider { QPromiseProvider} The {@link QPromiseProvider} class
17 | * @property Transport { Transport} The {@link Transport} class
18 | * @property JQueryTransport { JQueryTransport} The {@link JQueryTransport} class
19 | * @property RequestTransport { RequestTransport} The {@link RequestTransport} class
20 | * @property TransportError {TransportError} The {@link TransportError} class
21 | * @property JmapError {JmapError} The {@link JmapError} class
22 | *
23 | * @module API
24 | */
25 | export default {
26 | Client: require('./client/Client'),
27 | ES6PromiseProvider: require('./promises/ES6PromiseProvider'),
28 | JmapError: require('./errors/JmapError'),
29 | JQueryTransport: require('./transport/JQueryTransport'),
30 | PromiseProvider: require('./promises/PromiseProvider'),
31 | QPromiseProvider: require('./promises/QPromiseProvider'),
32 | RequestTransport: require('./transport/RequestTransport'),
33 | Transport: require('./transport/Transport'),
34 | TransportError: require('./errors/TransportError'),
35 | Utils: require('./utils/Utils')
36 | };
37 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "jmap-client",
3 | "version": "0.1.1-dev",
4 | "repository": "linagora/jmap-client",
5 | "description": "This lib help to make requests against a JMAP server",
6 | "main": "dist/jmap-client.js",
7 | "dependencies": {
8 | "uuid": "3.3.2"
9 | },
10 | "devDependencies": {
11 | "@babel/core": "7.0.0",
12 | "@babel/polyfill": "7.0.0",
13 | "@babel/preset-env": "7.0.0",
14 | "babel-plugin-add-module-exports": "0.2.1",
15 | "babel-plugin-transform-builtin-extend": "1.1.0",
16 | "babel-plugin-transform-object-assign": "6.22.0",
17 | "babel-preset-es2015": "6.22.0",
18 | "babelify": "7.3.0",
19 | "browserify-versionify": "1.0.6",
20 | "chai": "3.5.0",
21 | "chai-datetime": "1.4.1",
22 | "chai-shallow-deep-equal": "1.4.4",
23 | "eslint": "6.8.0",
24 | "eslint-config-airbnb-base": "14.1.0",
25 | "eslint-config-linagora-esn": "1.1.1",
26 | "eslint-plugin-import": "2.20.1",
27 | "grunt": "1.1.0",
28 | "grunt-browserify": "5.3.0",
29 | "grunt-continue": "0.1.0",
30 | "grunt-contrib-clean": "1.0.0",
31 | "grunt-contrib-uglify": "2.0.0",
32 | "grunt-contrib-watch": "1.1.0",
33 | "grunt-coveralls": "2.0.0",
34 | "grunt-eslint": "22.0.0",
35 | "grunt-exec": "1.0.1",
36 | "grunt-jsdoc": "2.1.0",
37 | "grunt-karma": "4.0.0",
38 | "grunt-lcov-merge": "1.2.3",
39 | "grunt-lint-pattern": "0.1.4",
40 | "grunt-mocha-cli": "6.0.0",
41 | "grunt-mocha-istanbul": "5.0.2",
42 | "grunt-release": "0.14.0",
43 | "istanbul": "0.4.5",
44 | "jquery": "3.5.1",
45 | "jquery-mockjax": "2.2.1",
46 | "jsdoc": "3.6.3",
47 | "karma": "5.0.5",
48 | "karma-babel-preprocessor": "8.0.0-beta.0",
49 | "karma-chrome-launcher": "2.0.0",
50 | "karma-coverage": "2.0.2",
51 | "karma-firefox-launcher": "1.0.0",
52 | "karma-mocha": "2.0.1",
53 | "karma-phantomjs-launcher": "1.0.2",
54 | "karma-spec-reporter": "0.0.26",
55 | "load-grunt-tasks": "3.5.2",
56 | "mocha": "7.1.2",
57 | "mockery": "2.0.0",
58 | "node-getopt": "0.2.3",
59 | "phantomjs-polyfill": "0.0.2",
60 | "phantomjs-prebuilt": "2.1.14",
61 | "q": "1.4.1",
62 | "request": "2.79.0",
63 | "sinon": "1.17.7",
64 | "sinon-chai": "2.8.0"
65 | },
66 | "scripts": {
67 | "test": "grunt test",
68 | "dist": "grunt compile && grunt apidoc"
69 | },
70 | "author": "Linagora Folks",
71 | "license": "MIT"
72 | }
73 |
--------------------------------------------------------------------------------
/lib/transport/Transport.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * The {@link Transport} class is the base class for providers of a HTTP transport layer.
5 | * A concrete implementation is required to implement {@link Transport#post} so that this method returns a
6 | * {@link Promise} that will be resolved or rejected depending on the result of the underlying HTTP request
7 | * To create {@link Promise} instances, a {@link Transport} implementation should use a {@link PromiseProvider}.
8 | * {@link Client} instances will automatically provide transports with the configured {@link PromiseProvider} as the
9 | * `promiseProvider` property.
10 | *
11 | * This level of abstraction allows users of the library to plug in their own implementation in order to use their
12 | * favorite HTTP transport library. Implementations for [Request]{@link https://github.com/request/request}
13 | * and [jQuery]{@link https://jquery.com/} are provided.
14 | *
15 | * @abstract
16 | * @class Transport
17 | *
18 | * @see JQueryTransport
19 | * @see RequestTransport
20 | * @see PromiseProvider
21 | */
22 | export default class Transport {
23 | /**
24 | * This method must be implemented by concrete {@link Transport} implementations in such a way that:
25 | * * A HTTP POST request is made on `url` with the given `headers` and `data` (i.e.: payload)
26 | * * A {@link Promise} is returned (`this.promiseProvider` will be available to create Promise instances)
27 | * * The {@link Promise} is fulfilled when the HTTP request returns 200 (http://jmap.io/spec.html#jmap-over-https)
28 | * * The {@link Promise} is rejected if the HTTP request fails, or if the return status code is not 200
29 | * * When the {@link Promise} is fulfilled, the raw JMAP response is returned
30 | *
31 | * @abstract
32 | *
33 | * @param url {String} The URL to POST to
34 | * @param headers {Object} A hash of header names to header values that must be transmitted as part of the request
35 | * @param data {*} The request payload, as a Javascript object. It's the responsibility of the {@link Transport} implementation
36 | * to serialize the data as a JSON {@link String} whenever required.
37 | * @param raw {Boolean} Whether the requests sends and expects raw (plain text) data instead of the default JSON.
38 | *
39 | * @return {Promise}
40 | */
41 | post() {
42 | throw new Error('Transport is an abstract class. Please use a concrete implementation.');
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # JMAP-CLIENT
2 |
3 | :warning: jmap-client is based on an outdated jmap draft. If you are looking for a jmap client you can check this project [jmap-client-ts](https://github.com/OpenPaaS-Suite/jmap-client-ts)
4 |
5 | [](https://gitter.im/linagora/jmap-client?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [](https://travis-ci.org/linagora/jmap-client) [](https://coveralls.io/github/linagora/jmap-client?branch=master)
6 |
7 | This repository provides a JavaScript library to make requests against a JMAP server.
8 | It is a client-side implementation of the [JMAP](http://jmap.io/spec.html) specification.
9 | It is developed using **ES6** and transpiled to ES5 code using [babel.js](https://babeljs.io/).
10 |
11 | Non-exhaustive list of features:
12 | * Entities are modeled as JavaScript classes.
13 | * Expose a *Client* class that you can use to send JMAP requests, using a fluent API.
14 | * Uses Promises exclusively, and allows for pluggable Promise implementation.
15 | * Allows for pluggable transports, with default support for node.js [request](https://github.com/request/request) and [jQuery](http://jquery.com/) in the browser.
16 |
17 | ## Usage
18 |
19 | ### Installation
20 |
21 | The library is provided as a NPM or Bower packages, thus to install either use:
22 |
23 | npm install jmap-client
24 |
25 | or
26 |
27 | bower install jmap-client
28 |
29 | depending on your preferred package manager.
30 |
31 | ## How to contribute
32 |
33 | ### 1. Clone the repository
34 |
35 | git clone https://ci.open-paas.org/stash/scm/olibs/jmap-client.git
36 | cd jmap-client
37 |
38 | ### 2. Install dependencies
39 |
40 | npm install
41 |
42 | ### 3. Compile the library and run the tests
43 |
44 | grunt
45 |
46 | ### 4. Code, execute tests then pull request !
47 |
48 | More detailled instructions can be found in the [contributing section](./CONTRIBUTING.md).
49 |
50 | ## Release
51 |
52 | If you are a maintainer of this project, here's how you can release a new version:
53 |
54 | 1. Checkout the _master_ branch and pull the latest changes from the remote repository
55 | 2. Run `grunt release` to do the release. A lot of things will happen but you'll eventually be back on the _master_ branch
56 | 3. Bump the version in _master_ to **NEXT_VERSION-dev** (replace _NEXT_VERSION_ by the supposed next version)
57 |
58 | ## License
59 |
60 | [MIT](LICENSE)
61 |
--------------------------------------------------------------------------------
/test/common/utils/Utils.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var expect = require('chai').expect,
4 | jmap = require('../../../dist/jmap-client');
5 |
6 | describe('The Utils class', function() {
7 |
8 | describe('The constructor', function() {
9 |
10 | it('should throw an Error', function() {
11 | expect(function() {
12 | new jmap.Utils();
13 | }).to.throw(Error);
14 | });
15 |
16 | });
17 |
18 | describe('The isDefined static method', function() {
19 |
20 | it('should return false for undefined', function() {
21 | expect(jmap.Utils.isDefined(undefined)).to.equal(false);
22 | });
23 |
24 | it('should return false for null', function() {
25 | expect(jmap.Utils.isDefined(null)).to.equal(false);
26 | });
27 |
28 | it('should return true for zero', function() {
29 | expect(jmap.Utils.isDefined(0)).to.equal(true);
30 | });
31 |
32 | it('should return true for false', function() {
33 | expect(jmap.Utils.isDefined(false)).to.equal(true);
34 | });
35 |
36 | it('should return true for empty string', function() {
37 | expect(jmap.Utils.isDefined('')).to.equal(true);
38 | });
39 |
40 | it('should return true for empty array', function() {
41 | expect(jmap.Utils.isDefined([])).to.equal(true);
42 | });
43 |
44 | it('should return true for object', function() {
45 | expect(jmap.Utils.isDefined({})).to.equal(true);
46 | });
47 | });
48 |
49 | describe('The assertRequiredParameterIsPresent static method', function() {
50 |
51 | it('should not throw an Error if the parameter is defined', function() {
52 | expect(jmap.Utils.assertRequiredParameterIsPresent({})).to.deep.equal({});
53 | });
54 |
55 | it('should not throw an Error if the parameter is false', function() {
56 | expect(jmap.Utils.assertRequiredParameterIsPresent(false, 'parameter')).to.equal(false);
57 | });
58 |
59 | it('should not throw an Error if the parameter is zero', function() {
60 | expect(jmap.Utils.assertRequiredParameterIsPresent(0, 'parameter')).to.equal(0);
61 | });
62 |
63 | it('should not throw an Error if the parameter is empty string', function() {
64 | expect(jmap.Utils.assertRequiredParameterIsPresent('', 'parameter')).to.equal('');
65 | });
66 |
67 | it('should throw an Error if the parameter is null', function() {
68 | expect(function() {
69 | jmap.Utils.assertRequiredParameterIsPresent(null, 'parameter');
70 | }).to.throw(Error);
71 | });
72 |
73 | it('should throw an Error if the parameter is undefined', function() {
74 | expect(function() {
75 | jmap.Utils.assertRequiredParameterIsPresent();
76 | }).to.throw(Error);
77 | });
78 |
79 | });
80 |
81 | });
82 |
--------------------------------------------------------------------------------
/test/frontend/transport/JQueryTransport.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /* global chai: false, jmap: false */
4 |
5 | var expect = chai.expect;
6 |
7 | function newTransport() {
8 | return new jmap.JQueryTransport(new jmap.QPromiseProvider());
9 | }
10 |
11 | describe('The JQueryTransport class', function() {
12 |
13 | before(function() {
14 | jQuery.mockjaxSettings.logging = false;
15 | });
16 |
17 | afterEach(function() {
18 | jQuery.mockjax.clear();
19 | });
20 |
21 | function expectTransportError(err, cause, statusCode, responseText) {
22 | expect(err).to.be.a.instanceof(jmap.TransportError);
23 | expect(err.cause).to.equal(cause);
24 | expect(err.statusCode).to.equal(statusCode);
25 | expect(err.responseText).to.equal(responseText);
26 | }
27 |
28 | describe('The post method', function() {
29 |
30 | it('should reject the promise when an error occurs', function(done) {
31 | jQuery.mockjax({
32 | url: 'http://test',
33 | isTimeout: true
34 | });
35 |
36 | newTransport()
37 | .post('http://test')
38 | .then(null, function(err) {
39 | expectTransportError(err, 'timeout', 0, null);
40 |
41 | done();
42 | });
43 | });
44 |
45 | it('should reject the promise when request fails with an error status code', function(done) {
46 | jQuery.mockjax({
47 | url: 'http://test',
48 | status: 400,
49 | statusText: 'Bad Request',
50 | responseText: '{"error":"failure"}'
51 | });
52 |
53 | newTransport()
54 | .post('http://test')
55 | .then(null, function(err) {
56 | expectTransportError(err, 'Bad Request', 400, '{"error":"failure"}');
57 |
58 | done();
59 | });
60 | });
61 |
62 | it('should resolve the promise when request succeeds', function(done) {
63 | jQuery.mockjax({
64 | url: 'http://test',
65 | status: 200,
66 | responseText: '[]'
67 | });
68 |
69 | newTransport()
70 | .post('http://test')
71 | .then(function() {
72 | done();
73 | });
74 | });
75 |
76 | it('should serialize data to JSON, build a correct options object, and pass it to jQuery', function(done) {
77 | jQuery.mockjax({
78 | url: 'http://test',
79 | response: function(options) {
80 | expect(options).to.deep.equal({
81 | method: 'POST',
82 | url: 'http://test',
83 | headers: {
84 | Authorization: 'SuperSecretToken'
85 | },
86 | data: '{"a":"b","c":0}',
87 | dataType: 'json',
88 | jsonp: false,
89 | processData: false,
90 | crossDomain: false
91 | });
92 |
93 | this.responseText = '[]';
94 | }
95 | });
96 |
97 | newTransport()
98 | .post('http://test', {
99 | Authorization: 'SuperSecretToken'
100 | }, {
101 | a: 'b',
102 | c: 0
103 | })
104 | .then(function() {
105 | done();
106 | });
107 | });
108 |
109 | it('should build a correct options object and pass it to jQuery, when using raw mode', function(done) {
110 | jQuery.mockjax({
111 | url: 'http://test',
112 | response: function(options) {
113 | expect(options).to.deep.equal({
114 | method: 'POST',
115 | url: 'http://test',
116 | headers: {
117 | Authorization: 'SuperSecretToken'
118 | },
119 | data: null,
120 | dataType: undefined,
121 | jsonp: false,
122 | processData: false,
123 | crossDomain: false
124 | });
125 |
126 | this.responseText = '[]';
127 | }
128 | });
129 |
130 | newTransport().post('http://test', { Authorization: 'SuperSecretToken' }, null, true)
131 | .then(function() {
132 | done();
133 | });
134 | });
135 |
136 | });
137 |
138 | });
139 |
--------------------------------------------------------------------------------
/test/backend/transport/RequestTransport.js:
--------------------------------------------------------------------------------
1 | const { expect } = require('chai');
2 | const mockery = require('mockery');
3 | const jmap = require('../../../dist/jmap-client');
4 |
5 | function newTransport() {
6 | return new jmap.RequestTransport(new jmap.QPromiseProvider());
7 | }
8 |
9 | describe('The RequestTransport class', function() {
10 |
11 | beforeEach(function() {
12 | mockery.enable({
13 | useCleanCache: true,
14 | warnOnReplace: false,
15 | warnOnUnregistered: false
16 | });
17 | });
18 |
19 | afterEach(function() {
20 | mockery.disable();
21 | });
22 |
23 | function expectTransportError(err, cause, statusCode, responseText) {
24 | expect(err).to.be.a.instanceof(jmap.TransportError);
25 | expect(err.cause).to.equal(cause);
26 | expect(err.statusCode).to.equal(statusCode);
27 | expect(err.responseText).to.equal(responseText);
28 | }
29 |
30 | describe('The post method', function() {
31 |
32 | it('should reject the promise when an error occurs', function(done) {
33 | var cause = new Error('Failed');
34 |
35 | mockery.registerMock('request', function(options, callback) {
36 | callback(cause);
37 | });
38 |
39 | newTransport().post('').then(null, function(err) {
40 | expectTransportError(err, cause, 0, null);
41 |
42 | done();
43 | });
44 | });
45 |
46 | it.only('should reject the promise when status code is not 200 nor 201', function(done) {
47 | mockery.registerMock('request', function(options, callback) {
48 | callback(null, {
49 | statusCode: 400
50 | }, '{}');
51 | });
52 |
53 | newTransport().post('').then(null, function(err) {
54 | expectTransportError(err, null, 400, '{}');
55 |
56 | done();
57 | });
58 | });
59 |
60 | it('should resolve the promise when status code is 200, returning the body', function(done) {
61 | mockery.registerMock('request', function(options, callback) {
62 | callback(null, {
63 | statusCode: 200
64 | }, 'Success !');
65 | });
66 |
67 | newTransport().post('').then(function(data) {
68 | expect(data).to.equal('Success !');
69 |
70 | done();
71 | });
72 | });
73 |
74 | it('should resolve the promise when status code is 201, returning the body', function(done) {
75 | mockery.registerMock('request', function(options, callback) {
76 | callback(null, {
77 | statusCode: 201
78 | }, 'Accepted !');
79 | });
80 |
81 | newTransport().post('').then(function(data) {
82 | expect(data).to.equal('Accepted !');
83 |
84 | done();
85 | });
86 | });
87 |
88 | it('should build a correct options object and pass it to request', function() {
89 | mockery.registerMock('request', function(options) {
90 | expect(options).to.deep.equal({
91 | method: 'POST',
92 | url: 'https://jmap.open-paas.org',
93 | headers: {
94 | Authorization: 'SuperSecretToken'
95 | },
96 | body: {
97 | a: 'b',
98 | c: 0
99 | },
100 | json: true
101 | });
102 | });
103 |
104 | newTransport().post('https://jmap.open-paas.org', {
105 | Authorization: 'SuperSecretToken'
106 | }, {
107 | a: 'b',
108 | c: 0
109 | });
110 | });
111 |
112 | it('should build a correct options object and pass it to request, when using raw mode', function() {
113 | mockery.registerMock('request', function(options) {
114 | expect(options).to.deep.equal({
115 | method: 'POST',
116 | url: 'https://jmap.open-paas.org',
117 | headers: {
118 | Authorization: 'SuperSecretToken'
119 | },
120 | body: {},
121 | json: false
122 | });
123 | });
124 |
125 | newTransport().post('https://jmap.open-paas.org', {
126 | Authorization: 'SuperSecretToken'
127 | }, {}, true);
128 | });
129 |
130 | });
131 |
132 | });
133 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # How to contribute to JMAP-CLIENT ?
2 |
3 | If you read this page, you're probably interested in contributing to the jmap-client library. That's awesome !
4 |
5 | ## Contributing code
6 |
7 | jmap-client tries to respect state of the art javascript coding guidelines. Here are some tips to get started
8 |
9 | ### getting started
10 |
11 | We use [GitHub](https://github.com/linagora/jmap-client) fork/pull request workflow to allow everyone to send patches to the project.
12 |
13 | Once you cloned the repository, use npm to install the dependencies:
14 | ```javascript
15 | npm install
16 | ```
17 |
18 | You can then transpile the library and run the tests by using grunt:
19 |
20 | ```javascript
21 | grunt
22 | ```
23 |
24 | If you don't have grunt installed, use the following command:
25 |
26 | ```javascript
27 | npm install -g grunt-cli
28 | ```
29 |
30 | ### philosophy
31 |
32 | jmap-client has been built around two well known patterns: promises, and fluent interface.
33 |
34 | [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) is a pattern to deal with asynchronous code.
35 | Instead of using callbacks, you return an object that represent the result of your async call, be it successful or not.
36 | It's so widely used that it has been included in the EcmaScript 2015 specification, which is the specification of the JavaScript language itself.
37 | The jmap-client library uses the promise implementation of your choice, we already provide [the EcmaScript 2015 Promise provider](https://github.com/linagora/jmap-client/blob/master/lib/promises/ES6PromiseProvider.js) and the [Q provider](https://github.com/linagora/jmap-client/blob/master/lib/promises/QPromiseProvider.js).
38 |
39 | Fluent interface is a nice way of writing code: you can chain calls to different class methods. An example is worth a thousand words:
40 |
41 | ```javascript
42 | new jmap.Client(, )
43 | .withAPIUrl('https://jmap.my.server.com')
44 | .withAuthenticationToken('YourAuthenticationToken')
45 | .getMailboxes()
46 | .then((mailboxes) => {
47 | // Do something with the list of mailboxes
48 | }, (err) => {
49 | // An error occured
50 | });
51 | ```
52 |
53 | ### Code formating and check
54 |
55 | The jmap-client use the [AirBNB JavaScript style convention](https://github.com/airbnb/javascript), with some relaxed rules:
56 |
57 | * we do not require new lines after block
58 | * we do not require a trailing comma at the end of arrays & objects
59 | * we allow declaration of multiple variables in a single "var" statement
60 | * we do not require a new line before a line comment
61 | * we do not enforce a special case for object properties
62 |
63 | All those settings can be found in the [code style checker configuration file](./.jscsrc).
64 |
65 | . We use [jshint](http://jshint.com/) static code analyser, and [jscs](http://jscs.info/) code style checker.
66 |
67 | At any moment, you can see if your code validates by running
68 |
69 | ```javascript
70 | grunt linters
71 | ```
72 |
73 | ### documentation
74 |
75 | The jmap-client project uses [jsdoc](http://usejsdoc.org/) version 3 and up. When you change code, please make sure to write or modify the related documentation, and make sure that the documentation displays nicely. We need a 100% coverage on public API documentation.
76 | At any moment, you can run
77 |
78 | ```javascript
79 | grunt apidoc
80 | ```
81 |
82 | to generate the documentation. Please note that the documentation is not commited in the repository.
83 |
84 | ### tests
85 |
86 | Tests are a super-important thing for the project developers.
87 | If you ever contribute code to the jmap-client library, please make sure you also provide the associated tests, with 100% coverage on your code.
88 | Tests is what ensure that the library will rock on the long run, and that many developers can work together on the same codebase.
89 |
90 | Thanks to the wonderfull abilities of the node.js ecosystem, we are able to have the same tests for the backend (read: node.js) and the frontend (browsers).
91 | You'll find all the tests in the [test/common](https://github.com/linagora/jmap-client/tree/master/test/common) folder.
92 | Tests specific to the frontend are located in [test/frontend](https://github.com/linagora/jmap-client/tree/master/test/frontend),
93 | and backend specifics... [you guessed it](https://github.com/linagora/jmap-client/tree/master/test/frontend).
94 |
95 | You can run tests at any moment in your development process by using the grunt command:
96 | ```javascript
97 | grunt
98 | ```
99 |
100 | ### Fill the change log
101 |
102 | The last step!
103 |
104 | Please before to open your pull-request, write in the CHANGELOG.md file a line explaining briefly your changes under the 'master' section.
105 |
106 | ## [master]
107 | ### Added
108 | - What I did #GITHUB_ISSUE
109 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 | All notable changes to this project will be documented in this file.
3 | This project adheres to [Semantic Versioning](http://semver.org/).
4 |
5 | ## [master]
6 |
7 | ## [0.0.30]
8 | ### Added
9 | - add 'to', 'cc', 'recipients' and 'subject' conditions to filter rules (#80)
10 |
11 | ## [0.0.29]
12 | ### Added
13 | - add JMap's filter feature #79
14 |
15 | ## [0.0.28] - 2018-05-17
16 | ### Added
17 | - add 'isForwarded' property to 'Message' #77
18 |
19 | ## [0.0.27] - 2018-03-12
20 | ### Added
21 | - add 'quota' property to 'Mailbox' #74
22 |
23 | ## [0.0.26] - 2018-03-08
24 | ### Added
25 | - add properties 'MDNSent' and 'MDNNotSent' to 'SetResponse' for read receipt notifications #72
26 |
27 | ### Changed
28 | - main entry point is now located in dist folder.
29 |
30 | ## [0.0.25] - 2017-10-10
31 | ### Added
32 | - Add 'namespace' and 'sharedWith' property to Mailbox. Fixes #69
33 | - JSONBuilder.appendObject
34 | - The ServerCapabilities model
35 | - AuthAccess.accounts. #58
36 | - AuthAccess.serverCapabilites. #58
37 | - AuthAccess.mailCapabilities. #58
38 |
39 | ### Changed
40 | - Migrate accounts + capabilities to new JMAP authentication spec. #58
41 |
42 | ## Removed
43 | - The AccountCapabilities model
44 |
45 | ## [0.0.24] - 2017-26-09
46 | ### Added
47 | - add required property blobId to Message. Fixes #65
48 |
49 | ## [0.0.23] - 2017-03-20
50 | ### Fixed
51 | - The Message model was not creating EMailer instances for To, CC and BCC fields. #63
52 |
53 | ## [0.0.22] - 2017-03-10
54 | ### Added
55 | - The AuthMethod model
56 | - The TransportError class. #18
57 | - The JmapError class. #18
58 |
59 | ### Changed
60 | - Use the X-JMAP authentication scheme to construct the Authorization header. #44
61 | - Data structures used during authentication procedure after changes to the JMAP spec. #44
62 | - The AuthContinuation model
63 | - Updated all development dependencies
64 | - SetError instances are now JmapError instances
65 | - Client.send now supports using an already available Outbox
66 |
67 | ## [0.0.20] - 2016-08-22
68 | ### Fixed
69 | - Utils.fillURITemplate now correctly encode replaced values.
70 |
71 | ## [0.0.19] - 2016-07-21
72 | ### Added
73 | - Added support for VacationResponse.isActivated. #53
74 |
75 | ### Changed
76 | - Attachment.size now defaults to 0 (was: null). #54
77 |
78 | ## [0.0.18] - 2016-07-08
79 | ### Added
80 | - Attachment.getSignedDownloadUrl. #50
81 | - Transport.post 'raw' parameter
82 | - Utils.appendQueryParameter
83 |
84 | ## [0.0.17] - 2016-07-06
85 | ### Changed
86 | - Client.createMailbox now resolves with a Mailbox object
87 |
88 | ### Removed
89 | - The CreateMailboxAck class.
90 |
91 | ## [0.0.16] - 2016-06-27
92 | ### Added
93 | - Client.destroyMailboxes #47
94 |
95 | ## [0.0.15] - 2016-06-17
96 | ### Added
97 | - Client.withAuthAccess
98 | - The VacationResponse model. #45
99 | - Client.getVacationResponse
100 | - The SetResponse class
101 | - Client.setVacationResponse
102 |
103 | ### Fixed
104 | - New/changed JMAP endpoint properties in AuthAcces. #38
105 |
106 | ### Removed
107 | - The MessagesSet class
108 | - The MailboxesSet class
109 |
110 | ## [0.0.14] - 2016-05-19
111 | ### Added
112 | - The AccountCapabilities class
113 | - The MailCapabilities class
114 | - Refactored Account capabilities to match the spec. #13
115 | - Account.hasMail
116 | - Account.hasCalendars
117 | - Account.hasContacts
118 |
119 | ### Fixed
120 | - Message.replyTo is now an array. #39
121 |
122 | ## [0.0.13] - 2016-02-24
123 | ### Fixed
124 | - getMessages responses now filter messages without mailboxIds
125 |
126 | ## [0.0.12] - 2016-02-18
127 | ### Added
128 | - Support multiple auth continue iterations #12
129 | - Client.promiseProvider
130 | - Client.send
131 |
132 | ## [0.0.11] - 2016-02-01
133 | ### Added
134 | - Client.destroyMessages
135 | - Thread.destroy
136 | - Thread.setIsFlagged
137 | - Thread.setIsUnread
138 | - Thread.move
139 | - Thread.moveToMailboxWithRole
140 |
141 | ## [0.0.10] - 2016-01-20
142 | ### Added
143 | - Support updateMessage #23
144 | - Add Client.updateMessage
145 | - Add Message.update
146 | - Add Message.setIsFlagged
147 | - Add Message.setIsUnread
148 | - Add Message.setIsAnswered
149 |
150 | ## [0.0.8] - 2016-01-18
151 | ### Added
152 | - Support setMailboxes #20
153 | - Add CreateMailboxAck class
154 | - Add MailboxesSet class
155 | - Add Client.setMailboxes
156 | - Add Client.createMailbox
157 | - Add Client.updateMailbox
158 | - Add Client.destroyMailbox
159 | - Add Mailbox.update
160 | - Add Mailbox.destroy
161 |
162 | ## [0.0.7] - 2015-12-21
163 | ### Added
164 | - Code style rules #9
165 | - Client.destroyMessage and Message.destroy #14
166 | - Password authentication method #8
167 | - Coverage tools #3
168 | - Add contribution detailled instructions
169 |
170 | ## [0.0.5] - 2015-11-02
171 | ### Added
172 | - The OutboundMessage and CreateMessageAck classes
173 | - The Client.saveAsDraft method
174 | - The JSONBuilder class to serialize models
175 |
176 | ## [0.0.4] - 2015-10-16
177 | ### Added
178 | - The Attachment class
179 | - Client.withDownloadUrl
180 | - Message.attachments
181 | - Utils.fillURITemplate
182 |
183 | ## [0.0.3] - 2015-10-05
184 | ### Added
185 | - The MessagesSet class
186 | - Client.setMessages
187 | - Client.moveMessage
188 | - Message.move
189 | - Utils.assertRequiredParameterIsArrayWithMinimumLength
190 | - The MailboxRole class
191 | - Client.getMailboxWithRole
192 | - Message.moveToMailboxWithRole
193 | - Client.authExternal
194 | - The Constants class
195 |
196 | ## [0.0.2] - 2015-09-22
197 | - First public release
198 |
--------------------------------------------------------------------------------
/Gruntfile.js:
--------------------------------------------------------------------------------
1 | module.exports = function(grunt) {
2 | grunt.initConfig({
3 | pkg: grunt.file.readJSON('package.json'),
4 |
5 | project: {
6 | lib: 'lib',
7 | test: 'test',
8 | dist: 'dist',
9 | doc: 'doc',
10 | apidoc: '<%= project.doc %>/api',
11 | name: 'jmap-client'
12 | },
13 |
14 | uglify: {
15 | dist: {
16 | files: [
17 | {
18 | dest: '<%= project.dist %>/<%= project.name %>.min.js',
19 | src: ['<%= project.dist %>/<%= project.name %>.js']
20 | }
21 | ]
22 | }
23 | },
24 |
25 | eslint: {
26 | all: {
27 | src: ['Gruntfile.js', 'lib/**/*.js', 'test/**/**/*.js']
28 | },
29 | options: {
30 | quiet: true
31 | }
32 | },
33 |
34 | lint_pattern: {
35 | options: {
36 | rules: [
37 | { pattern: /(describe|it)\.only/, message: 'Must not use .only in tests' }
38 | ]
39 | },
40 | all: {
41 | src: ['<%= eslint.all.src %>']
42 | }
43 | },
44 |
45 | mocha_istanbul: {
46 | coverage: {
47 | src: [
48 | '<%= project.test %>/common/',
49 | '<%= project.test %>/backend/'
50 | ],
51 | options: {
52 | require: ['chai'],
53 | reporter: 'spec',
54 | reportFormats: ['lcov', 'text-summary'],
55 | timeout: 3000,
56 | coverageFolder: 'coverage/backend',
57 | mask: '**/*.js',
58 | root: 'dist/'
59 | }
60 | }
61 | },
62 |
63 | lcovMerge: {
64 | options: {
65 | emitters: ['file'],
66 | outputFile: 'coverage/lcov-merged.info'
67 | },
68 | src: [
69 | 'coverage/backend/lcov.info',
70 | 'coverage/frontend/lcov.info'
71 | ]
72 | },
73 |
74 | coveralls: {
75 | options: {
76 | force: false // When true, grunt-coveralls will only print a warning rather than an error
77 | },
78 | publish: {
79 | src: 'coverage/lcov-merged.info'
80 | }
81 | },
82 |
83 | watch: {
84 | files: ['<%= eslint.all.src %>'],
85 | tasks: ['test']
86 | },
87 |
88 | // Empties folders to start fresh
89 | clean: {
90 | dist: {
91 | files: [{
92 | dot: true,
93 | src: [
94 | '<%= project.dist %>/*',
95 | '!<%= project.dist %>/.git*'
96 | ]
97 | }]
98 | },
99 | apidoc: {
100 | files: [{
101 | src: ['<%= project.apidoc %>/**/*']
102 | }]
103 | }
104 | },
105 |
106 | browserify: {
107 | dist: {
108 | options: {
109 | transform: [
110 | 'browserify-versionify',
111 | [
112 | 'babelify',
113 | {
114 | presets: ['es2015'],
115 | plugins: [
116 | ['transform-builtin-extend', { globals: ['Error'], approximate: true }],
117 | 'transform-object-assign',
118 | 'add-module-exports']
119 | }
120 | ]
121 | ],
122 | browserifyOptions: {
123 | standalone: 'jmap'
124 | },
125 | external: [
126 | 'request',
127 | 'q'
128 | ]
129 | },
130 | files: {
131 | '<%= project.dist %>/jmap-client.js': ['<%= project.lib %>/API.js']
132 | }
133 | }
134 | },
135 |
136 | karma: {
137 | unit: {
138 | configFile: '<%= project.test %>/config/karma.conf.js'
139 | }
140 | },
141 |
142 | jsdoc: {
143 | dist: {
144 | src: ['<%= project.lib %>/'],
145 | jsdoc: 'node_modules/jsdoc/jsdoc.js',
146 | options: {
147 | recurse: true,
148 | destination: '<%= project.apidoc %>',
149 | configure: '.jsdocrc'
150 | }
151 | }
152 | },
153 |
154 | release: {
155 | options: {
156 | file: 'package.json',
157 | additionalFiles: ['bower.json'],
158 | commitMessage: 'Bumped version to <%= version %>',
159 | tagName: 'v<%= version %>',
160 | tagMessage: 'Version <%= version %>',
161 | afterBump: ['exec:gitcheckout_ReleaseBranch', 'test', 'apidoc'],
162 | beforeRelease: ['exec:gitadd_DistAndAPIDoc', 'exec:gitcommit_DistAndAPIDoc'],
163 | afterRelease: ['exec:gitcheckout_master']
164 | }
165 | },
166 |
167 | exec: {
168 | gitcheckout_ReleaseBranch: {
169 | cmd: function() {
170 | return 'git checkout -b release-' + this.file.readJSON('package.json').version;
171 | }
172 | },
173 | gitcheckout_master: {
174 | cmd: 'git checkout master'
175 | },
176 | gitadd_DistAndAPIDoc: {
177 | cmd: 'git add -f dist/ doc/api/'
178 | },
179 | gitcommit_DistAndAPIDoc: {
180 | cmd: function() {
181 | return 'git commit -m"Added distribution and API documentation for release."';
182 | }
183 | }
184 | }
185 | });
186 |
187 | require('load-grunt-tasks')(grunt);
188 | grunt.loadNpmTasks('grunt-mocha-istanbul');
189 | grunt.loadNpmTasks('grunt-coveralls');
190 | grunt.loadNpmTasks('grunt-eslint');
191 |
192 | grunt.registerTask('compile', 'Compile from ES6 to ES5', ['clean:dist', 'browserify', 'uglify']);
193 | grunt.registerTask('coverage', ['test', 'lcovMerge', 'coveralls:publish']);
194 | grunt.registerTask('linters', 'Check code for lint', ['eslint:all', 'lint_pattern:all']);
195 | grunt.registerTask('test', 'Lint, compile and launch test suite', ['linters', 'compile', 'mocha_istanbul:coverage', 'karma']);
196 | grunt.registerTask('dev', 'Launch tests then for each changes relaunch it', ['test', 'watch']);
197 | grunt.registerTask('apidoc', 'Generates API documentation', ['clean:apidoc', 'jsdoc']);
198 |
199 | grunt.registerTask('default', ['test']);
200 |
201 | };
202 |
--------------------------------------------------------------------------------