109 Commits

Author SHA1 Message Date
Simon Garner 8ad83a4fc6 0.1.0
Build / dependencies (push) Has been cancelled
Build / lint (push) Has been cancelled
Build / build (push) Has been cancelled
Build / test (push) Has been cancelled
2021-09-08 09:17:14 +12:00
Simon Garner 5e99a3a354 0.1.0-alpha.5
Build / dependencies (push) Has been cancelled
Build / lint (push) Has been cancelled
Build / build (push) Has been cancelled
Build / test (push) Has been cancelled
2021-09-08 08:02:15 +12:00
Simon Garner cbe2c251fb Merge pull request #68 from madscience/fix/mutation-params
Enable mutation functions to accept all MutationOptions
2021-09-08 08:01:20 +12:00
Simon Garner ce94951550 Simplify overriding onError with params 2021-09-08 07:59:34 +12:00
Simon Garner f1969bc88c Enable mutation functions to accept all MutationOptions
The apollo client mutate() function accepts additional parameters (`optimisticResponse`, `update`, etc) that were not supported by the generated mutation operation functions before. Now those parameters will all be passed through.
2021-09-08 07:55:43 +12:00
Simon Garner 7be078995e 0.1.0-alpha.4
Build / dependencies (push) Has been cancelled
Build / lint (push) Has been cancelled
Build / build (push) Has been cancelled
Build / test (push) Has been cancelled
2021-09-07 07:56:22 +12:00
Simon Garner f8b9bbd403 Add missing types for throttle and debounce in query options
See https://github.com/vuejs/vue-apollo/issues/335
2021-09-07 07:55:55 +12:00
Simon Garner 656eef7bcd 0.1.0-alpha.3
Build / dependencies (push) Has been cancelled
Build / lint (push) Has been cancelled
Build / build (push) Has been cancelled
Build / test (push) Has been cancelled
2021-09-06 12:10:00 +12:00
Madbot 6326ec8211 Fix code style issues with Prettier 2021-09-06 12:03:18 +12:00
Simon Garner 512eb9328b fix type conflicts with GraphQLError.message 2021-09-06 12:03:18 +12:00
Renovate Bot 8f4c8cd88f Update dependency typescript to v4.4.2 2021-09-06 12:03:18 +12:00
Madbot 0f312478bc Fix code style issues with Prettier 2021-09-06 11:15:48 +12:00
Renovate Bot b022822a97 Update dependency prettier to v2.3.2 2021-09-06 11:15:48 +12:00
Simon Garner ee396004a5 Rename test workflow -> build 2021-09-06 10:55:30 +12:00
Simon Garner 2077acddcb Use lint action with automatic fixes in CI 2021-09-06 10:55:10 +12:00
Simon Garner e34cfec074 Reformat with prettier 2021-09-06 10:52:12 +12:00
Simon Garner 595733435b Add format script 2021-09-06 10:52:12 +12:00
Simon Garner e610d9dff0 Add prettier ignore file 2021-09-06 10:52:12 +12:00
Simon Garner f5b28b0dc0 Remove eslint-plugin-prettier 2021-09-06 10:52:12 +12:00
Renovate Bot c8f8c6c7b5 Update eslint 2021-09-06 10:52:12 +12:00
Simon Garner 6ce30d8063 Merge pull request #67 from madscience/decorator
Add SmartQuery decorator
2021-09-06 10:25:47 +12:00
Simon Garner c489613d12 Add credits to README 2021-09-06 10:15:51 +12:00
Simon Garner e835aac336 Improve grammar 2021-09-06 10:13:42 +12:00
Simon Garner a318b10e97 0.1.0-alpha.2
Test / dependencies (push) Has been cancelled
Test / lint (push) Has been cancelled
Test / build (push) Has been cancelled
Test / test (push) Has been cancelled
2021-09-06 09:46:05 +12:00
Simon Garner 1ca08f363c Type loadingKey query option as being a key of the component 2021-09-06 09:45:42 +12:00
Simon Garner 0d68177297 0.1.0-alpha.1 2021-09-05 16:25:52 +12:00
Simon Garner 47a9363624 Remove --passWithNoTests
We have a test now!
2021-09-05 16:25:52 +12:00
Simon Garner 02a95b0068 Simplify headings in README 2021-09-05 16:25:52 +12:00
Simon Garner 5f77e78dce Add usage instructions to README.md 2021-09-05 16:25:52 +12:00
Simon Garner a55c4df8ac Keep empty test directory 2021-09-05 16:25:51 +12:00
Simon Garner ffe674e179 fix lint 2021-09-05 16:25:51 +12:00
Simon Garner 5316dffc79 Don't need to worry about test/.results directory any more 2021-09-05 16:25:51 +12:00
Simon Garner 2c102ffd86 Revert "Run tsc build as part of test script"
This reverts commit b5ebe6cdf5.
2021-09-05 16:25:51 +12:00
Simon Garner 653651af9e Remove example test file 2021-09-05 16:25:51 +12:00
Simon Garner 2579221ef3 Add SmartQuery decorator tests 2021-09-05 16:25:51 +12:00
Simon Garner 1671a2a641 Add VueApolloSmartQueryOptionsFunction type 2021-09-05 16:25:51 +12:00
Simon Garner 271af35ff3 Remove vue-jest, use ts-jest only 2021-09-05 16:25:51 +12:00
Simon Garner 38efa7f5fd Run tsc build as part of test script 2021-09-05 16:25:50 +12:00
Simon Garner a184514f2c Remove jest-html-reporters 2021-09-05 16:25:50 +12:00
Simon Garner 7a79a5c1fa Add test for types and decorator 2021-09-05 16:25:50 +12:00
Simon Garner 4d55c37949 Add SmartQuery decorator 2021-09-05 16:25:50 +12:00
Simon Garner 62e5db0159 Add vue-class-component to optionalDependencies 2021-09-05 16:25:50 +12:00
Renovate Bot efe3394cc7 Update Node.js to v14.17.6 2021-09-03 17:34:00 +00:00
Renovate Bot 631f1c8836 Update eslint to v4.30.0 2021-09-02 18:53:02 +00:00
Renovate Bot b0060fa347 Update dependency graphql to v15.5.2 2021-09-02 18:52:21 +00:00
Renovate Bot 51af6f30e4 Update dependency jest to v27.1.0 2021-08-30 13:07:11 +00:00
Renovate Bot 0325a5bd2e Update eslint 2021-08-30 10:02:39 +00:00
Renovate Bot 46569adf84 Update dependency husky to v7.0.2 2021-08-28 04:50:07 +00:00
Renovate Bot 1ada9125b7 Update dependency ts-jest to v27.0.5 2021-08-20 09:49:20 +00:00
Renovate Bot 0a260bd78e Update eslint to v4.29.2 2021-08-19 19:38:49 +00:00
Renovate Bot f9415e58ba Update dependency @types/jest to v27 2021-08-16 02:10:14 +00:00
Renovate Bot 1abd6285a2 Update Node.js to v14.17.5 2021-08-14 17:47:36 +00:00
Renovate Bot 93d1f2b3fb Update eslint 2021-08-13 14:48:41 +00:00
Renovate Bot 3baa30e4b8 Update dependency lint-staged to v11.1.2 2021-08-09 12:15:20 +00:00
Renovate Bot b76d2b89f8 Update eslint 2021-08-06 05:18:24 +00:00
Simon Garner 7593629c61 0.0.4
Test / dependencies (push) Has been cancelled
Test / lint (push) Has been cancelled
Test / build (push) Has been cancelled
Test / test (push) Has been cancelled
2021-08-04 17:30:24 +12:00
Simon Garner 3fc3e664f4 Merge pull request #52 from madscience/fix/mutation-client
fix: set client from $apollo correctly in mutateWithErrorHandling
2021-08-04 17:26:42 +12:00
Simon Garner 2a24baba26 fix: export handleApolloError function from package index 2021-08-04 17:24:25 +12:00
Simon Garner 7008d8a401 fix: set client from $apollo correctly in mutateWithErrorHandling 2021-08-04 17:24:25 +12:00
Renovate Bot 75679dc539 Update Node.js to v14.17.4 2021-08-01 18:34:33 +00:00
Renovate Bot d6a4af0adf Update eslint to v4.28.5 2021-07-29 20:13:33 +00:00
Renovate Bot 1e4bb13b14 Update dependency lint-staged to v11.1.1 2021-07-27 13:37:55 +00:00
Renovate Bot 345bb7e0b6 Update dependency @vue/test-utils to v1.2.2 2021-07-25 14:28:08 +00:00
Renovate Bot fe75aaaf03 Update jest 2021-07-25 01:06:48 +00:00
Renovate Bot 4f6b27c3cc Update eslint 2021-07-22 21:01:45 +00:00
Renovate Bot 60e04d13a2 Update dependency lint-staged to v11.0.1 2021-07-16 11:43:49 +00:00
Renovate Bot a5af67e962 Update eslint to v4.28.3 2021-07-15 19:05:01 +00:00
Renovate Bot 70831ef914 Update dependency @types/jest to v26.0.24 2021-07-10 00:40:56 +00:00
Renovate Bot 3e20a074bf Update eslint 2021-07-09 12:32:51 +00:00
Renovate Bot 711ad93254 Update dependency husky to v7.0.1 2021-07-09 12:32:10 +00:00
Renovate Bot 95a4720a24 Update Node.js to v14.17.3 2021-07-08 18:02:31 +00:00
Renovate Bot 2f26bfd45a Update Node.js to v14.17.2 2021-07-04 16:20:56 +00:00
Renovate Bot a00b05b352 Update dependency husky to v7 2021-07-04 08:26:02 +00:00
Renovate Bot 5802d949e5 Update dependency jest to v27.0.6 2021-07-01 18:15:11 +00:00
Renovate Bot 860db84f51 Update eslint to v4.28.1 2021-07-01 18:14:50 +00:00
Renovate Bot 9ec02d15f0 Update dependency eslint-plugin-vue to v7.12.1 2021-06-28 14:01:58 +00:00
Renovate Bot cb7ef32616 Update dependency jest to v27.0.5 2021-06-25 13:28:38 +00:00
Renovate Bot 2e160a6f29 Update eslint 2021-06-24 19:19:52 +00:00
Renovate Bot 99f815dfbd Update dependency graphql to v15.5.1 2021-06-23 16:13:46 +00:00
Renovate Bot b10f95bd02 Update Node.js to v14.17.1 2021-06-18 15:32:09 +00:00
Renovate Bot a56f0168dd Update eslint 2021-06-17 19:30:10 +00:00
Renovate Bot 3426e2aff4 Update dependency @vue/test-utils to v1.2.1 2021-06-17 09:43:42 +00:00
Renovate Bot 8571bc04ab Update eslint 2021-06-10 18:40:50 +00:00
Renovate Bot bb4b2b875e Update dependency vue to v2.6.14 2021-06-10 14:04:35 +00:00
Renovate Bot f3382b7d76 Update dependency ts-jest to v27.0.3 2021-06-09 18:19:51 +00:00
Renovate Bot b0fc7d5506 Update jest 2021-06-06 09:33:54 +00:00
Renovate Bot 33068e663b Update dependency vue to v2.6.13 2021-06-04 18:56:10 +00:00
Renovate Bot 6ed1e9bdb1 Update eslint 2021-06-03 21:02:21 +00:00
Renovate Bot e6a80a4d88 Update eslint 2021-05-27 20:02:15 +00:00
Renovate Bot 2fe8c6b582 Update eslint 2021-05-20 20:09:53 +00:00
Renovate Bot 6bc078dbe4 Update Node.js to v14.17.0 2021-05-15 01:31:20 +00:00
Renovate Bot 7fd1b3977b Update eslint 2021-05-13 20:06:41 +00:00
Renovate Bot cce1dbd5a9 Update dependency lint-staged to v11 2021-05-10 20:12:42 +00:00
Renovate Bot 1c314402c5 Update jest 2021-05-09 17:21:25 +00:00
Renovate Bot 2dae141096 Update eslint to v4.22.1 2021-05-07 17:35:23 +00:00
Renovate Bot b58b07cf9b Update dependency jest-html-reporters to v2.1.5 2021-05-05 13:57:42 +00:00
Renovate Bot 3b15071a3f Update dependency @vue/test-utils to v1.2.0 2021-05-04 11:25:39 +00:00
Renovate Bot 770c2e3b98 Update jest 2021-04-29 13:56:36 +00:00
Renovate Bot 6f33c7d785 Update dependency np to v7.5.0 2021-04-28 11:07:42 +00:00
Renovate Bot ced75f75ae Update dependency eslint to v7.25.0 2021-04-27 01:31:57 +00:00
Renovate Bot 3815b97099 Update jest 2021-04-21 14:32:10 +00:00
Renovate Bot 5626dc129b Update eslint 2021-04-18 04:16:24 +00:00
Renovate Bot 7cacc37645 Update dependency @vue/test-utils to v1.1.4 2021-04-15 04:50:45 +00:00
Renovate Bot 9f15bc22c5 Update dependency eslint-plugin-jest to v24.3.5 2021-04-14 02:23:01 +00:00
Simon Garner 16ca4b711b 0.0.3
Test / dependencies (push) Has been cancelled
Test / lint (push) Has been cancelled
Test / build (push) Has been cancelled
Test / test (push) Has been cancelled
2021-04-11 13:09:39 +12:00
Simon Garner e4a814126b Add example handleApolloError function 2021-04-11 13:09:03 +12:00
Simon Garner 4f6ddc91ba Change AbstractApolloErrorProcessor to concrete ApolloErrorProcessor 2021-04-11 13:05:29 +12:00
Renovate Bot 36ddad8c67 Update dependency typescript to v4.2.4 2021-04-11 00:35:03 +00:00
Renovate Bot 301e22b24e Update Node.js to v14.16.1 2021-04-10 22:28:56 +00:00
22 changed files with 3477 additions and 3312 deletions
+1 -6
View File
@@ -7,12 +7,7 @@ module.exports = {
parserOptions: {
parser: '@typescript-eslint/parser',
},
extends: [
'plugin:@typescript-eslint/recommended',
'plugin:vue/essential',
'prettier/vue',
'plugin:prettier/recommended',
],
extends: ['plugin:@typescript-eslint/recommended', 'plugin:vue/essential', 'prettier'],
plugins: ['@typescript-eslint', 'vue'],
// add your custom rules here
rules: {
@@ -1,4 +1,4 @@
name: Test
name: Build
on: [push]
@@ -52,6 +52,8 @@ jobs:
steps:
# Setup
- uses: actions/checkout@v2
with:
token: ${{ secrets.MADSCI_BUILD_USER_TOKEN }} # allows commit of any fixes to trigger a new workflow run
- name: Use Node.js 12.x
uses: actions/setup-node@v2
with:
@@ -64,9 +66,17 @@ jobs:
name: node_modules.tar.zstd
- name: Unarchive node_modules
run: tar --use-compress-program "zstd -d --long=31" -xf node_modules.tar.zstd
# ESLint
- name: Lint source code
run: npm run lint
# Lint
- name: Run linters
uses: wearerequired/lint-action@v1
with:
prettier: true
eslint: true
eslint_args: "--ext '.ts,.js' --ignore-path '.gitignore' --ignore-pattern '.github/*'"
continue_on_error: false
auto_fix: true
git_name: Madbot
git_email: 64821814+madsci-bot@users.noreply.github.com
#
# build job
+1 -1
View File
@@ -1 +1 @@
14.16.0
14.17.6
+13
View File
@@ -0,0 +1,13 @@
# Ignore workflows
.github
# Node modules
node_modules
# Test results
test/.results
test/.coverage
# IDE
.idea
.vscode
+154 -1
View File
@@ -1,3 +1,156 @@
# vue-apollo-smart-ops
Create TypeScript-typed operation functions for your Vue Apollo queries and mutations.
Creates TypeScript-typed operation functions for GraphQL queries and mutations compatible with
[Vue Apollo](https://apollo.vuejs.org/).
This library is intended to be used together with the
[`typescript-vue-apollo-smart-ops` plugin](https://www.graphql-code-generator.com/docs/plugins/typescript-vue-apollo-smart-ops)
for [GraphQL Code Generator](https://www.graphql-code-generator.com/), but it may also be useful standalone.
## Installation
```shell
npm install --save vue-apollo-smart-ops
```
## Smart Query Usage
### `createSmartQueryOptionsFunction(query, onError?)`
Returns a generated function which returns a [Vue Apollo Smart Query options object](https://apollo.vuejs.org/api/smart-query.html#options)
for the given query when called.
> ⚠️ Note: The returned function is not meant to execute the query itself. It is only used to configure a Vue Apollo
> Smart Query. The responsibility for executing the query lies with Vue Apollo.
The returned function accepts an options object as its first argument, allowing variables and other parameters to be
customised in runtime usage. Compared with creating an options object directly, the advantage here is that the options
accepted by the generated function are fully type-checked based on the query definition - and without needing to pass
type arguments at every usage.
Using the [`@graphql-codegen/typescript-vue-apollo-smart-ops` plugin](https://www.graphql-code-generator.com/docs/plugins/typescript-vue-apollo-smart-ops)
you can automatically generate options functions for all the query operations in your project.
The following example manually creates an options function and assigns it to the variable `useTodoListQuery`:
```typescript
const useTodoListQuery = createSmartQueryOptionsFunction<TodosQuery, TodosQueryVariables>(gql`
query Todos($skip: Int, $limit: Int) {
todos(skip: $skip, limit: $limit) {
id
title
}
}
`);
```
This function can subsequently be called inside the `apollo` options of a Vue component to configure a Smart Query. The
following example adds a `todos` Smart Query to a component, in Vue class component style:
```typescript
@Component<TodoList>({
apollo: {
todos: useTodoListQuery<TodoList>({
skip() {
return this.foo === 'bar';
},
variables() {
return {
limit: 10,
skip: 0,
};
},
}),
},
})
class TodoList extends Vue {
todos: Todo[] | null = null;
get foo(): string {
return 'bar';
}
}
```
### `@SmartQuery(options)`
The `@SmartQuery()` decorator works with your generated options functions to enable the declaration of Smart Queries
within the body of a Vue class component, instead of in the component options. This helps to keep the data property and
its query options together in one place.
The following example is equivalent to the previous example but using the decorated syntax style:
```typescript
@Component
class TodoList extends Vue {
@SmartQuery(
useTodoListQuery<TodoList>({
skip() {
return this.foo === 'bar';
},
variables() {
return this.vars;
},
}),
)
todos: Todo[] | null = null;
get foo(): string {
return 'bar';
}
}
```
## Mutations Usage
### `createMutationFunction(mutation, onError?)`
Returns a generated function which executes a mutation and returns the result when called.
The returned function requires a Vue app instance as its first argument (from which the `$apollo` client will be
accessed), and accepts an options object as its second argument, allowing variables and other parameters to be
customised in runtime usage. Compared with executing a mutation using the Vue Apollo client directly, the advantage here
is that the options accepted by the generated function are fully type-checked based on the operation definition - and
without needing to pass type arguments at every usage.
Using the [`@graphql-codegen/typescript-vue-apollo-smart-ops` plugin](https://www.graphql-code-generator.com/docs/plugins/typescript-vue-apollo-smart-ops)
you can automatically generate mutation functions for all the mutation operations in your project.
The following example manually creates a mutation function and assigns it to the variable `todoCreateMutation`:
```typescript
const todoCreateMutation = createMutationFunction<TodoCreateMutation, TodoCreateMutationVariables>(gql`
mutation TodoCreate($input: TodoInput!) {
todoCreate(input: $input) {
todo {
id
title
}
}
}
`);
```
The following example demonstrates how to call this mutation from a method on a component:
```typescript
@Component
class TodoList extends Vue {
async onClickCreateTodoButton(): Promise<void> {
const result = await todoCreateMutation(this, {
variables: {
title: 'Bake a cake',
},
});
if (!result.success) {
throw new Error(`Failed to create Todo!`);
}
}
}
```
## Credits
- `@SmartQuery()` decorator is based on the [vue-apollo-decorator](https://github.com/chanlito/vue-apollo-decorator)
package by chanlito, with some alteration to the types.
+1 -6
View File
@@ -1,11 +1,6 @@
{
"directory": "src/",
"exclude": [
"\\.(spec|e2e-spec)\\.ts$",
"src/cli/",
"src/database/",
"src/testing/"
],
"exclude": ["\\.(spec|e2e-spec)\\.ts$", "src/cli/", "src/database/", "src/testing/"],
"location": "all",
"structure": "flat",
"singleQuotes": true,
+15 -22
View File
@@ -1,24 +1,17 @@
const { pathsToModuleNameMapper } = require('ts-jest/utils');
const { compilerOptions } = require('./tsconfig');
module.exports = {
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/$1',
'^~/(.*)$': '<rootDir>/$1',
'^vue$': 'vue/dist/vue.common.js',
},
moduleFileExtensions: ['js', 'ts', 'vue', 'json'],
transform: {
'^.+\\.js$': 'babel-jest',
'^.+\\.ts?$': 'ts-jest',
'.*\\.(vue)$': 'vue-jest',
},
collectCoverage: false,
collectCoverageFrom: ['<rootDir>/components/**/*.vue', '<rootDir>/pages/**/*.vue'],
reporters: [
'default',
[
'jest-html-reporters',
{
filename: 'test/.results/test-results.html',
},
],
],
preset: 'ts-jest',
testEnvironment: 'node',
roots: ['<rootDir>/src/', '<rootDir>/test/'],
testRegex: '\\.(spec|e2e-spec)\\.ts$',
testPathIgnorePatterns: ['/node_modules/'],
testTimeout: 20000,
moduleNameMapper: pathsToModuleNameMapper(compilerOptions.paths, {
prefix: '<rootDir>/',
}),
coveragePathIgnorePatterns: ['<rootDir>/node_modules/', '<rootDir>/test/'],
coverageDirectory: '<rootDir>/test/.coverage',
reporters: ['default'],
};
+2787 -3170
View File
File diff suppressed because it is too large Load Diff
+28 -24
View File
@@ -1,6 +1,6 @@
{
"name": "vue-apollo-smart-ops",
"version": "0.0.2",
"version": "0.1.0",
"description": "Create TypeScript-typed operation functions for your Vue Apollo queries and mutations.",
"author": "Madscience Ltd",
"license": "MIT",
@@ -15,11 +15,12 @@
"prebuild": "rimraf dist && npm run barrels:generate",
"build": "tsc -p tsconfig.build.json",
"postbuild": "cp package.json README.md dist/",
"format": "prettier --write .",
"lint": "eslint --ext .ts --ignore-path .gitignore .",
"lint:fix": "eslint --ext .ts --ignore-path .gitignore . --fix",
"test": "jest",
"test:watch": "jest --watch",
"test:ci": "rimraf test/.results && mkdirp test/.results && jest --ci --runInBand --passWithNoTests",
"test:ci": "jest --ci --runInBand",
"version": "npm run build",
"postversion": "npm run postbuild",
"release": "np --contents dist/",
@@ -29,6 +30,9 @@
"lodash.isplainobject": "^4.0",
"lodash.mapvalues": "^4.6"
},
"optionalDependencies": {
"vue-class-component": "^7.2"
},
"peerDependencies": {
"apollo-client": ">=2.6",
"apollo-link": ">=1.2",
@@ -37,38 +41,38 @@
"vue-apollo": ">=3"
},
"devDependencies": {
"@types/jest": "26.0.22",
"@types/jest": "27.0.1",
"@types/lodash.isplainobject": "4.0.6",
"@types/lodash.mapvalues": "4.6.6",
"@typescript-eslint/eslint-plugin": "4.21.0",
"@typescript-eslint/parser": "4.21.0",
"@vue/test-utils": "1.1.3",
"@typescript-eslint/eslint-plugin": "4.30.0",
"@typescript-eslint/parser": "4.30.0",
"@vue/test-utils": "1.2.2",
"apollo-client": "2.6.10",
"apollo-link": "1.2.14",
"barrelsby": "2.2.0",
"dotenv-cli": "4.0.0",
"eslint": "7.23.0",
"eslint-config-prettier": "7.2.0",
"eslint-plugin-import": "2.22.1",
"eslint-plugin-jest": "24.3.4",
"eslint": "7.32.0",
"eslint-config-prettier": "8.3.0",
"eslint-plugin-import": "2.24.2",
"eslint-plugin-jest": "24.4.0",
"eslint-plugin-node": "11.1.0",
"eslint-plugin-prettier": "3.3.1",
"eslint-plugin-promise": "4.3.1",
"eslint-plugin-vue": "7.8.0",
"graphql": "15.5.0",
"husky": "6.0.0",
"jest": "26.6.3",
"jest-html-reporters": "2.1.3",
"lint-staged": "10.5.4",
"eslint-plugin-promise": "5.1.0",
"eslint-plugin-vue": "7.17.0",
"graphql": "15.5.2",
"graphql-tag": "2.12.5",
"husky": "7.0.2",
"jest": "27.1.0",
"lint-staged": "11.1.2",
"mkdirp": "1.0.4",
"np": "7.4.0",
"prettier": "2.2.1",
"np": "7.5.0",
"prettier": "2.3.2",
"rimraf": "3.0.2",
"ts-jest": "26.5.4",
"typescript": "4.2.3",
"vue": "2.6.12",
"ts-jest": "27.0.5",
"typescript": "4.4.2",
"vue": "2.6.14",
"vue-apollo": "3.0.7",
"vue-jest": "3.0.7"
"vue-class-component": "7.2.6",
"vue-property-decorator": "9.1.2"
},
"engines": {
"node": ">=12.9.0"
+2 -6
View File
@@ -12,12 +12,8 @@
"prCreation": "not-pending",
"stabilityDays": 3,
"rebaseWhen": "conflicted",
"labels": [
"dependencies"
],
"reviewers": [
"sgarner"
],
"labels": ["dependencies"],
"reviewers": ["sgarner"],
"packageRules": [
{
"packagePatterns": ["eslint"],
@@ -18,7 +18,7 @@ export function isGraphQLError(error: GraphQLError | any): error is GraphQLError
return error.extensions !== undefined;
}
export abstract class AbstractApolloErrorProcessor<TApp = Vue, TContext = ApolloOperationContext> {
export class ApolloErrorProcessor<TApp = Vue, TContext = ApolloOperationContext> {
public static FriendlyMessages: Record<string, string> = {
FAILED_TO_FETCH:
'Unable to communicate with server. The service may be down or you may be offline. Try again in a moment.',
@@ -39,7 +39,13 @@ export abstract class AbstractApolloErrorProcessor<TApp = Vue, TContext = Apollo
this.processedErrors = this.processApolloError(error);
}
public abstract showErrorNotifications(): void;
public showErrorNotifications(): void {
// This is just an example - to do something else (e.g. showing a visible notification to the user), you should
// implement your own class that extends ApolloErrorProcessor and replace this showErrorNotifications method.
this.processedErrors.forEach(error => {
console.error(`${error.type}: ${error.message}`, error.error);
});
}
public cleanError(error: ApolloError | GraphQLError | Record<string, any>): Error {
if (error instanceof Error) {
@@ -93,7 +99,30 @@ export abstract class AbstractApolloErrorProcessor<TApp = Vue, TContext = Apollo
protected getFriendlyMessage(errorCode: string, errorMessage: string): string;
protected getFriendlyMessage(errorCode: string): string | undefined;
protected getFriendlyMessage(errorCode: string, errorMessage?: string): string | undefined {
return (this.constructor as typeof AbstractApolloErrorProcessor).FriendlyMessages[errorCode] ?? errorMessage;
return (this.constructor as typeof ApolloErrorProcessor).FriendlyMessages[errorCode] ?? errorMessage;
}
protected processErrorMessage(message: GraphQLError['message']): string {
if (typeof message === 'object') {
if (message.error != null) {
return message.error;
}
return 'Unknown error: ' + JSON.stringify(message);
}
return message;
}
protected normalizeError(error: GraphQLError | Error): Error {
if (isGraphQLError(error)) {
return {
...error,
message: this.processErrorMessage(error.message),
};
}
return error;
}
private processApolloError(error: ApolloError): ProcessedApolloError[] {
@@ -134,8 +163,8 @@ export abstract class AbstractApolloErrorProcessor<TApp = Vue, TContext = Apollo
// Unauthorized (not logged in, or not allowed) error
const processedError: UnauthorizedError = {
type: ApolloErrorType.UNAUTHORIZED_ERROR,
error,
message: error.message,
error: this.normalizeError(error),
message: this.processErrorMessage(error.message),
path: error.path,
};
@@ -148,8 +177,8 @@ export abstract class AbstractApolloErrorProcessor<TApp = Vue, TContext = Apollo
// User input validation error
const processedError: InputValidationError = {
type: ApolloErrorType.INPUT_VALIDATION_ERROR,
error,
message: error.message,
error: this.normalizeError(error),
message: this.processErrorMessage(error.message),
path: error.path,
invalidArgs: error.extensions.invalidArgs,
violations: error.extensions.validationErrors,
@@ -163,9 +192,9 @@ export abstract class AbstractApolloErrorProcessor<TApp = Vue, TContext = Apollo
// Other GraphQL resolver error - probably a bug
const processedError: ServerError = {
type: error.extensions?.code != null ? error.extensions.code : ApolloErrorType.SERVER_ERROR,
error,
error: this.normalizeError(error),
message: this.getFriendlyMessage('INTERNAL_SERVER_ERROR', this.processErrorMessage(error.message)),
path: error.path,
message: this.getFriendlyMessage('INTERNAL_SERVER_ERROR', error.message),
};
this.onServerError(processedError);
@@ -198,16 +227,4 @@ export abstract class AbstractApolloErrorProcessor<TApp = Vue, TContext = Apollo
return errors;
}
private processErrorMessage(message: GraphQLError['message']): string {
if (typeof message === 'object') {
if (message.error != null) {
return message.error;
}
return 'Unknown error: ' + JSON.stringify(message);
}
return message;
}
}
+285
View File
@@ -0,0 +1,285 @@
import { ApolloQueryResult } from 'apollo-client';
import gql from 'graphql-tag';
import { Component, Vue } from 'vue-property-decorator';
import { createSmartQueryOptionsFunction } from '../query';
import { SmartQuery } from './SmartQuery';
interface Todo {
id: string;
title: string;
}
interface QueryResult {
todos: Todo[];
__typename?: 'Query';
}
interface QueryVariables {
skip?: number;
limit?: number;
}
describe('@SmartQuery() decorator', () => {
it('Adds basic query options', () => {
@Component
class TodoList extends Vue {
@SmartQuery({
query: gql`
query Todos($skip: Int, $limit: Int) {
todos(skip: $skip, limit: $limit) {
id
title
}
}
`,
variables() {
return this.vars;
},
})
todos!: Todo[];
get vars(): QueryVariables {
return {
limit: 10,
skip: 0,
};
}
}
const instance = new TodoList();
expect(instance.$options.apollo?.todos).toEqual(
expect.objectContaining({
query: expect.objectContaining({ definitions: expect.arrayContaining([]) }),
variables: expect.any(Function),
}),
);
});
it('Works with a query options function', () => {
const useTodoListQuery = createSmartQueryOptionsFunction<QueryResult, QueryVariables>(gql`
query Todos($skip: Int, $limit: Int) {
todos(skip: $skip, limit: $limit) {
id
title
}
}
`);
@Component<TodoList>({})
class TodoList extends Vue {
@SmartQuery(
useTodoListQuery<TodoList>({
skip() {
return this.foo === 'bar';
},
variables() {
return this.vars;
},
loadingKey: 'loading',
}),
)
todos!: Todo[];
loading: number = 0;
get vars(): QueryVariables {
return {
limit: 10,
skip: 0,
};
}
get foo(): string {
return 'bar';
}
}
const instance = new TodoList();
expect(instance.$options.apollo?.todos).toEqual(
expect.objectContaining({
query: expect.objectContaining({ definitions: expect.arrayContaining([]) }),
skip: expect.any(Function),
variables: expect.any(Function),
}),
);
});
it('Works with class inheritance and update, result methods', () => {
@Component
class TodoList extends Vue {
@SmartQuery({
query: gql`
query Todos($skip: Int, $limit: Int) {
todos(skip: $skip, limit: $limit) {
id
title
}
}
`,
variables() {
return this.vars;
},
})
todos!: Todo[];
get vars(): QueryVariables {
return {
limit: 10,
skip: 0,
};
}
}
@Component
class TodoList2 extends TodoList {
@SmartQuery<TodoList2, QueryResult>({
query: gql`
query Todos($skip: Int, $limit: Int) {
todos(skip: $skip, limit: $limit) {
id
title
}
}
`,
variables() {
return this.vars;
},
// eslint-disable-next-line @typescript-eslint/no-unused-vars
update(data: QueryResult) {
// data: QueryResult
},
// eslint-disable-next-line @typescript-eslint/no-unused-vars
result({ data, errors, loading }) {
this.doThings();
},
subscribeToMore: {
document: gql`
query Todos($skip: Int, $limit: Int) {
todos(skip: $skip, limit: $limit) {
id
title
}
}
`,
variables() {
return this.vars;
},
},
})
todos!: Todo[];
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
doThings() {
//
}
}
const instance = new TodoList2();
expect(instance.$options.apollo?.todos).toEqual(
expect.objectContaining({
query: expect.objectContaining({ definitions: expect.arrayContaining([]) }),
variables: expect.any(Function),
update: expect.any(Function),
result: expect.any(Function),
}),
);
});
it('Supports subscribeToMore with updateQuery method', () => {
@Component
class TodoList extends Vue {
@SmartQuery({
query: gql`
query Todos($skip: Int, $limit: Int) {
todos(skip: $skip, limit: $limit) {
id
title
}
}
`,
variables() {
return this.vars;
},
})
todos!: Todo[];
get vars(): QueryVariables {
return {
limit: 10,
skip: 0,
};
}
}
@Component
class TodoList3 extends TodoList {
@SmartQuery<TodoList3, QueryResult, QueryVariables>({
query: gql`
query Todos($skip: Int, $limit: Int) {
todos(skip: $skip, limit: $limit) {
id
title
}
}
`,
variables() {
return this.vars;
},
// eslint-disable-next-line @typescript-eslint/no-unused-vars
update(data: QueryResult) {
// data: QueryResult
},
// eslint-disable-next-line @typescript-eslint/no-unused-vars,@typescript-eslint/no-empty-function
result(data: ApolloQueryResult<QueryResult>) {},
subscribeToMore: [
{
document: gql`
query Todos($skip: Int, $limit: Int) {
todos(skip: $skip, limit: $limit) {
id
title
}
}
`,
variables() {
return this.vars;
},
// eslint-disable-next-line @typescript-eslint/no-unused-vars
updateQuery(prev, { subscriptionData: { data }, variables }) {
return {
...prev,
todos: [
{ id: '1', title: 'Int' },
{ id: '2', title: 'Float' },
{ id: '3', title: 'String' },
],
};
},
},
],
})
todos!: Todo[];
}
const instance = new TodoList3();
expect(instance.$options.apollo?.todos).toEqual(
expect.objectContaining({
query: expect.objectContaining({ definitions: expect.arrayContaining([]) }),
variables: expect.any(Function),
update: expect.any(Function),
result: expect.any(Function),
subscribeToMore: expect.arrayContaining([
expect.objectContaining({
document: expect.objectContaining({ definitions: expect.arrayContaining([]) }),
variables: expect.any(Function),
updateQuery: expect.any(Function),
}),
]),
}),
);
});
});
+18
View File
@@ -0,0 +1,18 @@
/*
* Based on https://github.com/chanlito/vue-apollo-decorator by chanlito ❤️
*/
import { ApolloError, OperationVariables } from 'apollo-client';
import { DocumentNode } from 'graphql';
import Vue from 'vue';
import { createDecorator, VueDecorator } from 'vue-class-component';
import { VueApolloSmartQueryOptions } from '../query';
export function SmartQuery<TApp = any, TResult = any, TVariables = OperationVariables, TError = ApolloError>(
options: TApp extends Vue ? VueApolloSmartQueryOptions<TResult, TVariables, TError, TApp> : DocumentNode,
): VueDecorator {
return createDecorator((componentOptions: any, k: string) => {
componentOptions.apollo = componentOptions.apollo || {};
componentOptions.apollo[k] = options;
});
}
+5
View File
@@ -0,0 +1,5 @@
/**
* @file Automatically generated by barrelsby.
*/
export * from './SmartQuery';
+25
View File
@@ -0,0 +1,25 @@
import { ApolloErrorProcessor } from './ApolloErrorProcessor';
import {
ApolloError,
ApolloErrorHandlerResult,
ApolloOperationContext,
ApolloOperationErrorHandlerFunction,
} from './types';
import { Vue } from 'vue/types/vue';
/**
* This is a simple example of an error handler function. You can copy this and implement your own in your application.
*/
export const handleApolloError: ApolloOperationErrorHandlerFunction<ApolloError, Vue> = (
error: ApolloError,
app: Vue,
context?: ApolloOperationContext,
): ApolloErrorHandlerResult => {
const processor = new ApolloErrorProcessor(error, app, context ?? {});
processor.showErrorNotifications();
return {
processedErrors: processor.processedErrors,
};
};
+3 -1
View File
@@ -2,8 +2,10 @@
* @file Automatically generated by barrelsby.
*/
export * from './AbstractApolloErrorProcessor';
export * from './ApolloErrorProcessor';
export * from './handleApolloError';
export * from './mutation';
export * from './query';
export * from './subscription';
export * from './types';
export * from './decorator/index';
+19 -16
View File
@@ -33,18 +33,17 @@ export type ApolloMutationClient<TResult, TVariables extends OperationVariables>
// Mutation function with typings
export type MutationOperationFunction<TResult, TVariables extends OperationVariables, TError = ApolloError> = (
app: VueAppWithApollo,
params: Omit<MutationOperationParams<TVariables, TError>, 'mutation'>,
params: Omit<MutationOperationParams<TResult, TVariables, TError>, 'mutation'>,
client?: ApolloMutationClient<TResult, TVariables>,
) => Promise<MutationResult<TResult>>;
// Parameters given to a MutationOperationFunction
export interface MutationOperationParams<
TResult,
TVariables extends OperationVariables,
TError = ApolloError,
TContext = ApolloOperationContext
> {
mutation: DocumentNode;
variables: TVariables;
TContext = ApolloOperationContext,
> extends MutationOptions<TResult, TVariables> {
context?: TContext;
onError?: ApolloOperationErrorHandlerFunction<TError>;
}
@@ -83,19 +82,23 @@ export async function mutateWithErrorHandling<
TResult,
TVariables extends OperationVariables,
TError,
TApp extends VueAppWithApollo = VueAppWithApollo
TApp extends VueAppWithApollo = VueAppWithApollo,
>(
app: TApp,
{ mutation, variables, onError, context }: MutationOperationParams<TVariables, TError>,
params: MutationOperationParams<TResult, TVariables, TError>,
client?: ApolloMutationClient<TResult, TVariables>,
): Promise<MutationResult<TResult>> {
const mutate =
client === undefined ? app.$apollo : typeof client === 'function' ? client : client.mutate.bind(client);
const mutate: ApolloClientMutationFunction | ApolloComponentMutationFunction =
client === undefined
? app.$apollo.mutate.bind(app.$apollo)
: typeof client === 'function'
? client
: client.mutate.bind(client);
try {
const result = await mutate({
mutation,
variables: cleanInput(variables),
...params,
variables: params.variables != null ? cleanInput(params.variables) : undefined,
});
if (result == null) {
@@ -104,6 +107,7 @@ export async function mutateWithErrorHandling<
return { success: true, data: result.data };
} catch (error) {
const { onError, context } = params;
const errorHandlerResult: ApolloErrorHandlerResult | undefined =
onError != null ? onError(error, app, context) : undefined;
@@ -119,23 +123,22 @@ export function createMutationFunction<
TResult,
TVariables extends OperationVariables,
TError = ApolloError,
TApp extends VueAppWithApollo = VueAppWithApollo
TApp extends VueAppWithApollo = VueAppWithApollo,
>(
mutation: DocumentNode,
onError?: ApolloOperationErrorHandlerFunction<TError, TApp>,
): MutationOperationFunction<TResult, TVariables, TError> {
return (
app: TApp,
params: Omit<MutationOperationParams<TVariables, TError>, 'mutation'>,
params: Omit<MutationOperationParams<TResult, TVariables, TError>, 'mutation'>,
client?: ApolloMutationClient<TResult, TVariables>,
): Promise<MutationResult<TResult>> => {
return mutateWithErrorHandling(
app,
{
mutation,
variables: params.variables,
onError: params.onError ?? onError,
context: params.context,
onError,
...params,
},
client,
);
+57 -14
View File
@@ -1,39 +1,82 @@
import { VueApolloQueryDefinition, ErrorHandler } from 'vue-apollo/types/options';
import { ApolloError, OperationVariables } from 'apollo-client';
import { DocumentNode } from 'graphql';
import { ErrorHandler, VueApolloQueryDefinition, VueApolloSubscribeToMoreOptions } from 'vue-apollo/types/options';
import { Vue } from 'vue/types/vue';
import { ApolloOperationErrorHandlerFunction } from './types';
type OverrideThis<F, T> = F extends (...args: infer A) => infer B ? (this: T, ...args: A) => B : F;
type OverrideAllThis<O, T> = {
[key in keyof O]: OverrideThis<O[key], T>;
};
type SubscribeToMoreOptionsPatched<TComponent, TResult, TVariables> = OverrideAllThis<
Omit<VueApolloSubscribeToMoreOptions<TResult, TVariables>, 'updateQuery' | 'variables'>,
TComponent
> & {
variables?: (this: TComponent) => any;
updateQuery?: UpdateQueryFn<TComponent, TResult, any, any>; // TODO: How should we pass subscript data & variables types?
};
type UpdateQueryFn<TComponent = any, TResult = any, TSubscriptionVariables = any, TSubscriptionData = any> = (
this: TComponent,
previousQueryResult: TResult,
options: {
subscriptionData: {
data: TSubscriptionData;
};
variables?: TSubscriptionVariables;
},
) => TResult;
export interface VueApolloQueryDefinitionPatched<TComponent extends Vue = Vue, TResult = any, TVariables = any>
extends OverrideAllThis<
Omit<VueApolloQueryDefinition<TResult, TVariables>, 'subscribeToMore' | 'variables' | 'loadingKey'>,
TComponent
> {
variables?: ((this: TComponent) => TVariables) | TVariables;
subscribeToMore?:
| SubscribeToMoreOptionsPatched<TComponent, TResult, TVariables>
| Array<SubscribeToMoreOptionsPatched<TComponent, TResult, TVariables>>;
loadingKey?: keyof TComponent;
// added here pending upstream fix for missing types https://github.com/vuejs/vue-apollo/pull/1257
throttle?: number;
debounce?: number;
}
export type VueApolloSmartQueryErrorHandler<
TResult = any,
TVariables = OperationVariables,
TError = ApolloError,
TApp extends Vue = Vue
TComponent extends Vue = Vue,
> = (
error: TError,
vm: TApp,
vm: TComponent,
key: string,
type: 'query',
options: VueApolloSmartQueryOptions<TResult, TVariables, TError, TApp>,
options: VueApolloSmartQueryOptions<TResult, TVariables, TError, TComponent>,
) => void;
// Type of VueApolloQueryDefinition['subscribeToMore'] is incompatible with generated QueryVariables types.
// Omitting it here since we don't use it anyway.
export type VueApolloSmartQueryOptions<
TResult = any,
TVariables = OperationVariables,
TError = ApolloError,
TApp extends Vue = Vue
> = Omit<VueApolloQueryDefinition<TResult, TVariables>, 'subscribeToMore' | 'error'> & {
error?: VueApolloSmartQueryErrorHandler<TResult, TVariables, TError, TApp>;
TComponent extends Vue = Vue,
> = VueApolloQueryDefinitionPatched<TComponent, TResult, TVariables> & {
error?: VueApolloSmartQueryErrorHandler<TResult, TVariables, TError, TComponent>;
};
export type VueApolloSmartQueryOptionsFunction<TResult, TVariables, TError = ApolloError, TApp extends Vue = Vue> = <
TComponent extends Vue = TApp,
>(
options?: Partial<Omit<VueApolloSmartQueryOptions<TResult, TVariables, TError, TComponent>, 'query'>>,
) => VueApolloSmartQueryOptions<TResult, TVariables, TError, TComponent>;
export function createSmartQueryOptionsFunction<TResult, TVariables, TError = ApolloError, TApp extends Vue = Vue>(
query: DocumentNode,
onError?: ApolloOperationErrorHandlerFunction<TError, TApp>,
): (
options?: Partial<VueApolloSmartQueryOptions<TResult, TVariables, TError, TApp>>,
) => VueApolloQueryDefinition<TResult, TVariables> {
): VueApolloSmartQueryOptionsFunction<TResult, TVariables> {
return (options = {}) => {
const defaultErrorHandlerFn: VueApolloSmartQueryErrorHandler<TResult, TVariables, TError, TApp> = (
error: TError,
@@ -54,8 +97,8 @@ export function createSmartQueryOptionsFunction<TResult, TVariables, TError = Ap
...options,
error:
// we have to override vue-apollo types because they are incorrect
((options.error as unknown) as ErrorHandler | undefined) ?? onError !== undefined
? ((defaultErrorHandlerFn as unknown) as ErrorHandler)
(options.error as unknown as ErrorHandler | undefined) ?? onError !== undefined
? (defaultErrorHandlerFn as unknown as ErrorHandler)
: undefined,
};
};
+5 -5
View File
@@ -8,7 +8,7 @@ export type VueApolloSmartSubscriptionErrorHandler<
TResult = any,
TVariables = OperationVariables,
TError = ApolloError,
TApp extends Vue = Vue
TApp extends Vue = Vue,
> = (
error: TError,
vm: TApp,
@@ -21,7 +21,7 @@ export type VueApolloSmartSubscriptionOptions<
TResult = any,
TVariables = OperationVariables,
TError = ApolloError,
TApp extends Vue = Vue
TApp extends Vue = Vue,
> = Omit<VueApolloSubscriptionDefinition<TVariables>, 'error'> & {
error?: VueApolloSmartSubscriptionErrorHandler<TResult, TVariables, TError, TApp>;
};
@@ -30,7 +30,7 @@ export function createSmartSubscriptionOptionsFunction<
TResult,
TVariables,
TError = ApolloError,
TApp extends Vue = Vue
TApp extends Vue = Vue,
>(
query: DocumentNode,
onError?: ApolloOperationErrorHandlerFunction<TError, TApp>,
@@ -57,8 +57,8 @@ export function createSmartSubscriptionOptionsFunction<
...options,
error:
// we have to override vue-apollo types because they are incorrect
((options.error as unknown) as ErrorHandler | undefined) ?? onError !== undefined
? ((defaultErrorHandlerFn as unknown) as ErrorHandler)
(options.error as unknown as ErrorHandler | undefined) ?? onError !== undefined
? (defaultErrorHandlerFn as unknown as ErrorHandler)
: undefined,
};
};
+2 -2
View File
@@ -26,7 +26,7 @@ export interface ApolloNetworkError extends Error {
};
}
export type GraphQLError = BaseGraphQLError & {
export type GraphQLError = Omit<BaseGraphQLError, 'message'> & {
message: string | Record<string, any>;
};
@@ -88,5 +88,5 @@ export interface ApolloErrorHandlerResult {
export type ApolloOperationErrorHandlerFunction<
TError = BaseApolloError,
TApp extends Vue = Vue,
TContext = ApolloOperationContext
TContext = ApolloOperationContext,
> = (error: TError, app: TApp, context?: TContext) => ApolloErrorHandlerResult;
View File
+4 -13
View File
@@ -15,18 +15,9 @@
"strictNullChecks": true,
"outDir": "./dist",
"baseUrl": "./",
"paths": {
},
"typeRoots": [
"./node_modules/@types",
"./@types"
]
"paths": {},
"types": ["@types/node", "@types/jest", "vue-apollo/types"]
},
"include":[
"src/**/*",
"test/**/*"
],
"exclude": [
"node_modules"
]
"include": ["src/**/*", "test/**/*"],
"exclude": ["node_modules"]
}