Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add subscriptions #280

Merged
merged 27 commits into from
Dec 23, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
8d8c1e4
Remove VueApollo (not used)
Oct 6, 2019
47c10a8
Add apollo-link-ws and apollo-utilities
Oct 6, 2019
9606c4b
Fix unit tests, using polyfill for fetch, and working around WebSocke…
Oct 7, 2019
6e90ef9
Add docs and remove code not used for tests any longer
Oct 7, 2019
bd8426c
Remove Apollo from application plugins
Oct 7, 2019
3097610
Simplify URL creation
Oct 7, 2019
63649b6
Specify uri for ApolloClient
Oct 7, 2019
c8c669b
Add SubscriptionWorkflowService and instantiate a singleton globally …
Oct 7, 2019
f9a6a49
Use ApolloClient instead of ApolloBoost (split issue)
Oct 7, 2019
e8d8519
Update queries in views to
Oct 7, 2019
5094fa0
Removing Apollo Boost (not used anymore)
Oct 7, 2019
4b024d3
Add missing dependencies (were transitive from apollo-boost)
Oct 7, 2019
87cfad7
Add changelog
Oct 9, 2019
6ed0d93
Use a default export to trick import for different environments
kinow Oct 12, 2019
1e7f617
Fix lock file after rebase/merge
Dec 4, 2019
da4a649
Use global workflowService from graph and gscan components
Dec 4, 2019
56e44b6
Use subscription queries in graph and gscan
Dec 4, 2019
b15fa91
Await on Vue.nextTick for DOM changes in test
kinow Dec 4, 2019
ed164cf
Update test to have a workflowService in the localVue instance
kinow Dec 4, 2019
2f4f79f
Call destructor method when subscribing
kinow Dec 4, 2019
469879d
Add TODO about window.location dependency
Dec 19, 2019
a9fa099
Update Dashboard to use the new service
Dec 19, 2019
5551d5c
Remove old LiveWorkflowService and the request method from GQuery
Dec 19, 2019
c1696b2
vuex objects must be plan object as vue data
kinow Dec 19, 2019
09d16a9
Simplify the observable creation and use
kinow Dec 19, 2019
d0cac5f
Use the new ApolloClient (with two links) for the toolbar mutations
kinow Dec 19, 2019
e318e3a
Update Toolbar test to use the created apolloClient instead of vue-ap…
Dec 19, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ workflows only.
[#283](https://github.com/cylc/cylc-ui/pull/283) - Load user information
on application startup.

[#283](https://github.com/cylc/cylc-ui/pull/285) - Update Vuetify to 2.1,
[#285](https://github.com/cylc/cylc-ui/pull/285) - Update Vuetify to 2.1,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops.

along with other dependencies with updates available.

[#291](https://github.com/cylc/cylc-ui/pull/291) - Add families to Tree
Expand All @@ -38,6 +38,9 @@ dashboard workflows summary.
[#307](https://github.com/cylc/cylc-ui/pull/307) - Invoke mutations to
hold, release, and stop workflows.

[#280](https://github.com/cylc/cylc-ui/pull/280) - Add WebSockets client
for GraphQL subscriptions.

### Fixes

[#275](https://github.com/cylc/cylc-ui/pull/275) - Fix size of dashboard
Expand Down
2,656 changes: 1,478 additions & 1,178 deletions package-lock.json

Large diffs are not rendered by default.

14 changes: 10 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"test:unit": "vue-cli-service test:unit"
},
"dependencies": {
"apollo-boost": "^0.4.4",
"apollo-utilities": "^1.3.2",
"axios-fetch": "^1.1.0",
"cytoscape": "^3.9.0",
"cytoscape-cise": "^1.0.0",
Expand All @@ -37,7 +37,6 @@
"tippy.js": "^4.3.5",
"vee-validate": "^3.0.11",
"vue": "^2.6.10",
"vue-apollo": "^3.0.0-beta.30",
"vue-cytoscape": "^0.2.10",
"vue-i18n": "^8.14.1",
"vue-meta": "^2.3.1",
Expand All @@ -55,11 +54,17 @@
"@vue/cli-plugin-unit-mocha": "^3.12.0",
"@vue/cli-service": "^3.12.0",
"@vue/test-utils": "^1.0.0-beta.29",
"apollo-cache-inmemory": "^1.6.3",
"apollo-client": "^2.6.4",
"apollo-link": "^1.2.13",
"apollo-link-http": "^1.5.16",
"apollo-link-ws": "^1.0.19",
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These were dependencies of ApolloBoost, a helper that simplifies creating Apollo clients. However, when you provide a link that can handle both HTTP and WebSocket, ApolloBoost ignores it and unless you provide a URI, it will create and use an HTTP Link, simply ignoring the WebSocket.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://github.com/apollographql/apollo-client/blob/9c19c5810a8908d7034a384d06fdb76eec02b3b6/packages/apollo-boost/src/index.ts#L164

Where it shows that ApolloBoost is not compatible with WebSockets. Hence the removal, and the need to add these dependencies.

"axios": "^0.19.0",
"babel-eslint": "^10.0.3",
"chai": "^4.2.0",
"cross-env": "^6.0.3",
"eslint": "^6.5.1",
"cross-fetch": "^3.0.4",
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We were using the node-fetch for tests, but it didn't work after removing the default ApolloClient (which would prevent Apollo and its libraries of trying to validate that the fetch global existed. This polyfill provides the global fetch for tests.

"eslint-config-vuetify": "*",
"eslint-plugin-vue": "^5.2.3",
"istanbul-instrumenter-loader": "^3.0.1",
Expand All @@ -68,10 +73,11 @@
"nyc": "^14.1.1",
"sass": "^1.23.0",
"sass-loader": "^7.3.1",
"sinon": "^7.4.1",
"standard": "^14.0.2",
"sinon": "^7.5.0",
"standard": "^14.3.1",
"svgo": "^1.3.2",
"vue-cli-plugin-apollo": "^0.21.0",
"subscriptions-transport-ws": "^0.9.16",
"vue-cli-plugin-eslint-config-vuetify": "latest",
"vue-cli-plugin-vuetify": "latest",
"vue-cli-plugin-vuetify-essentials": "latest",
Expand Down
12 changes: 5 additions & 7 deletions src/components/cylc/Graph.vue
Original file line number Diff line number Diff line change
Expand Up @@ -78,12 +78,10 @@ import VueCytoscape from '@/components/core/Cytoscape.vue'
import { mixin } from '@/mixins/index'
// eslint-disable-next-line no-unused-vars
import { debounce, each, has, isEmpty, isUndefined, memoize } from 'lodash'
// eslint-disable-next-line no-unused-vars
import { workflowService } from 'workflow-service'

const QUERIES = {
root: `
{
subscription {
workflows(ids: ["WORKFLOW_ID"]) {
id
status
Expand Down Expand Up @@ -625,7 +623,7 @@ export default {
if (tippy) {
tippy.hide()
}
workflowService.unregister(this)
this.$workflowService.unregister(this)
},

mounted () {
Expand All @@ -637,7 +635,7 @@ export default {

created (cy) {
console.debug('CREATED')
workflowService.register(
this.$workflowService.register(
this,
{
activeCallback: this.setActive
Expand All @@ -653,7 +651,7 @@ export default {

methods: {
subscribe (queryName) {
const id = workflowService.subscribe(
const id = this.$workflowService.subscribe(
this,
QUERIES[queryName].replace('WORKFLOW_ID', this.workflowName)
)
Expand All @@ -666,7 +664,7 @@ export default {

unsubscribe (queryName) {
if (queryName in this.subscriptions) {
workflowService.unsubscribe(
this.$workflowService.unsubscribe(
this.subscriptions[queryName].id
)
}
Expand Down
6 changes: 3 additions & 3 deletions src/components/cylc/Toolbar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ export default {
const vm = this
if (this.isHeld) {
// release
this.$apollo.mutate({
this.$apolloClient.mutate({
mutation: RELEASE_WORKFLOW,
variables: {
workflow: this.currentWorkflow.id
Expand All @@ -156,7 +156,7 @@ export default {
})
} else {
// hold
this.$apollo.mutate({
this.$apolloClient.mutate({
mutation: HOLD_WORKFLOW,
variables: {
workflow: this.currentWorkflow.id
Expand All @@ -168,7 +168,7 @@ export default {
},
onClickStop () {
const vm = this
this.$apollo.mutate({
this.$apolloClient.mutate({
mutation: STOP_WORKFLOW,
variables: {
workflow: this.currentWorkflow.id
Expand Down
11 changes: 5 additions & 6 deletions src/components/cylc/gscan/GScan.vue
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,11 @@

<script>
import Job from '@/components/cylc/Job'
import { workflowService } from 'workflow-service'
import { getWorkflowSummary } from '@/components/cylc/gscan/index'

const QUERIES = {
root: `
{
subscription {
workflows {
id
name
Expand Down Expand Up @@ -137,7 +136,7 @@ export default {
},
created () {
this.viewID = `GScan(*): ${Math.random()}`
workflowService.register(
this.$workflowService.register(
this,
{
activeCallback: this.setActive
Expand All @@ -146,7 +145,7 @@ export default {
this.subscribe('root')
},
beforeDestroy () {
workflowService.unregister(this)
this.$workflowService.unregister(this)
},
methods: {
subscribe (queryName) {
Expand All @@ -156,7 +155,7 @@ export default {
*/
if (!(queryName in this.subscriptions)) {
this.subscriptions[queryName] =
workflowService.subscribe(
this.$workflowService.subscribe(
this,
QUERIES[queryName]
)
Expand All @@ -169,7 +168,7 @@ export default {
* @param {string} queryName - Must be in QUERIES.
*/
if (queryName in this.subscriptions) {
workflowService.unsubscribe(
this.$workflowService.unsubscribe(
this.subscriptions[queryName]
)
}
Expand Down
23 changes: 12 additions & 11 deletions src/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,23 @@ import i18n from '@/i18n'
import router from '@/router'
import store from '@/store'

// GraphQL
import VueApollo from 'vue-apollo'

// GraphQL client
import SubscriptionWorkflowService from 'workflow-service'
import { createApolloClient } from '@/utils/graphql'

const apolloClient = createApolloClient(
`${window.location.pathname}/graphql`
)
const apolloProvider = new VueApollo({
defaultClient: apolloClient
})

// Sync store with router
sync(store, router)

// TODO: revisit this and evaluate other ways to build the GraphQL URL - not safe to rely on window.location (?)
const baseUrl = `${window.location.hostname}${window.location.port ? ':' + window.location.port : ''}${window.location.pathname}`
const httpUrl = `${window.location.protocol}//${baseUrl}graphql`
const wsUrl = `ws://${baseUrl}subscriptions`
Vue.prototype.$apolloClient = createApolloClient(httpUrl, wsUrl)

// WorkflowService singleton available application-wide
const workflowService = new SubscriptionWorkflowService(Vue.prototype.$apolloClient)
Vue.prototype.$workflowService = workflowService

Vue.config.productionTip = false

/* eslint-disable no-new */
Expand All @@ -38,6 +40,5 @@ new Vue({
router,
store,
vuetify,
apolloProvider,
render: h => h(App)
}).$mount('#app')
23 changes: 0 additions & 23 deletions src/model/User.model.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,27 +9,4 @@ export default class User {
*/
this.server = server
}

/**
* Return the Hub URL for the currently logged in user. The Vue app when created
* uses a route guard to load the user information from JupyterHub REST API.
*
* This stored information contains the `server` parameter that includes the
* base URL. We use the base URL in this function to create the Hub URL. Otherwise
* we would end up with 404 errors for users that have a value set for the
* base URL (which is blank by default).
*
* @link https://github.com/cylc/cylc-ui/issues/258
* @returns {string}
*/
getHubUrl () {
const hubUrl = '/hub/home'
let baseUrl = ''
const server = this.server
const userTokenIdx = server.lastIndexOf('/user')
if (userTokenIdx > 0) {
baseUrl = server.substring(0, userTokenIdx)
}
return `${baseUrl}${hubUrl}`
}
}
4 changes: 0 additions & 4 deletions src/plugins/apollo.js

This file was deleted.

1 change: 0 additions & 1 deletion src/plugins/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import './axios'
import './layouts'
import './veevalidate'
import './apollo'
40 changes: 2 additions & 38 deletions src/services/gquery.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
import { parse } from 'graphql/language/parser'
import { print } from 'graphql/language/printer'

import { createApolloClient } from '@/utils/graphql'

import Alert from '@/model/Alert.model'
import store from '@/store/'

function gClone (query) {
/** Clone a GraphQL query.
* Why oh why isn't there a better way of doing this in JS!
Expand Down Expand Up @@ -76,11 +71,8 @@ class GQuery {
* GraphQL endpoint for a collection of views with potentially overlapping
* queries.
*/

constructor () {
this.apolloClient = createApolloClient(
`${window.location.pathname}/graphql`
)
constructor (apolloClient) {
this.apolloClient = apolloClient
this.query = null
this.subscriptions = []
this.views = []
Expand Down Expand Up @@ -162,34 +154,6 @@ class GQuery {
}

request () {
/**
* Perform a REST GraphQL request for all subscriptions.
*/
if (process.env.NODE_ENV !== 'production') {
// eslint-disable-next-line no-console
console.debug('graphql request:', this.query)
}
return this.apolloClient.query({
query: this.query,
fetchPolicy: 'no-cache'
}).then((response) => {
// commit results
store.dispatch(
'workflows/set',
response.data.workflows
)
// set all subscriptions to active
this.subscriptions
.filter(s => s.active === false)
.forEach(s => { s.active = true })
// run callback functions on the views
this.callbackActive()
}).catch((error) => {
store.dispatch(
'setAlert',
new Alert(error.message, null, 'error')
)
})
}

callbackActive () {
Expand Down
10 changes: 5 additions & 5 deletions src/services/mock/workflow.service.mock.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { checkpoint } from '@/services/mock/checkpoint.js'
import { GQuery } from '@/services/gquery'
import store from '@/store/'
import store from '@/store/index'

class MockWorkflowService extends GQuery {
/**
Expand All @@ -9,9 +9,9 @@ class MockWorkflowService extends GQuery {
*/

constructor () {
super()
super(/* enableWebSockets */ false)
// load mock data
store.dispatch('workflows/set', checkpoint.workflows)
store.dispatch('workflows/set', checkpoint.workflows).then(() => {})
}

subscribe (view, query) {
Expand All @@ -28,6 +28,6 @@ class MockWorkflowService extends GQuery {
}
}

const workflowService = new MockWorkflowService()
export { MockWorkflowService }

export { workflowService }
export default MockWorkflowService
Loading