feat(tasks): add Create, Update, Destroy tasks plus spec & lint fixes
Summary:
1. **Generic CUD Tasks**: There is now a generic `CreateModelTask`,
`UpdateModelTask`, and `DestroyModelTask`. These can either be used as-is
or trivially overridden to easily update simple objects. Hopefully all of
the boilerplate rollback, error handling, and undo logic won't have to be
re-duplicated on every task. There are also tests for these tasks. We use
them to perform mutating actions on `Metadata` objects.
1. **Failing on Promise Rejects**: Turns out that if a Promise rejected
due to an error or `Promise.reject` we were ignoring it and letting tests
pass. Now, tests will Fail if any unhandled promise rejects. This
uncovered a variety of errors throughout the test suite that had to be
fixed. The most significant one was during the `theme-manager` tests when
all packages (and their stores with async DB requests) was loaded. Long
after the `theme-manager` specs finished, those DB requests were
(somtimes) silently failing.
1. **Globally stub `DatabaseStore._query`**: All tests shouldn't actually
make queries on the database. Furthremore, the `inTransaction` block
doesn't resolve at all unless `_query` is stubbed. Instead of manually
remembering to do this in every test that touches the DB, it's now mocked
in `spec_helper`. This broke a handful of tests that needed to be manually
fixed.
1. **ESLint Fixes**: Some minor fixes to the linter config to prevent
yelling about minor ES6 things and ensuring we have the correct parser.
Test Plan: new tests
Reviewers: bengotow, juan, drew
Differential Revision: https://phab.nylas.com/D2419
Remove cloudState and N1-Send-Later
2016-01-05 08:39:14 +08:00
|
|
|
import {
|
|
|
|
Task,
|
|
|
|
NylasAPI,
|
|
|
|
APIError,
|
|
|
|
CreateModelTask,
|
|
|
|
DatabaseTransaction } from 'nylas-exports'
|
|
|
|
|
|
|
|
describe("CreateModelTask", () => {
|
|
|
|
beforeEach(() => {
|
|
|
|
spyOn(DatabaseTransaction.prototype, "persistModel")
|
|
|
|
});
|
|
|
|
|
|
|
|
it("constructs without error", () => {
|
|
|
|
const t = new CreateModelTask()
|
|
|
|
expect(t._rememberedToCallSuper).toBe(true)
|
|
|
|
});
|
|
|
|
|
|
|
|
describe("performLocal", () => {
|
|
|
|
it("throws if basic fields are missing", () => {
|
|
|
|
const t = new CreateModelTask()
|
|
|
|
try {
|
|
|
|
t.performLocal()
|
|
|
|
throw new Error("Shouldn't succeed");
|
|
|
|
} catch (e) {
|
|
|
|
expect(e.message).toMatch(/^Must pass.*/)
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
it("throws if `requiredFields` are missing", () => {
|
|
|
|
const accountId = "a123"
|
|
|
|
const modelName = "Metadata"
|
|
|
|
const endpoint = "/endpoint"
|
|
|
|
const data = {foo: "bar"}
|
|
|
|
const requiredFields = ["stuff"]
|
|
|
|
const t = new CreateModelTask({accountId, modelName, data, endpoint, requiredFields})
|
|
|
|
try {
|
|
|
|
t.performLocal()
|
|
|
|
throw new Error("Shouldn't succeed");
|
|
|
|
} catch (e) {
|
|
|
|
expect(e.message).toMatch(/^Must pass data field.*/)
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
it("throws if the model name can't be found", () => {
|
|
|
|
const accountId = "a123"
|
|
|
|
const modelName = "dne"
|
|
|
|
const endpoint = "/endpoint"
|
|
|
|
const data = {stuff: "bar"}
|
|
|
|
const requiredFields = ["stuff"]
|
|
|
|
const t = new CreateModelTask({accountId, modelName, data, endpoint, requiredFields})
|
|
|
|
try {
|
|
|
|
t.performLocal()
|
|
|
|
throw new Error("Shouldn't succeed");
|
|
|
|
} catch (e) {
|
|
|
|
expect(e.message).toMatch(/^Couldn't find the class for.*/)
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
it("persists the new model properly", () => {
|
|
|
|
const persistFn = DatabaseTransaction.prototype.persistModel
|
|
|
|
const accountId = "a123"
|
|
|
|
const modelName = "Metadata"
|
|
|
|
const endpoint = "/endpoint"
|
feat(metadata): add cloudState that sync with Metadata service
Summary:
Now all plugins get passed a `cloudState` object to their `activate`
method.
The `cloudState` object is an instance of `CloudState` and acts like a
key-value store backed by the yet-to-be-implemented Metadata service.
It has a `get`, `getAll`, and `observe` method. The `observe` method
returns a new `Rx.Observable` for the given key.
It has a `set`, and `unset` method that doesn't actually mutate state, but
rather dispatches new `Task`s to Create, Update, and Delete `Metadata`
objects.
The whole object is backed by `Metadata` objects. Since these are standard
Database Objects that will appear on the delta sync streaming API, any
updates from the server will automatically propagate down to listening
views via the `Rx.Observable`s.
Additionally, there is a new `N1-Send-Later` stub plugin that demonstrates
how to use the `cloudState`.
There are few other minor refactors included in this diff:
**Generic CUD Tasks**: There is now a generic `CreateModelTask`,
`UpdateModelTask`, and `DestroyModelTask`. These can either be used as-is
or trivially overridden to easily update simple objects. Hopefully all of
the boilerplate rollback, error handling, and undo logic won't have to be
re-duplicated on every task. There are also tests for these tasks. We use
them to perform mutating actions on `Metadata` objects.
**New `boundProps` for `InjectedComponents`**: When making the
`N1-Send_later` plugin, I realized that the injected component needed to
get the `cloudState` somehow. Traditionally components would require
Stores and load data that way, but these are setup at `require`-time. Now
that `cloudState` only is available on `activate` we needed a way to get
the data to the components. There's now the concept of `boundProps` which
will be props added to the Component when it gets injected. This required
changing the return signature of `findComponentMatching`, which got
renamed to `findComponentDataMatching`.
**Failing on Promise Rejects**: Turns out that if a Promise rejected
due to an error or `Promise.reject` we were ignoring it and letting tests
pass. Now, tests will Fail if any unhandled promise rejects. This
uncovered a variety of errors throughout the test suite that had to be
fixed. The most significant one was during the `theme-manager` tests when
all packages (and their stores with async DB requests) was loaded. Long
after the `theme-manager` specs finished, those DB requests were
(somtimes) silently failing.
**Globally stub `DatabaseStore._query`**: All tests shouldn't actually
make queries on the database. Furthremore, the `inTransaction` block
doesn't resolve at all unless `_query` is stubbed. Instead of manually
remembering to do this in every test that touches the DB, it's now mocked
in `spec_helper`. This broke a handful of tests that needed to be manually
fixed.
**ESLint Fixes**: Some minor fixes to the linter config to prevent
yelling about minor ES6 things and ensuring we have the correct parser.
Test Plan: new tests
Reviewers: drew, bengotow, juan
Reviewed By: juan
Differential Revision: https://phab.nylas.com/D2419
2016-02-03 04:28:06 +08:00
|
|
|
const data = {value: "bar"}
|
|
|
|
const requiredFields = ["value"]
|
feat(tasks): add Create, Update, Destroy tasks plus spec & lint fixes
Summary:
1. **Generic CUD Tasks**: There is now a generic `CreateModelTask`,
`UpdateModelTask`, and `DestroyModelTask`. These can either be used as-is
or trivially overridden to easily update simple objects. Hopefully all of
the boilerplate rollback, error handling, and undo logic won't have to be
re-duplicated on every task. There are also tests for these tasks. We use
them to perform mutating actions on `Metadata` objects.
1. **Failing on Promise Rejects**: Turns out that if a Promise rejected
due to an error or `Promise.reject` we were ignoring it and letting tests
pass. Now, tests will Fail if any unhandled promise rejects. This
uncovered a variety of errors throughout the test suite that had to be
fixed. The most significant one was during the `theme-manager` tests when
all packages (and their stores with async DB requests) was loaded. Long
after the `theme-manager` specs finished, those DB requests were
(somtimes) silently failing.
1. **Globally stub `DatabaseStore._query`**: All tests shouldn't actually
make queries on the database. Furthremore, the `inTransaction` block
doesn't resolve at all unless `_query` is stubbed. Instead of manually
remembering to do this in every test that touches the DB, it's now mocked
in `spec_helper`. This broke a handful of tests that needed to be manually
fixed.
1. **ESLint Fixes**: Some minor fixes to the linter config to prevent
yelling about minor ES6 things and ensuring we have the correct parser.
Test Plan: new tests
Reviewers: bengotow, juan, drew
Differential Revision: https://phab.nylas.com/D2419
Remove cloudState and N1-Send-Later
2016-01-05 08:39:14 +08:00
|
|
|
const t = new CreateModelTask({accountId, modelName, data, endpoint, requiredFields})
|
|
|
|
window.waitsForPromise(() => {
|
|
|
|
return t.performLocal().then(() => {
|
|
|
|
expect(persistFn).toHaveBeenCalled()
|
|
|
|
const model = persistFn.calls[0].args[0]
|
|
|
|
expect(model.constructor.name).toBe(modelName)
|
|
|
|
expect(model.value).toBe("bar")
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe("performRemote", () => {
|
|
|
|
const accountId = "a123"
|
|
|
|
const modelName = "Metadata"
|
|
|
|
const endpoint = "/endpoint"
|
feat(metadata): add cloudState that sync with Metadata service
Summary:
Now all plugins get passed a `cloudState` object to their `activate`
method.
The `cloudState` object is an instance of `CloudState` and acts like a
key-value store backed by the yet-to-be-implemented Metadata service.
It has a `get`, `getAll`, and `observe` method. The `observe` method
returns a new `Rx.Observable` for the given key.
It has a `set`, and `unset` method that doesn't actually mutate state, but
rather dispatches new `Task`s to Create, Update, and Delete `Metadata`
objects.
The whole object is backed by `Metadata` objects. Since these are standard
Database Objects that will appear on the delta sync streaming API, any
updates from the server will automatically propagate down to listening
views via the `Rx.Observable`s.
Additionally, there is a new `N1-Send-Later` stub plugin that demonstrates
how to use the `cloudState`.
There are few other minor refactors included in this diff:
**Generic CUD Tasks**: There is now a generic `CreateModelTask`,
`UpdateModelTask`, and `DestroyModelTask`. These can either be used as-is
or trivially overridden to easily update simple objects. Hopefully all of
the boilerplate rollback, error handling, and undo logic won't have to be
re-duplicated on every task. There are also tests for these tasks. We use
them to perform mutating actions on `Metadata` objects.
**New `boundProps` for `InjectedComponents`**: When making the
`N1-Send_later` plugin, I realized that the injected component needed to
get the `cloudState` somehow. Traditionally components would require
Stores and load data that way, but these are setup at `require`-time. Now
that `cloudState` only is available on `activate` we needed a way to get
the data to the components. There's now the concept of `boundProps` which
will be props added to the Component when it gets injected. This required
changing the return signature of `findComponentMatching`, which got
renamed to `findComponentDataMatching`.
**Failing on Promise Rejects**: Turns out that if a Promise rejected
due to an error or `Promise.reject` we were ignoring it and letting tests
pass. Now, tests will Fail if any unhandled promise rejects. This
uncovered a variety of errors throughout the test suite that had to be
fixed. The most significant one was during the `theme-manager` tests when
all packages (and their stores with async DB requests) was loaded. Long
after the `theme-manager` specs finished, those DB requests were
(somtimes) silently failing.
**Globally stub `DatabaseStore._query`**: All tests shouldn't actually
make queries on the database. Furthremore, the `inTransaction` block
doesn't resolve at all unless `_query` is stubbed. Instead of manually
remembering to do this in every test that touches the DB, it's now mocked
in `spec_helper`. This broke a handful of tests that needed to be manually
fixed.
**ESLint Fixes**: Some minor fixes to the linter config to prevent
yelling about minor ES6 things and ensuring we have the correct parser.
Test Plan: new tests
Reviewers: drew, bengotow, juan
Reviewed By: juan
Differential Revision: https://phab.nylas.com/D2419
2016-02-03 04:28:06 +08:00
|
|
|
const data = {value: "bar"}
|
feat(tasks): add Create, Update, Destroy tasks plus spec & lint fixes
Summary:
1. **Generic CUD Tasks**: There is now a generic `CreateModelTask`,
`UpdateModelTask`, and `DestroyModelTask`. These can either be used as-is
or trivially overridden to easily update simple objects. Hopefully all of
the boilerplate rollback, error handling, and undo logic won't have to be
re-duplicated on every task. There are also tests for these tasks. We use
them to perform mutating actions on `Metadata` objects.
1. **Failing on Promise Rejects**: Turns out that if a Promise rejected
due to an error or `Promise.reject` we were ignoring it and letting tests
pass. Now, tests will Fail if any unhandled promise rejects. This
uncovered a variety of errors throughout the test suite that had to be
fixed. The most significant one was during the `theme-manager` tests when
all packages (and their stores with async DB requests) was loaded. Long
after the `theme-manager` specs finished, those DB requests were
(somtimes) silently failing.
1. **Globally stub `DatabaseStore._query`**: All tests shouldn't actually
make queries on the database. Furthremore, the `inTransaction` block
doesn't resolve at all unless `_query` is stubbed. Instead of manually
remembering to do this in every test that touches the DB, it's now mocked
in `spec_helper`. This broke a handful of tests that needed to be manually
fixed.
1. **ESLint Fixes**: Some minor fixes to the linter config to prevent
yelling about minor ES6 things and ensuring we have the correct parser.
Test Plan: new tests
Reviewers: bengotow, juan, drew
Differential Revision: https://phab.nylas.com/D2419
Remove cloudState and N1-Send-Later
2016-01-05 08:39:14 +08:00
|
|
|
|
|
|
|
beforeEach(() => {
|
|
|
|
this.task = new CreateModelTask({accountId, modelName, data, endpoint})
|
|
|
|
});
|
|
|
|
|
|
|
|
const performRemote = (fn) => {
|
|
|
|
window.waitsForPromise(() => {
|
|
|
|
return this.task.performLocal().then(() => {
|
|
|
|
return this.task.performRemote().then(fn)
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
it("makes a POST request to the Nylas API", () => {
|
|
|
|
spyOn(NylasAPI, "makeRequest").andReturn(Promise.resolve())
|
|
|
|
performRemote(() => {
|
|
|
|
const opts = NylasAPI.makeRequest.calls[0].args[0]
|
|
|
|
expect(opts.method).toBe("POST")
|
feat(metadata): add cloudState that sync with Metadata service
Summary:
Now all plugins get passed a `cloudState` object to their `activate`
method.
The `cloudState` object is an instance of `CloudState` and acts like a
key-value store backed by the yet-to-be-implemented Metadata service.
It has a `get`, `getAll`, and `observe` method. The `observe` method
returns a new `Rx.Observable` for the given key.
It has a `set`, and `unset` method that doesn't actually mutate state, but
rather dispatches new `Task`s to Create, Update, and Delete `Metadata`
objects.
The whole object is backed by `Metadata` objects. Since these are standard
Database Objects that will appear on the delta sync streaming API, any
updates from the server will automatically propagate down to listening
views via the `Rx.Observable`s.
Additionally, there is a new `N1-Send-Later` stub plugin that demonstrates
how to use the `cloudState`.
There are few other minor refactors included in this diff:
**Generic CUD Tasks**: There is now a generic `CreateModelTask`,
`UpdateModelTask`, and `DestroyModelTask`. These can either be used as-is
or trivially overridden to easily update simple objects. Hopefully all of
the boilerplate rollback, error handling, and undo logic won't have to be
re-duplicated on every task. There are also tests for these tasks. We use
them to perform mutating actions on `Metadata` objects.
**New `boundProps` for `InjectedComponents`**: When making the
`N1-Send_later` plugin, I realized that the injected component needed to
get the `cloudState` somehow. Traditionally components would require
Stores and load data that way, but these are setup at `require`-time. Now
that `cloudState` only is available on `activate` we needed a way to get
the data to the components. There's now the concept of `boundProps` which
will be props added to the Component when it gets injected. This required
changing the return signature of `findComponentMatching`, which got
renamed to `findComponentDataMatching`.
**Failing on Promise Rejects**: Turns out that if a Promise rejected
due to an error or `Promise.reject` we were ignoring it and letting tests
pass. Now, tests will Fail if any unhandled promise rejects. This
uncovered a variety of errors throughout the test suite that had to be
fixed. The most significant one was during the `theme-manager` tests when
all packages (and their stores with async DB requests) was loaded. Long
after the `theme-manager` specs finished, those DB requests were
(somtimes) silently failing.
**Globally stub `DatabaseStore._query`**: All tests shouldn't actually
make queries on the database. Furthremore, the `inTransaction` block
doesn't resolve at all unless `_query` is stubbed. Instead of manually
remembering to do this in every test that touches the DB, it's now mocked
in `spec_helper`. This broke a handful of tests that needed to be manually
fixed.
**ESLint Fixes**: Some minor fixes to the linter config to prevent
yelling about minor ES6 things and ensuring we have the correct parser.
Test Plan: new tests
Reviewers: drew, bengotow, juan
Reviewed By: juan
Differential Revision: https://phab.nylas.com/D2419
2016-02-03 04:28:06 +08:00
|
|
|
expect(opts.body.value).toBe("bar")
|
feat(tasks): add Create, Update, Destroy tasks plus spec & lint fixes
Summary:
1. **Generic CUD Tasks**: There is now a generic `CreateModelTask`,
`UpdateModelTask`, and `DestroyModelTask`. These can either be used as-is
or trivially overridden to easily update simple objects. Hopefully all of
the boilerplate rollback, error handling, and undo logic won't have to be
re-duplicated on every task. There are also tests for these tasks. We use
them to perform mutating actions on `Metadata` objects.
1. **Failing on Promise Rejects**: Turns out that if a Promise rejected
due to an error or `Promise.reject` we were ignoring it and letting tests
pass. Now, tests will Fail if any unhandled promise rejects. This
uncovered a variety of errors throughout the test suite that had to be
fixed. The most significant one was during the `theme-manager` tests when
all packages (and their stores with async DB requests) was loaded. Long
after the `theme-manager` specs finished, those DB requests were
(somtimes) silently failing.
1. **Globally stub `DatabaseStore._query`**: All tests shouldn't actually
make queries on the database. Furthremore, the `inTransaction` block
doesn't resolve at all unless `_query` is stubbed. Instead of manually
remembering to do this in every test that touches the DB, it's now mocked
in `spec_helper`. This broke a handful of tests that needed to be manually
fixed.
1. **ESLint Fixes**: Some minor fixes to the linter config to prevent
yelling about minor ES6 things and ensuring we have the correct parser.
Test Plan: new tests
Reviewers: bengotow, juan, drew
Differential Revision: https://phab.nylas.com/D2419
Remove cloudState and N1-Send-Later
2016-01-05 08:39:14 +08:00
|
|
|
})
|
|
|
|
});
|
|
|
|
|
|
|
|
it("marks task success on API success", () => {
|
|
|
|
spyOn(NylasAPI, "makeRequest").andReturn(Promise.resolve())
|
|
|
|
performRemote((status) => {
|
|
|
|
expect(status).toBe(Task.Status.Success)
|
|
|
|
})
|
|
|
|
});
|
|
|
|
|
|
|
|
it("retries on non permanent errors", () => {
|
|
|
|
spyOn(NylasAPI, "makeRequest").andCallFake(() => {
|
|
|
|
return Promise.reject(new APIError({statusCode: 429}))
|
|
|
|
})
|
|
|
|
performRemote((status) => {
|
|
|
|
expect(status).toBe(Task.Status.Retry)
|
|
|
|
})
|
|
|
|
});
|
|
|
|
|
|
|
|
it("fails on permanent errors", () => {
|
|
|
|
const err = new APIError({statusCode: 500})
|
|
|
|
spyOn(NylasAPI, "makeRequest").andCallFake(() => {
|
|
|
|
return Promise.reject(err)
|
|
|
|
})
|
|
|
|
performRemote((status) => {
|
|
|
|
expect(status).toEqual([Task.Status.Failed, err])
|
|
|
|
})
|
|
|
|
});
|
|
|
|
|
|
|
|
it("fails on other thrown errors", () => {
|
|
|
|
const err = new Error("foo")
|
|
|
|
spyOn(NylasAPI, "makeRequest").andCallFake(() => {
|
|
|
|
return Promise.reject(err)
|
|
|
|
})
|
|
|
|
performRemote((status) => {
|
|
|
|
expect(status).toEqual([Task.Status.Failed, err])
|
|
|
|
})
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe("undo", () => {
|
|
|
|
const accountId = "a123"
|
|
|
|
const modelName = "Metadata"
|
|
|
|
const endpoint = "/endpoint"
|
|
|
|
const data = {key: "foo", value: "bar"}
|
|
|
|
|
|
|
|
beforeEach(() => {
|
|
|
|
this.task = new CreateModelTask({accountId, modelName, data, endpoint})
|
|
|
|
});
|
|
|
|
|
|
|
|
it("indicates it's undoable", () => {
|
|
|
|
expect(this.task.canBeUndone()).toBe(true)
|
|
|
|
expect(this.task.isUndo()).toBe(false)
|
|
|
|
});
|
|
|
|
|
|
|
|
it("creates the appropriate DestroyModelTask", () => {
|
|
|
|
window.waitsForPromise(() => {
|
|
|
|
return this.task.performLocal().then(() => {
|
|
|
|
const undoTask = this.task.createUndoTask()
|
|
|
|
expect(undoTask.constructor.name).toBe("DestroyModelTask")
|
|
|
|
expect(undoTask.clientId).toBe(this.task.model.clientId)
|
|
|
|
expect(undoTask.isUndo()).toBe(true)
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|