Skip to content

Commit

Permalink
MTH-250 add sample extension as an exmaple of adding custom entity ty…
Browse files Browse the repository at this point in the history
…pe, add Docs
  • Loading branch information
yuriboyko committed Sep 27, 2018
1 parent 18ba05c commit 6d969a9
Show file tree
Hide file tree
Showing 7 changed files with 218 additions and 5 deletions.
11 changes: 6 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Vue Storefront - headless PWA for eCommerce
<a href="https://travis-ci.org/DivanteLtd/vue-storefront"><img src="https://travis-ci.org/DivanteLtd/vue-storefront.svg?branch=master" alt="build:passed"></a>
![version](https://img.shields.io/badge/node-v8.x-blue.svg)
![Branch stable](https://img.shields.io/badge/stable%20branch-master-blue.svg)
# Vue Storefront - headless PWA for eCommerce
<a href="https://travis-ci.org/DivanteLtd/vue-storefront"><img src="https://travis-ci.org/DivanteLtd/vue-storefront.svg?branch=master" alt="build:passed"></a>
![version](https://img.shields.io/badge/node-v8.x-blue.svg)
![Branch stable](https://img.shields.io/badge/stable%20branch-master-blue.svg)
![Branch Develop](https://img.shields.io/badge/dev%20branch-develop-blue.svg)
<a href="https://join.slack.com/t/vuestorefront/shared_invite/enQtMzA4MTM2NTE5NjM2LTI1M2RmOWIyOTk0MzFlMDU3YzJlYzcyYzNiNjUyZWJiMTZjZjc3MjRlYmE5ZWQ1YWRhNTQyM2ZjN2ZkMzZlNTg">![Branch Develop](https://img.shields.io/badge/community%20chat-slack-FF1493.svg)</a>

Expand Down Expand Up @@ -89,7 +89,7 @@ Try out our open demo and if you like it **first give us some star on Github ★
<a href="https://demo-magento1.vuestorefront.io">
demo-magento1.vuestorefront.io
</a>
</td>
</td>
<td align="center" valign="middle">
<a href="https://demo-magento-checkout.vuestorefront.io">
demo-magento-checkout.vuestorefront.io
Expand Down Expand Up @@ -141,6 +141,7 @@ You can find some tutorials and explainations on our [YouTube channel](https://w
* [Vue Storefront modules](https://github.com/DivanteLtd/vue-storefront/blob/master/doc/api-modules/about-modules.md)
* [TypeScript Action Plan](https://github.com/DivanteLtd/vue-storefront/blob/master/doc/TypeScript%20Action%20Plan.md)
* [GraphQL Action Plan](https://github.com/DivanteLtd/vue-storefront/blob/develop/doc/GraphQL%20Action%20Plan.md)
* [Entity Types](https://github.com/DivanteLtd/vue-storefront/blob/develop/doc/Entity%20Types.md)
* [SSR Cache](https://github.com/DivanteLtd/vue-storefront/blob/develop/doc/SSR%20Cache.md)

### Vue Storefront core and themes
Expand Down
98 changes: 98 additions & 0 deletions doc/Entity Types.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
## Data Entity Types

Vue-storefront uses multuiple data entity typos to cover whole scope of storefront
Default entity types are
- Product
- Category
- Attribute
- Taxrule

Using if these entity types was hardcoded and there were no ability to easy use another custom entity type required for customisation
Now Vue-storefront has a new logic to work with entityes in the data fetching prospective. It uses Entity Types.

Each search adapter should register entity type to cover search feature. Default API and new Graphql search adapters are updated to register all required exsiting entity types. But develper is able alss inject custom entittypes what allow them to work with some other custom entity type Data (eg get list of offline stores or something else)

To use it internal graphql server should be updated with adding correspond resolver for new entity type. Or some other external graphql server what already have implemeted resolver for this entity type can be used

To register such entity type use searchAdapter.registerEntityTypeByQuery method like as on an example below


```js
const factory = new SearchAdapterFactory()
let searchAdapter = factory.getSearchAdapter('graphql')
searchAdapter.registerEntityTypeByQuery('testentity', {
url: 'http://localhost:8080/graphql/',
query: require('./queries/testentity.gql'),
queryProcessor: (query) => {
// function that can modify the query each time before it's being executed
return query
},
resultPorcessor: (resp, start, size) => {
if (resp === null) {
throw new Error('Invalid graphQl result - null not exepcted')
}
if (resp.hasOwnProperty('data')) {
return processESResponseType(resp.data.testentity, start, size)
} else {
if (resp.error) {
throw new Error(JSON.stringify(resp.error))
} else {
throw new Error('Unknown error with graphQl result in resultPorcessor for entity type \'category\'')
}
}
}
})

```

Sample extension 'sample-custom-entity-graphql' was added to illustrate how it can be used. It injects custom entity type 'testentity'
and set custom graphql server url( it is the same as a default api host in the example just because resolver for this 'testentity' was added there for testing. But please notice it was removed there)

To test sample extension with resolver you can add graphql schema file and reolver file in the separate 'src/graphql/elastcisearch/testentity' folder in the Vue-Storefront-Api

'schema.graphqls' file:
```graphql
type Query {
testentity(filter: TestInput): ESResponse
}
input TestInput @doc(description: "TaxRuleInput specifies the tax rules information to search") {
id: FilterTypeInput @doc(description: "An ID that uniquely identifies the tax rule")
code: FilterTypeInput @doc(description: "The unique identifier for an tax rule. This value should be in lowercase letters without spaces.")
priority: FilterTypeInput @doc(description: "Priority of the tax rule")
position: FilterTypeInput @doc(description: "Position of the tax rule")
customer_tax_class_ids: FilterTypeInput @doc(description: "Cunstomer tax class ids of the tax rule")
product_tax_class_ids: FilterTypeInput @doc(description: "Products tax class ids of the tax rule")
tax_rate_ids: FilterTypeInput @doc(description: "Tax rates ids of the tax rule")
calculate_subtotal: FilterTypeInput @doc(description: "Calculating subtotals of the tax rule")
rates: FilterTypeInput @doc(description: "Rates of the tax rule")
}
```

and resolver file 'resolver.js' is

```js
import config from 'config';
import client from '../client';
import { buildQuery } from '../queryBuilder';

async function testentity(filter) {
let query = buildQuery({ filter, pageSize: 150, type: 'taxrule' });

const response = await client.search({
index: config.elasticsearch.indices[0],
type: config.elasticsearch.indexTypes[4],
body: query
});

return response;
}

const resolver = {
Query: {
testentity: (_, { filter }) => testentity(filter)
}
};

export default resolver;

```
71 changes: 71 additions & 0 deletions src/extensions/sample-custom-entity-graphql/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import extensionStore from './store'
import extensionRoutes from './router'
import SearchAdapterFactory from 'core/store/lib/search/adapter/factory'
import {processESResponseType} from 'core/store/lib/search/adapter/graphql/processor/processType'
import {currentStoreView} from 'core/store/lib/multistore'
import SearchQuery from 'core/store/lib/search/searchQuery'

const EXTENSION_KEY = 'sample-custom-entity-graphql-extension'
const TEST_ENTITY_TYPE = 'testentity'

export default function (app, router, store, config) {
router.addRoutes(extensionRoutes) // add custom routes
store.registerModule(EXTENSION_KEY, extensionStore) // add custom store

app.$on('application-after-init', () => {
console.debug('Example of custom entity graphql extension')

// load Search adapter factory to handle graphql search adapter
const factory = new SearchAdapterFactory()

// create graphQl searchAdapter
let searchAdapter = factory.getSearchAdapter('graphql')

// register custom entity type using registerEntityTypeByQuery
// differnt graphql servers cold be used for different entity types
// resolver for testentity should be implemented on the graphql server provided
searchAdapter.registerEntityTypeByQuery(TEST_ENTITY_TYPE, {
url: 'http://localhost:8080/graphql/',
query: require('./queries/testentity.gql'),
queryProcessor: (query) => {
// function that can modify the query each time before it's being executed
return query
},
resultPorcessor: (resp, start, size) => {
if (resp === null) {
throw new Error('Invalid graphQl result - null not exepcted')
}
if (resp.hasOwnProperty('data')) {
return processESResponseType(resp.data.testentity, start, size)
} else {
if (resp.error) {
throw new Error(JSON.stringify(resp.error))
} else {
throw new Error('Unknown error with graphQl result in resultPorcessor for entity type \'category\'')
}
}
}
})

const storeView = currentStoreView()

// create an empty SearchQuery to get all data for new custom entity
const searchQuery = new SearchQuery()

// prepare a Request object
const Request = {
store: storeView.storeCode, // TODO: add grouped product and bundled product support
type: TEST_ENTITY_TYPE,
searchQuery: searchQuery,
sort: ''
}

// apply test search
searchAdapter.search(Request).then((resp) => { // we're always trying to populate cache - when online
const res = searchAdapter.entities[Request.type].resultPorcessor(resp, 0, 200)
console.log('Testentity response: ', res)
})
})

return { EXTENSION_KEY, extensionRoutes, extensionStore }
}
13 changes: 13 additions & 0 deletions src/extensions/sample-custom-entity-graphql/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"name": "@vue-storefront/extension-sample-custom-entity-graphql",
"version": "1.3.0",
"description": "Extension template for Vue Storefront",
"license": "MIT",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"publishConfig": {
"access": "public"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
query testentity ($filter: TestInput) {
testentity(
filter: $filter
)
{
hits
}
}
2 changes: 2 additions & 0 deletions src/extensions/sample-custom-entity-graphql/router.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export default [
]
20 changes: 20 additions & 0 deletions src/extensions/sample-custom-entity-graphql/store.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
const state = {
}

const getters = {
}

// actions
const actions = {
}

// mutations
const mutations = {
}

export default {
state,
getters,
actions,
mutations
}

0 comments on commit 6d969a9

Please sign in to comment.