Skip to content
This repository has been archived by the owner on Apr 21, 2024. It is now read-only.

(@mikojs/graphql) Add new package #692

Merged
merged 21 commits into from Oct 27, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 6 additions & 0 deletions server/graphql/.npmignore
@@ -0,0 +1,6 @@
# babel
src

# flow
flow-typed/npm
.flowconfig
17 changes: 17 additions & 0 deletions server/graphql/README.md
@@ -0,0 +1,17 @@
# [@mikojs/graphql][website] · <!-- badges.start -->[![npm][npm-image]][npm-link] ![npm-size][npm-size-image]

[npm-image]: https://img.shields.io/npm/v/@mikojs/graphql.svg
[npm-link]: https://www.npmjs.com/package/@mikojs/graphql
[npm-size-image]: https://img.shields.io/bundlephobia/minzip/@mikojs/graphql.svg

<!-- badges.end -->

[website]: https://mikojs.github.io/core/graphql

Use to build a graphql server.

## Install

```sh
yarn add @mikojs/graphql
```
167 changes: 167 additions & 0 deletions server/graphql/flow-typed/node-fetch_v2.x.x.js
@@ -0,0 +1,167 @@
/* eslint-disable */

// flow-typed signature: 800c99f4687ac083d3ed2dd6b9ee9457
// flow-typed version: 711a5f887a/node-fetch_v2.x.x/flow_>=v0.104.x

declare module 'node-fetch' {
import type http from 'http';
import type https from 'https';
import type { URL } from 'url';
import type { Readable } from 'stream';

declare export type AbortSignal = {
+aborted: boolean,
+onabort: (event?: { ... }) => void,

+addEventListener: (name: string, cb: () => mixed) => void,
+removeEventListener: (name: string, cb: () => mixed) => void,
+dispatchEvent: (event: { ... }) => void,
...
};

declare export class Request mixins Body {
constructor(
input: string | { href: string, ... } | Request,
init?: RequestInit,
): this;
context: RequestContext;
headers: Headers;
method: string;
redirect: RequestRedirect;
referrer: string;
url: string;

// node-fetch extensions
agent: http.Agent | https.Agent;
compress: boolean;
counter: number;
follow: number;
hostname: string;
port: number;
protocol: string;
size: number;
timeout: number;
}

declare type HeaderObject = { [index: string]: string, ... };

declare type RequestInit = {|
body?: BodyInit,
headers?: HeaderObject,
method?: string,
redirect?: RequestRedirect,
signal?: AbortSignal | null,

// node-fetch extensions
agent?: (URL => http.Agent | https.Agent) | http.Agent | https.Agent | null,
compress?: boolean,
follow?: number,
size?: number,
timeout?: number,
|};

declare export interface FetchError extends Error {
name: 'FetchError';
type: string;
code: ?number;
errno: ?number;
}

declare export interface AbortError extends Error {
name: 'AbortError';
type: 'aborted';
}

declare type RequestContext =
| 'audio'
| 'beacon'
| 'cspreport'
| 'download'
| 'embed'
| 'eventsource'
| 'favicon'
| 'fetch'
| 'font'
| 'form'
| 'frame'
| 'hyperlink'
| 'iframe'
| 'image'
| 'imageset'
| 'import'
| 'internal'
| 'location'
| 'manifest'
| 'object'
| 'ping'
| 'plugin'
| 'prefetch'
| 'script'
| 'serviceworker'
| 'sharedworker'
| 'subresource'
| 'style'
| 'track'
| 'video'
| 'worker'
| 'xmlhttprequest'
| 'xslt';
declare type RequestRedirect = 'error' | 'follow' | 'manual';

declare export class Headers {
append(name: string, value: string): void;
delete(name: string): void;
forEach(callback: (value: string, name: string) => void): void;
get(name: string): string;
getAll(name: string): Array<string>;
has(name: string): boolean;
raw(): { [k: string]: string[], ... };
set(name: string, value: string): void;
}

declare export class Body {
buffer(): Promise<Buffer>;
json(): Promise<any>;
json<T>(): Promise<T>;
text(): Promise<string>;
body: stream$Readable;
bodyUsed: boolean;
}

declare export class Response mixins Body {
constructor(body?: BodyInit, init?: ResponseInit): this;
clone(): Response;
error(): Response;
redirect(url: string, status: number): Response;
headers: Headers;
ok: boolean;
status: number;
statusText: string;
size: number;
timeout: number;
type: ResponseType;
url: string;
}

declare type ResponseType =
| 'basic'
| 'cors'
| 'default'
| 'error'
| 'opaque'
| 'opaqueredirect';

declare interface ResponseInit {
headers?: HeaderInit;
status: number;
statusText?: string;
}

declare type HeaderInit = Headers | Array<string>;
declare type BodyInit = string | null | Buffer | Blob | Readable;

declare export default function fetch(
url: string | Request,
init?: RequestInit,
): Promise<Response>;
}
26 changes: 26 additions & 0 deletions server/graphql/package.json
@@ -0,0 +1,26 @@
{
"name": "@mikojs/graphql",
"description": "Use to build a graphql server.",
"license": "MIT",
"author": "HsuTing <hsuting0106@gmail.com>",
"homepage": "https://mikojs.github.io/core/graphql",
"repository": "https://github.com/mikojs/core/tree/master/server/graphql",
"version": "1.12.0",
"main": "./lib/index.js",
"dependencies": {
"@babel/runtime": "^7.11.2",
"@graphql-tools/schema": "^7.0.0",
"@mikojs/server": "^1.12.0",
"core-js": "^3.6.5",
"express-graphql": "^0.11.0",
"graphql": "^15.3.0"
},
"devDependencies": {
"flow-bin": "^0.135.0",
"flow-typed": "^3.2.1"
},
"keywords": ["graphql", "mikojs", "server"],
"publishConfig": {
"access": "public"
}
}
15 changes: 15 additions & 0 deletions server/graphql/src/__tests__/__ignore__/index.js
@@ -0,0 +1,15 @@
// @flow

