-
Notifications
You must be signed in to change notification settings - Fork 2
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
Showing
21 changed files
with
1,466 additions
and
50 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,4 @@ | ||
/coverage | ||
/dist | ||
/node_modules | ||
npm-debug.log* |
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,27 @@ | ||
## Prerequisites | ||
|
||
[Node.js](http://nodejs.org/) >= v4 must be installed. | ||
|
||
## Installation | ||
|
||
- Running `npm install` in the app's root directory will install everything you need for development. | ||
|
||
## Development Server | ||
|
||
- `npm start` will run the app's development server at [http://localhost:3000](http://localhost:3000) with hot module reloading. | ||
|
||
## Running Tests | ||
|
||
- `npm test` will run the tests once. | ||
|
||
- `npm run test:coverage` will run the tests and produce a coverage report in `coverage/`. | ||
|
||
- `npm run test:watch` will run the tests on every change. | ||
|
||
## Building | ||
|
||
- `npm run build` creates a production build by default. | ||
|
||
To create a development build, set the `NODE_ENV` environment variable to `development` while running this command. | ||
|
||
- `npm run clean` will delete built resources. |
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,103 @@ | ||
# IIIF Graphql | ||
|
||
IIIF specification through GraphQL over rest. Thats the goal, this is the start. It is a heavy client-side implementation built on top of IIIF Redux, which acts as safe-access and a unified API to both IIIF Presentation 2 and Presentation 3 resources. | ||
|
||
The goal of this library will be to offer both a server implementation and client side implementation. The server implementation will have the option to be hooked up to a non-IIIF data source through a layer over the resolver API for institutions collections. It will also feature an out of the box proxy to content hosted in the IIIF space so you can host your own IIIF GraphQL instance that can query any IIIF content, composed together for building your own applications and tools. | ||
|
||
The client implementation will get better with time, but the main focus is building a single implementation that can work both on the server and on the client. | ||
|
||
## The why | ||
|
||
At the moment the IIIF space is full of duplicate code, multiple solutions to the same problem and lots of already solved problems that are hard to discover. This implementation of GraphQL is intended to become a place to colabourate on solving common problems parsing IIIF data and building a library of GraphQL resolvers and custom queries that can be used quickly in small to large projects. | ||
|
||
## The what | ||
|
||
Heres the dream query: | ||
|
||
```graphql | ||
query getManifest($id: String, $q: String!) { | ||
manifest(id: $id) { | ||
id | ||
label | ||
metadata { | ||
label | ||
value | ||
} | ||
search(query: $q) { | ||
results { | ||
canvasId | ||
contentAsText | ||
selector { | ||
x | ||
y | ||
width | ||
height | ||
} | ||
} | ||
} | ||
canvases { | ||
id | ||
label | ||
thumbnail: thumbnailAtSize(size: 200) { | ||
src | ||
height | ||
width | ||
} | ||
tiledImages { | ||
height | ||
width | ||
tiles | ||
scaleFactor | ||
} | ||
} | ||
} | ||
} | ||
``` | ||
|
||
If you are unfamiliar with GraphQL, the key concept is that they query language descibes a JSON format that you want returned. You can read this query and know what the fields will be and so can predicably start using these fields. | ||
|
||
Reading through this query, you can see we are grabbing some basic properties like `id` and `label` from the manifest. We are also requesting the metadata pairs. This is always an array, so each item in the array will match the format `{label: "str", value: "str"}`. Translation is done automatically using a global context (to be documented). | ||
|
||
The next section is where the power of GraphQL starts coming through, theres a search block that accepts `query` just like the IIIF specification. Behind the scenes this will be doing many things: | ||
|
||
- Checking if the resource has a search service | ||
- Fetching the service | ||
- Constructing and executing the search | ||
- Formatting and parsing that back in your desired format | ||
|
||
In addition, because this is using IIIF Redux under the hood, any references (such as targets) in the search results or any request are maintained as logical links. IIIF Redux maintains that graph and makes it query-able here. | ||
|
||
The next section `canvases` is dropping deeper into the graph of the manifest. You can go as deep as you want or need to here. You can see another non-IIIF property in the query `thumbnailAtSize` being assigned to the `thumbnail` property. This is another theoretical extension where some derived data is being reduced into a handful of fields. Under the hood this could be: | ||
|
||
- Finding a thumbnail service | ||
- Finding a thumbnail static image, checking its height/width against your requested height | ||
- Looking for an image service in the annotations | ||
|
||
In the end its goal is to find the best thumbnail given the criteria. This is immediately available in the query language to be reused once created. It can be improved. | ||
|
||
## The how | ||
|
||
Slowly. This is the start of this project that I am working on in my down-time. Currently with IIIF Redux you can use functional composition to create a bunch of queries for IIIF content, but the interface to that is low-level but fail-safe. The goal of IIIF-GraphQL is to be both fail-safe and nice to work with. | ||
|
||
GraphQL and apollo, the library that this is using, has integrations with every Frontend framework under the sun. There is no lock-in with this library, this broadens the developer interest and collabouration. | ||
|
||
## Roadmap | ||
|
||
The road map is not in any particular order. The first goal is a base for collabouration. The second goal is exploring some of the challenges in IIIF-space using GraphQL. | ||
|
||
### v1.0 | ||
|
||
- Fully IIIF compliant GraphQL type definition | ||
- Full compatibility with Presentation 2 over a presentation 3 query interface | ||
- Extension model to start adding derived fields to queries | ||
- v1.0 IIIF explorer using GraphQL | ||
- Cookbook of GraphQL queries for common UI interfaces | ||
|
||
### v1.x | ||
|
||
- W3C Annotation compliant GraphQL type definition | ||
- IIIF Image 2.x compliant GraphQL type definition | ||
- IIIF Activity stream compliant GraphQL type definition | ||
- IIIF Discovery integration in GraphQL server (invalidating caches) | ||
- IIIF Authentication compliant GraphQL type defintion | ||
- Authentication extensions |
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,3 @@ | ||
module.exports = { | ||
type: 'react-app', | ||
}; |
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,32 @@ | ||
{ | ||
"name": "iiif-graphql", | ||
"version": "1.0.0", | ||
"description": "Describe iiif-graphql here", | ||
"private": true, | ||
"scripts": { | ||
"build": "nwb build-react-app", | ||
"clean": "nwb clean-app", | ||
"start": "nwb serve-react-app", | ||
"test": "echo \"No tests yet\"" | ||
}, | ||
"dependencies": { | ||
"iiif-redux": "1.0.0", | ||
"apollo-cache-inmemory": "^1.3.5", | ||
"apollo-client": "^2.4.2", | ||
"apollo-link": "^1.2.3", | ||
"apollo-link-schema": "^1.1.1", | ||
"apollo-link-set-context": "^0.5.4", | ||
"graphql": "^14.0.2", | ||
"graphql-tag": "^2.10.0", | ||
"graphql-tools": "^4.0.1", | ||
"react": "^16.5.2", | ||
"react-apollo": "^2.2.4", | ||
"react-dom": "^16.5.2" | ||
}, | ||
"devDependencies": { | ||
"nwb": "0.19.x" | ||
}, | ||
"author": "", | ||
"license": "MIT", | ||
"repository": "" | ||
} |
Empty file.
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,48 @@ | ||
.App { | ||
height: 100%; | ||
min-height: 400px; | ||
text-align: center; | ||
display: flex; | ||
flex-direction: column; | ||
justify-content: stretch; | ||
} | ||
|
||
.App-flex { | ||
flex: 1; | ||
display: flex; | ||
align-items: center; | ||
justify-content: center; | ||
} | ||
|
||
.App-heading { | ||
background-color: #222; | ||
color: #f8f8f8; | ||
font-size: 6vh; | ||
box-shadow: 0px 4px 4vh 4px rgba(34,34,34,0.9); | ||
z-index: 2; | ||
} | ||
|
||
.App-react { | ||
color: #00d8ff; | ||
text-decoration: overline underline; | ||
} | ||
|
||
.App-logo { | ||
max-height: 30vh; | ||
max-width: 30vh; | ||
} | ||
|
||
.App-instructions { | ||
background-color: #f8f8f8; | ||
color: #222; | ||
font-size: 3vh; | ||
line-height: 1.5; | ||
padding: 0 1em; | ||
} | ||
|
||
.App-instructions code { | ||
background-color: #222; | ||
color: #00d8ff; | ||
padding: .2em .3em; | ||
border-radius: .2em; | ||
} |
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,30 @@ | ||
import './App.css'; | ||
|
||
import React, { Component } from 'react'; | ||
import { ApolloProvider } from 'react-apollo'; | ||
import HelloWorld from './components/HelloWorld'; | ||
|
||
import client from './graphql/client'; | ||
|
||
class App extends Component { | ||
state = { | ||
client: null, | ||
}; | ||
|
||
async componentWillMount() { | ||
this.setState({ client: await client }); | ||
} | ||
|
||
render() { | ||
if (!this.state.client) { | ||
return 'Loading ...'; | ||
} | ||
return ( | ||
<ApolloProvider client={this.state.client}> | ||
<HelloWorld /> | ||
</ApolloProvider> | ||
); | ||
} | ||
} | ||
|
||
export default App; |
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,54 @@ | ||
import React, { Component } from 'react'; | ||
import { Query } from 'react-apollo'; | ||
import gql from 'graphql-tag'; | ||
|
||
const queryText = gql` | ||
query { | ||
getCollection( | ||
collectionId: "https://view.nls.uk/collections/7446/74466699.json" | ||
) { | ||
id | ||
type | ||
label | ||
metadata { | ||
label | ||
value | ||
} | ||
} | ||
getManifest( | ||
manifestId: "https://wellcomelibrary.org/iiif/b20432033/manifest" | ||
) { | ||
id | ||
type | ||
label | ||
metadata { | ||
label | ||
value | ||
} | ||
} | ||
getTest { | ||
label | ||
} | ||
} | ||
`; | ||
|
||
class HelloWorld extends Component { | ||
render() { | ||
return ( | ||
<Query query={queryText}> | ||
{({ data, loading, error }) => { | ||
if (loading) { | ||
return <h2> Loading </h2>; | ||
} | ||
if (error) { | ||
return <h2> {JSON.stringify(error)} </h2>; | ||
} | ||
|
||
return <pre>{JSON.stringify(data, null, 2)}</pre>; | ||
}} | ||
</Query> | ||
); | ||
} | ||
} | ||
|
||
export default HelloWorld; |
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,22 @@ | ||
import { SchemaLink } from 'apollo-link-schema'; | ||
import { InMemoryCache } from 'apollo-cache-inmemory'; | ||
import SetContextLink from 'apollo-link-set-context'; | ||
import { ApolloLink } from 'apollo-link'; | ||
import { createStore } from 'iiif-redux'; | ||
import ApolloClient from 'apollo-client'; | ||
import schema from './schema'; | ||
|
||
const store = createStore(); | ||
|
||
// const link = ApolloLink.from([ | ||
// consoleLink, | ||
// TestLink, | ||
// new SchemaLink({ schema, context: { store } }), | ||
// ]); | ||
|
||
const client = new ApolloClient({ | ||
link: new SchemaLink({ schema, context: { store } }), | ||
cache: new InMemoryCache(), | ||
}); | ||
|
||
export default client; |
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,35 @@ | ||
import { SchemaDirectiveVisitor } from 'graphql-tools'; | ||
import { defaultFieldResolver } from 'graphql'; | ||
|
||
/** | ||
* This is non functional. | ||
*/ | ||
function translate(str, path, locale) { | ||
if (!str) { | ||
return ''; | ||
} | ||
if (typeof str === 'string') { | ||
return str; | ||
} | ||
if (Array.isArray(str)) { | ||
return str && str[0] ? str[0]['@value'] || '' : ''; | ||
} | ||
return str[Object.keys(str)[0]][0] || ''; | ||
} | ||
|
||
class IntlDirective extends SchemaDirectiveVisitor { | ||
visitFieldDefinition(field, details) { | ||
const { resolve = defaultFieldResolver } = field; | ||
field.resolve = async function(...args) { | ||
const context = args[2]; | ||
const defaultText = await resolve.apply(this, args); | ||
// In this example, path would be ["Query", "greeting"]: | ||
const path = [details.objectType.name, field.name]; | ||
return translate(defaultText, path, context.locale); | ||
}; | ||
} | ||
} | ||
|
||
export default { | ||
intl: IntlDirective, | ||
}; |
Oops, something went wrong.