18 Commits

Author SHA1 Message Date
Simon Garner 051ab50e78 0.1.0-alpha.1
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-05 16:25:13 +12:00
Simon Garner df18d94a0a Remove --passWithNoTests
We have a test now!
2021-09-05 16:22:41 +12:00
Simon Garner 80acf744e5 Simplify headings in README 2021-09-05 16:19:11 +12:00
Simon Garner c521592611 Add usage instructions to README.md 2021-09-05 14:08:58 +12:00
Simon Garner 0d94ca7d58 Keep empty test directory 2021-09-05 13:10:07 +12:00
Simon Garner 9c46089dfb fix lint 2021-09-05 13:07:30 +12:00
Simon Garner daa242a889 Don't need to worry about test/.results directory any more 2021-09-05 11:51:55 +12:00
Simon Garner ddc04379c0 Revert "Run tsc build as part of test script"
This reverts commit b5ebe6cdf5.
2021-09-05 11:47:57 +12:00
Simon Garner cef7c4747d Remove example test file 2021-09-05 11:47:38 +12:00
Simon Garner e6645ef992 Add SmartQuery decorator tests 2021-09-05 11:47:17 +12:00
Simon Garner 93af489fde Add VueApolloSmartQueryOptionsFunction type 2021-09-05 08:11:04 +12:00
Simon Garner 756e88a2eb Remove vue-jest, use ts-jest only 2021-09-05 08:05:03 +12:00
Simon Garner b5ebe6cdf5 Run tsc build as part of test script 2021-09-05 08:04:18 +12:00
Simon Garner 8fbc9ab0d2 Remove jest-html-reporters 2021-09-05 07:59:26 +12:00
Simon Garner 80c0d3e9ad Add test for types and decorator 2021-09-05 07:57:09 +12:00
Simon Garner e4e0b61c88 fixup! Add vue-class-component to peerDependencies 2021-09-05 07:56:19 +12:00
Simon Garner 22787d57e4 Add SmartQuery decorator 2021-09-05 07:55:23 +12:00
Simon Garner b5e3123877 Add vue-class-component to peerDependencies 2021-09-05 07:52:26 +12:00
25 changed files with 5160 additions and 13153 deletions
+6 -1
View File
@@ -7,7 +7,12 @@ module.exports = {
parserOptions: {
parser: '@typescript-eslint/parser',
},
extends: ['plugin:@typescript-eslint/recommended', 'plugin:vue/essential', 'prettier'],
extends: [
'plugin:@typescript-eslint/recommended',
'plugin:vue/essential',
'prettier/vue',
'plugin:prettier/recommended',
],
plugins: ['@typescript-eslint', 'vue'],
// add your custom rules here
rules: {
+18
View File
@@ -0,0 +1,18 @@
name: Cleanup
on:
schedule:
- cron: '0 1 * * *' # every day at 1am
jobs:
remove-old-artifacts:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Remove old artifacts
uses: c-hive/gha-remove-artifacts@v1
with:
age: '1 days' # delete artifacts older than this
skip-tags: false # don't treat artifacts created by runs on tagged commits any differently
skip-recent: 2 # keep the last 2 runs, regardless of age
+12 -10
View File
@@ -3,7 +3,6 @@ name: Publish
on:
release:
types: [created]
workflow_dispatch:
jobs:
#
@@ -13,27 +12,30 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Use Node.js 16.x
uses: actions/setup-node@v3
- uses: actions/checkout@v2
- name: Use Node.js 12.x
uses: actions/setup-node@v2
with:
node-version: '16.x'
node-version: '12.x'
always-auth: true
registry-url: https://registry.npmjs.org
# Dependencies
- id: cache_npm
name: Cache Node.js modules
uses: actions/cache@v3
uses: actions/cache@v2
with:
path: |
~/.npm
./node_modules.tar.zstd
key: ${{ runner.OS }}-node16.x-${{ hashFiles('**/package-lock.json') }}
key: ${{ runner.OS }}-npm-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.OS }}-node16.x-
${{ runner.OS }}-npm-
${{ runner.OS }}-
- name: Install dependencies with NPM
if: steps.cache_npm.outputs.cache-hit != 'true'
run: npm ci
env:
NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
- name: Unarchive node_modules
if: steps.cache_npm.outputs.cache-hit == 'true'
run: tar --use-compress-program "zstd -d --long=31" -xf node_modules.tar.zstd
@@ -54,6 +56,6 @@ jobs:
- name: Copy extra files into dist directory
run: cp package.json README* dist/
- name: Publish release to NPM registry
run: npm publish dist/
run: npm publish dist
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_PUBLISH_TOKEN }}
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN_PUBLISHING }}
@@ -1,4 +1,4 @@
name: Build
name: Test
on: [push]
@@ -10,35 +10,37 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Use Node.js 16.x
uses: actions/setup-node@v3
- uses: actions/checkout@v2
- name: Use Node.js 12.x
uses: actions/setup-node@v2
with:
node-version: '16.x'
node-version: '12.x'
always-auth: true
registry-url: https://registry.npmjs.org
- id: cache_npm
name: Cache Node.js modules
uses: actions/cache@v3
uses: actions/cache@v2
with:
path: |
~/.npm
./node_modules.tar.zstd
key: ${{ runner.OS }}-node16.x-${{ hashFiles('**/package-lock.json') }}
key: ${{ runner.OS }}-npm-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.OS }}-node16.x-
${{ runner.OS }}-npm-
${{ runner.OS }}-
- name: Install dependencies with NPM
if: steps.cache_npm.outputs.cache-hit != 'true'
run: npm ci
env:
NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
- name: Archive node_modules
if: steps.cache_npm.outputs.cache-hit != 'true'
run: tar --use-compress-program "zstd -T0 --long=31 -1" -cf node_modules.tar.zstd -P node_modules
- name: Persisting node_modules artifact
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v2
with:
name: node_modules.tar.zstd
path: node_modules.tar.zstd
retention-days: 2
#
# lint job
@@ -49,32 +51,22 @@ jobs:
steps:
# Setup
- uses: actions/checkout@v3
- uses: actions/checkout@v2
- name: Use Node.js 12.x
uses: actions/setup-node@v2
with:
token: ${{ secrets.BUILD_USER_TOKEN || github.token }} # allows commit of any fixes to trigger a new workflow run
- name: Use Node.js 16.x
uses: actions/setup-node@v3
with:
node-version: '16.x'
node-version: '12.x'
always-auth: true
registry-url: https://registry.npmjs.org
- name: Restore node_modules artifact
uses: actions/download-artifact@v3
uses: actions/download-artifact@v2
with:
name: node_modules.tar.zstd
- name: Unarchive node_modules
run: tar --use-compress-program "zstd -d --long=31" -xf node_modules.tar.zstd
# Lint
- name: Run linters
uses: wearerequired/lint-action@v2
with:
prettier: true
eslint: true
eslint_args: "--ext '.ts,.js' --ignore-path '.gitignore' --ignore-pattern '.github/*'"
continue_on_error: false
auto_fix: ${{ secrets.BUILD_USER_TOKEN && 'true' || 'false' }}
git_name: equabot
git_email: git@equalogic.com
# ESLint
- name: Lint source code
run: npm run lint
#
# build job
@@ -85,15 +77,15 @@ jobs:
steps:
# Setup
- uses: actions/checkout@v3
- name: Use Node.js 16.x
uses: actions/setup-node@v3
- uses: actions/checkout@v2
- name: Use Node.js 12.x
uses: actions/setup-node@v2
with:
node-version: '16.x'
node-version: '12.x'
always-auth: true
registry-url: https://registry.npmjs.org
- name: Restore node_modules artifact
uses: actions/download-artifact@v3
uses: actions/download-artifact@v2
with:
name: node_modules.tar.zstd
- name: Unarchive node_modules
@@ -121,15 +113,9 @@ jobs:
steps:
# Setup
- uses: actions/checkout@v3
- name: Use Node.js 16.x
uses: actions/setup-node@v3
with:
node-version: '16.x'
always-auth: true
registry-url: https://registry.npmjs.org
- uses: actions/checkout@v2
- name: Restore node_modules artifact
uses: actions/download-artifact@v3
uses: actions/download-artifact@v2
with:
name: node_modules.tar.zstd
- name: Unarchive node_modules
@@ -139,3 +125,9 @@ jobs:
run: node_modules/.bin/dotenv -e .env.ci -- npm run test:ci
env:
CI: true
- if: always()
name: Persisting test-results.html artifact
uses: actions/upload-artifact@v2
with:
name: test-results.html
path: test/.results/test-results.html
+1 -1
View File
@@ -1 +1 @@
16.16.0
14.17.6
-13
View File
@@ -1,13 +0,0 @@
# Ignore workflows
.github
# Node modules
node_modules
# Test results
test/.results
test/.coverage
# IDE
.idea
.vscode
+5 -10
View File
@@ -29,7 +29,7 @@ accepted by the generated function are fully type-checked based on the query def
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.
you can automatically generate options functions for all of the query operations in your project.
The following example manually creates an options function and assigns it to the variable `useTodoListQuery`:
@@ -60,8 +60,8 @@ following example adds a `todos` Smart Query to a component, in Vue class compon
skip: 0,
};
},
}),
},
})
}
})
class TodoList extends Vue {
todos: Todo[] | null = null;
@@ -114,7 +114,7 @@ is that the options accepted by the generated function are fully type-checked ba
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.
you can automatically generate mutation functions for all of the mutation operations in your project.
The following example manually creates a mutation function and assigns it to the variable `todoCreateMutation`:
@@ -142,15 +142,10 @@ class TodoList extends Vue {
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.
+6 -1
View File
@@ -1,6 +1,11 @@
{
"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,
+4617 -12567
View File
File diff suppressed because it is too large Load Diff
+38 -28
View File
@@ -1,21 +1,20 @@
{
"name": "vue-apollo-smart-ops",
"version": "0.2.0-beta.1",
"version": "0.1.0-alpha.1",
"description": "Create TypeScript-typed operation functions for your Vue Apollo queries and mutations.",
"author": "Equalogic Ltd",
"author": "Madscience Ltd",
"license": "MIT",
"publishConfig": {
"access": "public"
},
"repository": {
"type": "git",
"url": "https://github.com/equalogic/vue-apollo-smart-ops.git"
"url": "https://github.com/madscience/vue-apollo-smart-ops.git"
},
"scripts": {
"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",
@@ -36,39 +35,42 @@
"peerDependencies": {
"apollo-client": ">=2.6",
"apollo-link": ">=1.2",
"graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0",
"graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0",
"vue": ">=2.6",
"vue-apollo": ">=3"
},
"devDependencies": {
"@types/jest": "27.5.2",
"@types/lodash.isplainobject": "4.0.7",
"@types/lodash.mapvalues": "4.6.7",
"@typescript-eslint/eslint-plugin": "5.30.5",
"@typescript-eslint/parser": "5.30.5",
"@vue/test-utils": "1.3.0",
"@types/jest": "27.0.1",
"@types/lodash.isplainobject": "4.0.6",
"@types/lodash.mapvalues": "4.6.6",
"@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.3.4",
"dotenv-cli": "6.0.0",
"eslint": "8.19.0",
"eslint-config-prettier": "8.5.0",
"eslint-plugin-import": "2.26.0",
"eslint-plugin-jest": "26.5.3",
"barrelsby": "2.2.0",
"dotenv-cli": "4.0.0",
"eslint": "7.32.0",
"eslint-config-prettier": "7.2.0",
"eslint-plugin-import": "2.24.2",
"eslint-plugin-jest": "24.4.0",
"eslint-plugin-node": "11.1.0",
"eslint-plugin-promise": "6.0.0",
"eslint-plugin-vue": "9.2.0",
"graphql": "15.8.0",
"graphql-tag": "2.12.6",
"jest": "27.5.1",
"eslint-plugin-prettier": "3.4.1",
"eslint-plugin-promise": "4.3.1",
"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.6.1",
"prettier": "2.7.1",
"np": "7.5.0",
"prettier": "2.2.1",
"rimraf": "3.0.2",
"ts-jest": "27.1.5",
"typescript": "4.7.4",
"ts-jest": "27.0.5",
"typescript": "4.2.4",
"vue": "2.6.14",
"vue-apollo": "3.1.0",
"vue-apollo": "3.0.7",
"vue-class-component": "7.2.6",
"vue-property-decorator": "9.1.2"
},
@@ -76,5 +78,13 @@
"node": ">=12.9.0"
},
"main": "index.js",
"types": "index.d.ts"
"types": "index.d.ts",
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"*.{ts,js}": "eslint --ext .ts,.js --ignore-path .gitignore --fix --cache"
}
}
+7 -10
View File
@@ -2,7 +2,7 @@
"extends": [
"config:js-lib",
":automergeMajor",
":automergeBranch",
":automergePr",
":automergeRequireAllStatusChecks",
":dependencyDashboard",
":semanticCommitsDisabled"
@@ -12,9 +12,12 @@
"prCreation": "not-pending",
"stabilityDays": 3,
"rebaseWhen": "conflicted",
"lockFileMaintenance": true,
"labels": ["dependencies"],
"reviewers": ["sgarner"],
"labels": [
"dependencies"
],
"reviewers": [
"sgarner"
],
"packageRules": [
{
"packagePatterns": ["eslint"],
@@ -23,12 +26,6 @@
{
"packagePatterns": ["jest"],
"groupName": "jest"
},
{
"matchDatasources": ["nvm", "npm"],
"matchPackageNames": ["node", "@types/node"],
"groupName": "Node.js",
"allowedVersions": "^16"
}
]
}
+219
View File
@@ -0,0 +1,219 @@
import Vue from 'vue';
import {
ApolloError,
ApolloErrorType,
ApolloOperationContext,
GraphQLError,
InputValidationError,
ProcessedApolloError,
ServerError,
UnauthorizedError,
} from './types';
export function isApolloError(error: ApolloError | any): error is ApolloError {
return error.graphQLErrors !== undefined;
}
export function isGraphQLError(error: GraphQLError | any): error is GraphQLError {
return error.extensions !== undefined;
}
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.',
INTERNAL_SERVER_ERROR: `A server error has occurred.`,
};
public processedErrors: ProcessedApolloError[];
protected readonly originalError: Error;
protected readonly app: TApp;
protected readonly context: TContext;
public constructor(error: ApolloError, app: TApp, context: TContext) {
this.originalError = error;
this.app = app;
this.context = context;
this.processedErrors = this.processApolloError(error);
}
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) {
return error;
}
// the `error` object we have may not be an actual Error instance
// create a new one suitable for e.g. capturing to Sentry
const cleanError = new Error(error.message);
if (isGraphQLError(error)) {
cleanError.name = 'GraphQLError' + (error.extensions?.code != null ? `[${error.extensions.code}]` : '');
cleanError.stack = this.originalError.stack;
Object.defineProperty(cleanError, 'nodes', { value: error.nodes });
Object.defineProperty(cleanError, 'source', { value: error.source });
Object.defineProperty(cleanError, 'positions', { value: error.positions });
Object.defineProperty(cleanError, 'path', { value: error.path });
Object.defineProperty(cleanError, 'extensions', { value: JSON.stringify(error.extensions) });
Object.defineProperty(cleanError, 'originalError', { value: this.originalError });
} else {
Object.keys(error).forEach(key => Object.defineProperty(cleanError, key, { value: error[key] }));
}
return cleanError;
}
protected isUnauthorizedError(error: GraphQLError): boolean {
return (
error.message === 'Unauthorized' ||
error.extensions?.code === 'FORBIDDEN' ||
error.extensions?.exception?.status === 401
);
}
// eslint-disable-next-line @typescript-eslint/no-empty-function,@typescript-eslint/no-unused-vars
protected onUnauthorizedError(error: UnauthorizedError): void {
// extending classes can take action here, e.g. go to log in page
}
// eslint-disable-next-line @typescript-eslint/no-empty-function,@typescript-eslint/no-unused-vars
protected onServerError(error: ServerError): void {
// extending classes can take action here, e.g. capture to Sentry
}
// eslint-disable-next-line @typescript-eslint/no-empty-function,@typescript-eslint/no-unused-vars
protected onInputValidationError(error: InputValidationError): void {
// extending classes can take action here, e.g. capture to Sentry
}
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 ApolloErrorProcessor).FriendlyMessages[errorCode] ?? errorMessage;
}
private processApolloError(error: ApolloError): ProcessedApolloError[] {
if (error.graphQLErrors != null && error.graphQLErrors.length > 0) {
// Successful request but with errors from the resolver
return error.graphQLErrors.flatMap(graphQLError => this.processGraphQLError(graphQLError));
}
if (
error.networkError != null &&
error.networkError.result != null &&
error.networkError.result.errors != null &&
error.networkError.result.errors.length > 0
) {
// Network error that contains GraphQL errors inside it. Can occur when server responds with a non-200 status code
return error.networkError.result.errors.flatMap(graphQLError => this.processGraphQLError(graphQLError));
}
if (error.networkError != null) {
// Network error, e.g. server is not responding or some other exception occurs
return this.processNetworkError(error);
}
// Some other internal server error
const processedError: ServerError = {
type: ApolloErrorType.SERVER_ERROR,
error,
message: error.message,
};
this.onServerError(processedError);
return [processedError];
}
private processGraphQLError(error: GraphQLError): ProcessedApolloError[] {
if (this.isUnauthorizedError(error)) {
// Unauthorized (not logged in, or not allowed) error
const processedError: UnauthorizedError = {
type: ApolloErrorType.UNAUTHORIZED_ERROR,
error,
message: error.message,
path: error.path,
};
this.onUnauthorizedError(processedError);
return [processedError];
}
if (error.extensions?.validationErrors != null) {
// User input validation error
const processedError: InputValidationError = {
type: ApolloErrorType.INPUT_VALIDATION_ERROR,
error,
message: error.message,
path: error.path,
invalidArgs: error.extensions.invalidArgs,
violations: error.extensions.validationErrors,
};
this.onInputValidationError(processedError);
return [processedError];
}
// Other GraphQL resolver error - probably a bug
const processedError: ServerError = {
type: error.extensions?.code != null ? error.extensions.code : ApolloErrorType.SERVER_ERROR,
error,
path: error.path,
message: this.getFriendlyMessage('INTERNAL_SERVER_ERROR', error.message),
};
this.onServerError(processedError);
return [processedError];
}
private processNetworkError(error: ApolloError): ProcessedApolloError[] {
const errors: ProcessedApolloError[] = [];
let message: string;
if (error.networkError != null && error.networkError.message != null) {
message = this.processErrorMessage(error.networkError.message);
} else {
message = this.processErrorMessage(error.message);
}
switch (message) {
case 'Failed to fetch':
message = this.getFriendlyMessage('FAILED_TO_FETCH', message);
break;
}
errors.push({
type: ApolloErrorType.NETWORK_ERROR,
error,
statusCode: error.networkError != null ? error.networkError.statusCode : undefined,
message,
});
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;
}
}
-3
View File
@@ -76,13 +76,10 @@ describe('@SmartQuery() decorator', () => {
variables() {
return this.vars;
},
loadingKey: 'loading',
}),
)
todos!: Todo[];
loading: number = 0;
get vars(): QueryVariables {
return {
limit: 10,
-50
View File
@@ -1,50 +0,0 @@
import {
ApolloErrorType,
InputValidationError,
NetworkError,
ProcessedApolloError,
ServerError,
UnauthorizedError,
UserInputError,
ValidationRuleViolation,
} from './types';
export interface ApolloErrorHandlerResultInterface {
allErrors: ProcessedApolloError[];
validationRuleViolations?: ValidationRuleViolation[];
}
export class ApolloErrorHandlerResult implements ApolloErrorHandlerResultInterface {
public readonly allErrors: ProcessedApolloError[];
public constructor(
public readonly unhandledErrors: ProcessedApolloError[],
public readonly handledErrors: ProcessedApolloError[],
) {
this.allErrors = [...unhandledErrors, ...handledErrors];
}
public get networkErrors(): NetworkError[] {
return this.allErrors.filter((e): e is NetworkError => e.type === ApolloErrorType.NETWORK_ERROR);
}
public get serverErrors(): ServerError[] {
return this.allErrors.filter((e): e is ServerError => e.type === ApolloErrorType.SERVER_ERROR);
}
public get unauthorizedErrors(): UnauthorizedError[] {
return this.allErrors.filter((e): e is UnauthorizedError => e.type === ApolloErrorType.UNAUTHORIZED_ERROR);
}
public get userInputErrors(): UserInputError[] {
return this.allErrors.filter((e): e is UserInputError => e.type === ApolloErrorType.BAD_USER_INPUT);
}
public get inputValidationErrors(): InputValidationError[] {
return this.allErrors.filter((e): e is InputValidationError => e.type === ApolloErrorType.INPUT_VALIDATION_ERROR);
}
public get validationRuleViolations(): ValidationRuleViolation[] {
return this.inputValidationErrors.flatMap(e => e.violations);
}
}
-34
View File
@@ -1,34 +0,0 @@
import { processApolloError } from './processApolloError';
import { ApolloOperationContext } from '../types';
import { Vue } from 'vue/types/vue';
import { ApolloError, ApolloOperationErrorHandlerFunction } from './types';
import { ApolloErrorHandlerResult } from './ApolloErrorHandlerResult';
/**
* 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,
ApolloOperationContext,
ApolloErrorHandlerResult
> = (error: ApolloError, app: Vue, context?: ApolloOperationContext): ApolloErrorHandlerResult => {
const { unhandledErrors, handledErrors } = processApolloError(error, {
app,
context,
// Example of a handler function for a particular type of error:
onUnauthorizedError: error => {
console.warn('Unauthorized! Logging you out...', error);
//logout();
// Returning true indicates to the processor that we've handled this error
return true;
},
});
unhandledErrors.forEach(error => {
console.error(`${error.type}: ${error.message}`, error.error);
});
return new ApolloErrorHandlerResult(unhandledErrors, handledErrors);
};
-8
View File
@@ -1,8 +0,0 @@
/**
* @file Automatically generated by barrelsby.
*/
export * from './ApolloErrorHandlerResult';
export * from './handleApolloError';
export * from './processApolloError';
export * from './types';
-213
View File
@@ -1,213 +0,0 @@
import Vue from 'vue';
import { ApolloOperationContext } from '../types';
import {
ApolloError,
ApolloErrorType,
GraphQLError,
InputValidationError,
NetworkError,
ProcessedApolloError,
ServerError,
UnauthorizedError,
} from './types';
export function isApolloError(error: ApolloError | any): error is ApolloError {
return error.graphQLErrors !== undefined;
}
export function isGraphQLError(error: GraphQLError | any): error is GraphQLError {
return error.extensions !== undefined;
}
function normalizeErrorMessage(message: GraphQLError['message']): string {
if (typeof message === 'object') {
if (message.error != null) {
return message.error;
}
return 'Unknown error: ' + JSON.stringify(message);
}
return message;
}
function normalizeError(error: GraphQLError | Error): Error {
if (isGraphQLError(error)) {
return {
...error,
message: normalizeErrorMessage(error.message),
};
}
return error;
}
function translateErrorMessage(messageOrCode: string, translations: Record<string, string>): string {
return translations[messageOrCode] ?? messageOrCode;
}
export interface ApolloErrorProcessorOptions<TApp = Vue, TContext = ApolloOperationContext> {
app?: TApp;
context?: TContext;
isUnauthorizedError?: (error: GraphQLError) => boolean;
onUnauthorizedError?: (error: UnauthorizedError) => boolean | void;
onInputValidationError?: (error: InputValidationError) => boolean | void;
onServerError?: (error: ServerError) => boolean | void;
onNetworkError?: (error: NetworkError) => boolean | void;
translations?: Record<string, string>;
}
export const defaultErrorMessageTranslations: 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.',
INTERNAL_SERVER_ERROR: `A server error has occurred.`,
};
const defaultProcessorOptions: ApolloErrorProcessorOptions = {
isUnauthorizedError: (error: GraphQLError) =>
error.message === 'Unauthorized' ||
error.extensions?.code === 'FORBIDDEN' ||
error.extensions?.exception?.status === 401,
translations: defaultErrorMessageTranslations,
};
export interface ApolloErrorProcessorResult {
unhandledErrors: ProcessedApolloError[];
handledErrors: ProcessedApolloError[];
}
function processGraphQLError(
error: GraphQLError,
options: ApolloErrorProcessorOptions = {},
): ApolloErrorProcessorResult {
options = { ...defaultProcessorOptions, ...options };
const { isUnauthorizedError, onUnauthorizedError, onInputValidationError, onServerError, translations } = options;
if (isUnauthorizedError != null && isUnauthorizedError(error)) {
// Unauthorized (not logged in, or not allowed) error
const processedError: UnauthorizedError = {
type: ApolloErrorType.UNAUTHORIZED_ERROR,
error: normalizeError(error),
message: normalizeErrorMessage(error.message),
path: error.path,
};
if (onUnauthorizedError != null && onUnauthorizedError(processedError)) {
return { unhandledErrors: [], handledErrors: [processedError] };
}
return { unhandledErrors: [processedError], handledErrors: [] };
}
if (error.extensions?.validationErrors != null) {
// User input validation error
const processedError: InputValidationError = {
type: ApolloErrorType.INPUT_VALIDATION_ERROR,
error: normalizeError(error),
message: normalizeErrorMessage(error.message),
path: error.path,
invalidArgs: error.extensions.invalidArgs,
violations: error.extensions.validationErrors,
};
if (onInputValidationError != null && onInputValidationError(processedError)) {
return { unhandledErrors: [], handledErrors: [processedError] };
}
return { unhandledErrors: [processedError], handledErrors: [] };
}
// Other GraphQL resolver error - probably a bug
const processedError: ServerError = {
type: error.extensions?.code != null ? error.extensions.code : ApolloErrorType.SERVER_ERROR,
error: normalizeError(error),
message: translateErrorMessage('INTERNAL_SERVER_ERROR', translations ?? defaultErrorMessageTranslations),
path: error.path,
};
if (onServerError != null && onServerError(processedError)) {
return { unhandledErrors: [], handledErrors: [processedError] };
}
return { unhandledErrors: [processedError], handledErrors: [] };
}
function processNetworkError(
error: ApolloError,
options: ApolloErrorProcessorOptions = {},
): ApolloErrorProcessorResult {
options = { ...defaultProcessorOptions, ...options };
const { onNetworkError, translations } = options;
let message: string =
error.networkError != null && error.networkError.message != null
? normalizeErrorMessage(error.networkError.message)
: normalizeErrorMessage(error.message);
switch (message) {
case 'Failed to fetch':
message = translateErrorMessage('FAILED_TO_FETCH', translations ?? defaultErrorMessageTranslations);
break;
}
const processedError: NetworkError = {
type: ApolloErrorType.NETWORK_ERROR,
error,
statusCode: error.networkError != null ? error.networkError.statusCode : undefined,
message,
};
if (onNetworkError != null && onNetworkError(processedError)) {
return { unhandledErrors: [], handledErrors: [processedError] };
}
return { unhandledErrors: [processedError], handledErrors: [processedError] };
}
export function processApolloError(
error: ApolloError,
options: ApolloErrorProcessorOptions = {},
): ApolloErrorProcessorResult {
options = { ...defaultProcessorOptions, ...options };
const { onServerError } = options;
if (error.graphQLErrors != null && error.graphQLErrors.length > 0) {
// Successful request but with errors from the resolver
const errorProcessorResults = error.graphQLErrors.map(graphQLError => processGraphQLError(graphQLError, options));
return {
unhandledErrors: errorProcessorResults.flatMap(result => result.unhandledErrors),
handledErrors: errorProcessorResults.flatMap(result => result.handledErrors),
};
}
if (error.networkError?.result?.errors != null && error.networkError.result.errors.length > 0) {
// Network error that contains GraphQL errors inside it. Can occur when server responds with a non-200 status code
const errorProcessorResults = error.networkError.result.errors.map(graphQLError =>
processGraphQLError(graphQLError, options),
);
return {
unhandledErrors: errorProcessorResults.flatMap(result => result.unhandledErrors),
handledErrors: errorProcessorResults.flatMap(result => result.handledErrors),
};
}
if (error.networkError != null) {
// Network error, e.g. server is not responding or some other exception occurs
return processNetworkError(error, options);
}
// Some other internal server error
const processedError: ServerError = {
type: ApolloErrorType.SERVER_ERROR,
error,
message: error.message,
};
if (onServerError != null && onServerError(processedError)) {
return { unhandledErrors: [], handledErrors: [processedError] };
}
return { unhandledErrors: [processedError], handledErrors: [] };
}
-87
View File
@@ -1,87 +0,0 @@
import { GraphQLError as BaseGraphQLError } from 'graphql';
import { ApolloError as BaseApolloError } from 'apollo-client';
import { Vue } from 'vue/types/vue';
import { ApolloOperationContext } from '../types';
import { ApolloErrorHandlerResultInterface } from './ApolloErrorHandlerResult';
export enum ApolloErrorType {
NETWORK_ERROR = 'NETWORK_ERROR',
SERVER_ERROR = 'SERVER_ERROR',
UNAUTHORIZED_ERROR = 'UNAUTHORIZED_ERROR',
BAD_USER_INPUT = 'BAD_USER_INPUT',
INPUT_VALIDATION_ERROR = 'INPUT_VALIDATION_ERROR',
}
// Upstream type declares networkError as Error, but it can contain additional properties, we override to add these
export type ApolloError = BaseApolloError & {
networkError: ApolloNetworkError | null;
graphQLErrors: GraphQLError[];
};
export interface ApolloNetworkError extends Error {
statusCode?: number;
response?: any;
result?: {
errors: GraphQLError[];
};
}
export type GraphQLError = Omit<BaseGraphQLError, 'message'> & {
message: string | Record<string, any>;
};
export type ProcessedApolloError =
| NetworkError
| ServerError
| UnauthorizedError
| UserInputError
| InputValidationError;
export interface NetworkError {
type: ApolloErrorType.NETWORK_ERROR;
error: Error;
message: string;
statusCode?: number;
}
export interface ServerError {
type: ApolloErrorType.SERVER_ERROR;
error: Error;
path?: readonly (string | number)[];
message: string;
}
export interface UnauthorizedError {
type: ApolloErrorType.UNAUTHORIZED_ERROR;
error: Error;
path?: readonly (string | number)[];
message: string;
}
export interface UserInputError {
type: ApolloErrorType.BAD_USER_INPUT;
error: Error;
path?: readonly (string | number)[];
message: string;
}
export interface InputValidationError {
type: ApolloErrorType.INPUT_VALIDATION_ERROR;
error: Error;
path?: readonly (string | number)[];
message: string;
invalidArgs: string[];
violations: ValidationRuleViolation[];
}
export interface ValidationRuleViolation {
path: string[];
message: string;
value?: any;
}
export type ApolloOperationErrorHandlerFunction<
TError = BaseApolloError,
TApp extends Vue = Vue,
TContext = ApolloOperationContext,
TResult = ApolloErrorHandlerResultInterface,
> = (error: TError, app: TApp, context?: TContext) => TResult;
+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,
};
};
+2 -1
View File
@@ -2,9 +2,10 @@
* @file Automatically generated by barrelsby.
*/
export * from './ApolloErrorProcessor';
export * from './handleApolloError';
export * from './mutation';
export * from './query';
export * from './subscription';
export * from './types';
export * from './decorator/index';
export * from './error/index';
+20 -19
View File
@@ -6,13 +6,13 @@ import { FetchPolicy, MutationBaseOptions } from 'apollo-client/core/watchQueryO
import { Vue } from 'vue/types/vue';
import mapValues from 'lodash.mapvalues';
import isPlainObject from 'lodash.isplainobject';
import { ApolloOperationContext } from './types';
import {
ApolloErrorHandlerResultInterface,
ApolloErrorHandlerResult,
ApolloOperationContext,
ApolloOperationErrorHandlerFunction,
ProcessedApolloError,
ValidationRuleViolation,
} from './error';
} from './types';
export type ApolloComponentMutationFunction<R = any, TVariables = OperationVariables> = (
options: MutationBaseOptions<R, TVariables> &
@@ -33,17 +33,18 @@ 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<TResult, TVariables, TError>, 'mutation'>,
params: Omit<MutationOperationParams<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,
> extends MutationOptions<TResult, TVariables> {
TContext = ApolloOperationContext
> {
mutation: DocumentNode;
variables: TVariables;
context?: TContext;
onError?: ApolloOperationErrorHandlerFunction<TError>;
}
@@ -82,13 +83,13 @@ export async function mutateWithErrorHandling<
TResult,
TVariables extends OperationVariables,
TError,
TApp extends VueAppWithApollo = VueAppWithApollo,
TApp extends VueAppWithApollo = VueAppWithApollo
>(
app: TApp,
params: MutationOperationParams<TResult, TVariables, TError>,
{ mutation, variables, onError, context }: MutationOperationParams<TVariables, TError>,
client?: ApolloMutationClient<TResult, TVariables>,
): Promise<MutationResult<TResult>> {
const mutate: ApolloClientMutationFunction | ApolloComponentMutationFunction =
const mutate =
client === undefined
? app.$apollo.mutate.bind(app.$apollo)
: typeof client === 'function'
@@ -97,8 +98,8 @@ export async function mutateWithErrorHandling<
try {
const result = await mutate({
...params,
variables: params.variables != null ? cleanInput(params.variables) : undefined,
mutation,
variables: cleanInput(variables),
});
if (result == null) {
@@ -107,13 +108,12 @@ export async function mutateWithErrorHandling<
return { success: true, data: result.data };
} catch (error) {
const { onError, context } = params;
const errorHandlerResult: ApolloErrorHandlerResultInterface | undefined =
const errorHandlerResult: ApolloErrorHandlerResult | undefined =
onError != null ? onError(error, app, context) : undefined;
return {
success: false,
errors: errorHandlerResult?.allErrors,
errors: errorHandlerResult?.processedErrors,
validationRuleViolations: errorHandlerResult?.validationRuleViolations,
};
}
@@ -123,22 +123,23 @@ 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<TResult, TVariables, TError>, 'mutation'>,
params: Omit<MutationOperationParams<TVariables, TError>, 'mutation'>,
client?: ApolloMutationClient<TResult, TVariables>,
): Promise<MutationResult<TResult>> => {
return mutateWithErrorHandling(
app,
{
mutation,
onError,
...params,
variables: params.variables,
onError: params.onError ?? onError,
context: params.context,
},
client,
);
+41 -20
View File
@@ -1,31 +1,52 @@
import { ApolloError, OperationVariables } from 'apollo-client';
import { DocumentNode } from 'graphql';
import { ErrorHandler, VueApolloQueryDefinition } from 'vue-apollo/types/options';
import { ErrorHandler, VueApolloQueryDefinition, VueApolloSubscribeToMoreOptions } from 'vue-apollo/types/options';
import { Vue } from 'vue/types/vue';
import { ApolloOperationErrorHandlerFunction } from './error';
import { OverrideAllThis, SubscribeToMoreOptionsPatched } from './types';
import { ApolloOperationErrorHandlerFunction } from './types';
export interface VueApolloQueryDefinitionPatched<TComponent extends Vue = Vue, TResult = any, TVariables = any>
extends OverrideAllThis<
Omit<VueApolloQueryDefinition<TResult, TVariables>, 'subscribeToMore' | 'variables' | 'loadingKey'>,
TComponent
> {
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 VueApolloQueryDefinitionWithoutVariablesAndSubscribeToMore<TResult = any, TVariables = any> = Omit<
VueApolloQueryDefinition<TResult, TVariables>,
'subscribeToMore' | 'variables'
>;
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 = any, TResult = any, TVariables = any>
extends OverrideAllThis<VueApolloQueryDefinitionWithoutVariablesAndSubscribeToMore<TResult, TVariables>, 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,
TComponent extends Vue = Vue,
TComponent extends Vue = Vue
> = (
error: TError,
vm: TComponent,
@@ -38,21 +59,21 @@ export type VueApolloSmartQueryOptions<
TResult = any,
TVariables = OperationVariables,
TError = ApolloError,
TComponent extends Vue = Vue,
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,
TComponent extends Vue = TApp
>(
options?: Partial<Omit<VueApolloSmartQueryOptions<TResult, TVariables, TError, TComponent>, 'query'>>,
) => VueApolloSmartQueryOptions<TResult, TVariables, TError, TComponent>;
) => VueApolloSmartQueryOptions<TResult, TVariables>;
export function createSmartQueryOptionsFunction<TResult, TVariables, TError = ApolloError, TApp extends Vue = Vue>(
query: DocumentNode,
onError?: ApolloOperationErrorHandlerFunction<TError, TApp>,
): VueApolloSmartQueryOptionsFunction<TResult, TVariables> {
): VueApolloSmartQueryOptionsFunction<TResult, TVariables, TError, TApp> {
return (options = {}) => {
const defaultErrorHandlerFn: VueApolloSmartQueryErrorHandler<TResult, TVariables, TError, TApp> = (
error: TError,
@@ -73,8 +94,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,
};
};
+6 -6
View File
@@ -2,13 +2,13 @@ import { ErrorHandler, VueApolloSubscriptionDefinition } from 'vue-apollo/types/
import { ApolloError, OperationVariables } from 'apollo-client';
import { DocumentNode } from 'graphql';
import { Vue } from 'vue/types/vue';
import { ApolloOperationErrorHandlerFunction } from './error';
import { ApolloOperationErrorHandlerFunction } from './types';
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,
};
};
+90 -26
View File
@@ -1,28 +1,92 @@
import { VueApolloSubscribeToMoreOptions } from 'vue-apollo/types/options';
type OverrideThis<F, T> = F extends (...args: infer A) => infer B ? (this: T, ...args: A) => B : F;
export type OverrideAllThis<O, T> = {
[key in keyof O]: OverrideThis<O[key], T>;
};
export 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;
import { ApolloError as BaseApolloError } from 'apollo-client';
import { Vue } from 'vue/types/vue';
import { GraphQLError as BaseGraphQLError } from 'graphql';
export type ApolloOperationContext<TAttrs = Record<string, any>> = TAttrs;
export enum ApolloErrorType {
NETWORK_ERROR = 'NETWORK_ERROR',
SERVER_ERROR = 'SERVER_ERROR',
UNAUTHORIZED_ERROR = 'UNAUTHORIZED_ERROR',
BAD_USER_INPUT = 'BAD_USER_INPUT',
INPUT_VALIDATION_ERROR = 'INPUT_VALIDATION_ERROR',
}
// Upstream type declares networkError as Error, but it can contain additional properties, we override to add these
export type ApolloError = BaseApolloError & {
networkError: ApolloNetworkError | null;
graphQLErrors: GraphQLError[];
};
export interface ApolloNetworkError extends Error {
statusCode?: number;
response?: any;
result?: {
errors: GraphQLError[];
};
}
export type GraphQLError = BaseGraphQLError & {
message: string | Record<string, any>;
};
export type ProcessedApolloError =
| NetworkError
| ServerError
| UnauthorizedError
| UserInputError
| InputValidationError;
export interface NetworkError {
type: ApolloErrorType.NETWORK_ERROR;
error: Error;
message: string;
statusCode?: number;
}
export interface ServerError {
type: ApolloErrorType.SERVER_ERROR;
error: Error;
path?: readonly (string | number)[];
message: string;
}
export interface UnauthorizedError {
type: ApolloErrorType.UNAUTHORIZED_ERROR;
error: Error;
path?: readonly (string | number)[];
message: string;
}
export interface UserInputError {
type: ApolloErrorType.BAD_USER_INPUT;
error: Error;
path?: readonly (string | number)[];
message: string;
}
export interface InputValidationError {
type: ApolloErrorType.INPUT_VALIDATION_ERROR;
error: Error;
path?: readonly (string | number)[];
message: string;
invalidArgs: string[];
violations: ValidationRuleViolation[];
}
export interface ValidationRuleViolation {
path: string[];
message: string;
value?: any;
}
export interface ApolloErrorHandlerResult {
processedErrors: ProcessedApolloError[];
validationRuleViolations?: ValidationRuleViolation[];
}
export type ApolloOperationErrorHandlerFunction<
TError = BaseApolloError,
TApp extends Vue = Vue,
TContext = ApolloOperationContext
> = (error: TError, app: TApp, context?: TContext) => ApolloErrorHandlerResult;
+14 -4
View File
@@ -15,9 +15,19 @@
"strictNullChecks": true,
"outDir": "./dist",
"baseUrl": "./",
"paths": {},
"types": ["@types/node", "@types/jest", "vue-apollo/types"]
"paths": {
},
"types": [
"@types/node",
"@types/jest",
"vue-apollo/types"
]
},
"include": ["src/**/*", "test/**/*"],
"exclude": ["node_modules"]
"include":[
"src/**/*",
"test/**/*"
],
"exclude": [
"node_modules"
]
}