export default {
typeDefs: `
type Query {
version: String!
}
`,
Query: {
/**
* @return {string} - version
*/
version: (): string => '1.0.0',
},
};
9 changes: 9 additions & 0 deletions server/graphql/src/__tests__/__ignore__/node.js
@@ -0,0 +1,9 @@
// @flow

export default {
typeDefs: `
interface Node {
id: ID!
}
`,
};
39 changes: 39 additions & 0 deletions server/graphql/src/__tests__/index.js
@@ -0,0 +1,39 @@
// @flow

import path from 'path';

import testingServer, { type fetchResultType } from '../testingServer';

describe('router', () => {
beforeAll(async () => {
await testingServer.run(path.resolve(__dirname, './__ignore__'));
});

test('fetch', async () => {
expect(
await testingServer
.fetch('/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
query: `
{
version
}
`,
}),
})
.then((res: fetchResultType) => res.json()),
).toEqual({
data: {
version: '1.0.0',
},
});
});

afterAll(() => {
testingServer.close();
});
});
38 changes: 38 additions & 0 deletions server/graphql/src/index.js
@@ -0,0 +1,38 @@
// @flow

import {
type IncomingMessage as IncomingMessageType,
type ServerResponse as ServerResponseType,
} from 'http';

import {
graphqlHTTP,
type OptionsData as OptionsDataType,
} from 'express-graphql';
import { makeExecutableSchema } from '@graphql-tools/schema';

import server, { type middlewareType } from '@mikojs/server';

import buildCache, { type cacheType } from './utils/buildCache';

type resType = ServerResponseType & {| json?: (data: mixed) => void |};

/**
* @param {string} folderPath - folder path
* @param {string} prefix - pathname prefix
* @param {OptionsDataType} options - graphql middleware options
*
* @return {middlewareType} - router middleware
*/
export default (
folderPath: string,
prefix?: string,
options?: $Diff<OptionsDataType, {| schema: mixed |}>,
): middlewareType<> =>
server.mergeDir(
folderPath,
prefix,
buildCache,
)((cache: cacheType): middlewareType<IncomingMessageType, resType> =>
graphqlHTTP({ ...options, schema: makeExecutableSchema(cache) }),
);
20 changes: 20 additions & 0 deletions server/graphql/src/testingServer.js
@@ -0,0 +1,20 @@
// @flow

import testingServer, {
type fetchResultType as testingServerFetchResultType,
} from '@mikojs/server/lib/testingServer';

import graphql from './index';

export type fetchResultType = testingServerFetchResultType;

export default {
...testingServer,

/**
* @param {string} folderPath - folder path
*/
run: async (folderPath: string) => {
await testingServer.run(graphql(folderPath));
},
};
22 changes: 22 additions & 0 deletions server/graphql/src/utils/__tests__/buildCache.js
@@ -0,0 +1,22 @@
// @flow

import buildCache from '../buildCache';

describe('build cache', () => {
test.each`
info | exists | expected
${'add a.js'} | ${true} | ${`[requireModule(path.resolve(__filename, 'a.js'))]`}
${'remove a.js'} | ${false} | ${'[]'}
`(
'$info',
({ exists, expected }: {| exists: boolean, expected: string |}) => {
expect(
buildCache({
exists,
filePath: 'a.js',
pathname: '/a',
}),
).toMatch(expected);
},
);
});