-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
049ed1b
commit 96afd3f
Showing
10 changed files
with
811 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
--- | ||
title: Error Handling | ||
--- | ||
|
||
GrAMPS comes with optional error handling. | ||
|
||
- | ||
{:toc} | ||
|
||
##### `GrampsError([errorData])` | ||
|
||
TKTK | ||
|
||
###### Parameters | ||
|
||
- `TKTK`: TKTK | ||
|
||
###### Return Value | ||
|
||
TKTK | ||
|
||
###### Example | ||
|
||
```js | ||
// TKTK | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
--- | ||
title: Test Helpers | ||
--- | ||
|
||
These functions are used to make it easier to write tests for GrAMPS data sources. | ||
|
||
- | ||
{:toc} | ||
|
||
##### `expectMockFields(resolver, fieldArray)` | ||
|
||
Creates Jest tests for each field in `fieldArray` to ensure that the `resolver` returns a mock value for it. | ||
|
||
> **NOTE:** This helper is intended for use with mock resolvers. | ||
###### Parameters | ||
|
||
- `resolver`: a mock resolver for a given GraphQL type | ||
- `fieldArray`: an array of field names that should be mocked | ||
|
||
###### Return Value | ||
|
||
Returns a [Jest test](https://facebook.github.io/jest/docs/en/api.html#testname-fn). | ||
|
||
###### Example | ||
|
||
Assuming type `PFX_MyType` with two fields, `fieldOne` and `fieldTwo`, which both have mock resolvers defined: | ||
|
||
```js | ||
import resolvers from '../src/resolvers'; | ||
|
||
describe('PFX_MyType', () => { | ||
const mockResolver = resolvers.mockResolvers.PFX_MyType(); | ||
expectMockFields(mockResolver, ['fieldOne', 'fieldTwo']); | ||
}); | ||
``` | ||
|
||
##### `expectMockList(resolver, fieldArray)` | ||
|
||
Creates Jest tests for each field in `fieldArray` to ensure that the `resolver` returns an instance of [`MockList`](http://dev.apollodata.com/tools/graphql-tools/mocking.html#Using-MockList-in-resolvers). | ||
|
||
> **NOTE:** This helper is intended for use with mock resolvers. | ||
###### Parameters | ||
|
||
- `resolver`: a mock resolver for a given GraphQL type | ||
- `fieldArray`: an array of field names that should be mocked | ||
|
||
###### Return Value | ||
|
||
Returns an array of [Jest tests](https://facebook.github.io/jest/docs/en/api.html#testname-fn). | ||
|
||
###### Example | ||
|
||
Assuming type `PFX_MyType` with two fields, `fieldOne` and `fieldTwo`, which both use `MockList` to generate an array of mock data: | ||
|
||
```js | ||
import resolvers from '../src/resolvers'; | ||
|
||
describe('PFX_MyType', () => { | ||
const mockResolver = resolvers.mockResolvers.PFX_MyType(); | ||
expectMockList(mockResolver, ['fieldOne', 'fieldTwo']); | ||
}); | ||
``` | ||
|
||
##### `expectNullable(resolver, fieldArray)` | ||
|
||
Creates Jest tests for each field in `fieldArray` to ensure that the `resolver` returns `null` if a value isn’t found for the given field. | ||
|
||
> **NOTE:** This helper is intended for use with field resolvers. | ||
###### Parameters | ||
|
||
- `resolver`: a mock resolver for a given GraphQL type | ||
- `fieldArray`: an array of field names that should be mocked | ||
|
||
###### Return Value | ||
|
||
Returns an array of [Jest tests](https://facebook.github.io/jest/docs/en/api.html#testname-fn). | ||
|
||
###### Example | ||
|
||
Assuming type `PFX_MyType` with one nullable field, `fieldOne`: | ||
|
||
```js | ||
import resolvers from '../src/resolvers'; | ||
|
||
describe('PFX_MyType', () => { | ||
const resolver = resolvers.dataResolvers.PFX_MyType; | ||
expectNullable(resolver, ['fieldOne']); | ||
}); | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
--- | ||
title: How to Test GrAMPS Data Sources | ||
weight: 450 | ||
--- | ||
|
||
TKTK | ||
|
||
## How to Convert Keyed Objects to Arrays | ||
|
||
Something that can be tricky with GraphQL is the expectation that we know _exactly_ what fields are going to arrive. But we don’t always have that information — for example, the `filmography` field that comes back from the IMDB API when searching a person ([example](https://www.theimdbapi.org/api/find/person?name=jim+carrey)) is an object with keys that refer to the type of appearance that person made. Since we don’t have a master list of all available positions someone can hold, we aren’t able to define the schema with expected object keys. | ||
|
||
Instead, we need to transform a response like this: | ||
|
||
```json | ||
{ | ||
"filmography": { | ||
"actor": [ | ||
{ | ||
"imdb_id": "tt1234567", | ||
"...": "..." | ||
} | ||
], | ||
"director": [ | ||
{ | ||
"imdb_id": "tt7654321", | ||
"...": "..." | ||
} | ||
] | ||
} | ||
} | ||
``` | ||
|
||
Into something more like this: | ||
|
||
```json | ||
{ | ||
"filmography": [ | ||
{ | ||
"position": "actor", | ||
"imdb_id": "tt1234567", | ||
"...": "..." | ||
}, | ||
{ | ||
"position": "director", | ||
"imdb_id": "tt7654321", | ||
"...": "..." | ||
} | ||
] | ||
} | ||
``` | ||
|
||
This way, we can create a predictable data shape for our schema, despite not knowing exactly what the value of `position` will be. | ||
|
||
The resolver we end up writing to account for this might look a little intimidating at first, especially if you’re not familiar with [array methods](https://mzl.la/2yX77TK) and/or [ES2015+ syntax](https://git.io/vdNeC), but let’s take a look, then break down what’s happening. | ||
|
||
```js | ||
IMDB_Person: { | ||
// Convert the filmography object into an array for filtering/typing. | ||
filmography: ({ filmography }, { filter = 'all' }) => | ||
Object.keys(filmography) | ||
.reduce( | ||
(works, position) => | ||
works.concat( | ||
filmography[position].map(work => ({ | ||
position, | ||
...work, | ||
})), | ||
), | ||
[], | ||
) | ||
.filter(work => filter === 'all' || work.position === filter), | ||
}, | ||
``` | ||
|
||
Let’s walk through this function step-by-step: | ||
|
||
1. First, we use [`Object.keys()`](https://mzl.la/2hWjc0P) to get an array of | ||
the `filmography` object‘s keys (e.g. `['actor', 'director']`) | ||
2. Next, we use [`.reduce()`](https://mzl.la/2xe0nPv) to transform our array | ||
of key names into an array of actual filmography objects. The `works` argument contains the entries we’ve added so far (this starts as an empty array), and `position` is the current key (e.g. `actor`) | ||
3. In the reducer, we use [`.concat()`](https://mzl.la/2gxku5y) to combine the | ||
`works` array with a new array containing filmography objects | ||
4. The filmography objects are created by accessing the current position’s | ||
array (e.g. if `position = 'actor'`, then `filmography[position] === filmography.actor`) | ||
5. Using [`.map()`](https://mzl.la/2ipT3v7), we loop through the position’s | ||
array of works and return a new object, which is created by adding the position (e.g. `position: 'actor'`), then adding all the other fields from the original object with the spread operator | ||
6. After we have the full array of works, we can apply an optional filter to | ||
the array (e.g. the query can be `filmography(filter: 'actor')`) using the `.filter()` method |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
--- | ||
title: "Tutorial: Development Modes" | ||
weight: 60 | ||
--- | ||
|
||
At this point, we have our [connector]({{ site.github.url }}/data-source/tutorial-connector), [model]({{ site.github.url }}/data-source/tutorial-model), [schema]({{ site.github.url }}/data-source/tutorial-schema), and [resolvers]({{ site.github.url }}/data-source/tutorial-resolvers) built and ready to go. All that’s left to do is fire up our data source in mock and live mode to make sure it’s working. | ||
|
||
## Table of Contents | ||
{:.no_toc} | ||
|
||
- [Initial Data Source Setup]({{ site.github.url }}/data-source/tutorial-setup) | ||
- [Create a Connector]({{ site.github.url }}/data-source/tutorial-connector) | ||
- [Create a Model]({{ site.github.url }}/data-source/tutorial-model) | ||
- [Write a GraphQL Schema]({{ site.github.url }}/data-source/tutorial-schema) | ||
- [Write Resolvers]({{ site.github.url }}/data-source/tutorial-resolvers) | ||
- **> [Use Development Modes]({{ site.github.url }}/data-source/tutorial-dev)** | ||
|
||
## In This Section | ||
{:.no_toc} | ||
|
||
- | ||
{:toc} | ||
|
||
## Run the Data Source in Mock Data Mode | ||
|
||
To test the data source with mock data, open your terminal and run the following command: | ||
|
||
```bash | ||
yarn mock-data | ||
``` | ||
|
||
Then open `http://localhost:8080/graphiql` in your browser and run a test query. You’ll see the GraphiQL user interface, and you can make a test query like this: | ||
|
||
```graphql | ||
query searchMoviesByTitle($options: IMDB_MovieSearchInput) { | ||
searchMoviesByTitle(options: $options) { | ||
imdb_id | ||
title | ||
genre | ||
year | ||
} | ||
} | ||
``` | ||
|
||
The result will be mock data that looks something like this: | ||
|
||
{% include figure.html | ||
src="/assets/img/data-source-mock-data.png" | ||
alt="A GrAMPS data source running in mock data mode" | ||
caption="A GrAMPS data source running in mock data mode" | ||
%} | ||
|
||
## Run the Data Source in Live Data Mode | ||
|
||
To use live data, stop the server (`control` + `C`), then run this command: | ||
|
||
```bash | ||
yarn live-data | ||
``` | ||
|
||
Then go back to `http://localhost:8080/graphiql` and run the same command we used with the mock data: | ||
|
||
```graphql | ||
query searchMoviesByTitle($options: IMDB_MovieSearchInput) { | ||
searchMoviesByTitle(options: $options) { | ||
imdb_id | ||
title | ||
genre | ||
year | ||
} | ||
} | ||
``` | ||
|
||
This time you’ll get back a response with live data! | ||
|
||
> **NOTE:** Unfortunately, it turns out the IMDB API is pretty unreliable and | ||
> frequently goes down. It’s | ||
> [on our todo list](https://github.com/gramps-graphql/gramps-express/issues/31) | ||
> to update this tutorial with a more reliable data source. (Want to contribute? | ||
> We’d love to have you participate!) | ||
## Further Reading | ||
|
||
- [Review the full source code of the GrAMPS data source for the IMBDAPI](https://github.com/gramps-graphql/data-source-imdbapi) | ||
- [Learn more about testing GrAMPS data sources]({{ site.github.url}}/data-source/testing) | ||
- [Learn how to publish GrAMPS data sources]({{ site.github.url}}/data-source/publishing) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.