-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[E4E-30]: Port of existing code from private repo
- Loading branch information
Showing
24 changed files
with
47,413 additions
and
132 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
node_modules | ||
build | ||
dist |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
const baseConfig = { | ||
extends: ['@encoura/eslint-config'], | ||
settings: { | ||
'import/resolver': { | ||
'babel-plugin-root-import': {}, | ||
typescript: { | ||
project: ['tsconfig.json', 'test/tsconfig.json'], | ||
}, | ||
}, | ||
}, | ||
}; | ||
|
||
const tsConfig = { | ||
extends: ['@encoura/eslint-config'], | ||
files: ['**/*.ts'], | ||
parser: '@typescript-eslint/parser', | ||
parserOptions: { | ||
project: ['tsconfig.json', 'test/tsconfig.json'], | ||
}, | ||
plugins: ['@typescript-eslint', 'prettier'], | ||
rules: { | ||
...baseConfig.rules, | ||
}, | ||
}; | ||
|
||
module.exports = { ...baseConfig, overrides: [tsConfig] }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
node_modules | ||
build |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,26 +1,75 @@ | ||
# Apollo Rest utils | ||
|
||
Encoura's solution to integrating rest APIs with [Apollo](https://www.apollographql.com/docs/) and [Apollo Client](https://www.apollographql.com/docs/react/). | ||
Encoura's solution to integrating rest APIs with | ||
[Apollo](https://www.apollographql.com/docs/) and | ||
[Apollo Client](https://www.apollographql.com/docs/react/). | ||
|
||
These utils build on top of the great work of [apollo-link-rest](https://www.apollographql.com/docs/react/api/link/apollo-link-rest/). | ||
These utils build on top of the great work of | ||
[apollo-link-rest](https://www.apollographql.com/docs/react/api/link/apollo-link-rest/). | ||
|
||
This library provides helper functions and generators for making integration with REST apis and Apollo in a TypeScript application. | ||
This library provides helper functions and generators for making integration | ||
with REST apis and Apollo in a TypeScript application. | ||
|
||
## Features | ||
|
||
* A command line utiltity that takes a swagger file/url, and automatically generates input and output types, and endpoint definitions that can be used to make integration with `apollo-link-rest` much easier. | ||
* Wrapper functions for common GraphQL operations that allow you to pass in pure GraphQL, and enables the input variables and the result to be strongly typed based on the swagger definition. | ||
* Automatically checks your GraphQL at runtime and will throw exceptions if your GraphQL fields do not match the endpoint definition. | ||
* A command line utiltity that takes a swagger file/url, and automatically | ||
generates input and output types, and endpoint definitions that can be used | ||
to make integration with `apollo-link-rest` much easier. | ||
* Wrapper functions for common GraphQL operations that allow you to pass in | ||
pure GraphQL, and enables the input variables and the result to be strongly | ||
typed based on the swagger definition. | ||
* Automatically checks your GraphQL at runtime and will throw exceptions if | ||
your GraphQL fields do not match the endpoint definition. | ||
|
||
## Usage | ||
|
||
TODO | ||
From the command line you can generate definitions for endpoints: | ||
|
||
`npx apollo-rest-utils <path_to_swagger_file_or_url> <output_directory_where_you_want_the_files>` | ||
|
||
Then you can use those definitions to make GraphQL calls within an Apollo context: | ||
|
||
```TypeScript | ||
import { wrapRestQuery } from 'apollo-rest-utils'; | ||
|
||
import ROUTES from 'path/to/directory_specified_in_cli_call/__generatedRestEndpoints'; | ||
|
||
const userSearch = (searchPattern: string) => { | ||
const wrappedRestQuery = wrapRestQuery<'users'>(); | ||
|
||
const { data, loading } = wrappedRestQuery( | ||
gql` | ||
query RenderUserSearchQuery($search: String!) { | ||
user(search: $search) { | ||
id | ||
name | ||
city | ||
state | ||
} | ||
} | ||
`, | ||
{ | ||
endpoint: ROUTES.GET.USER_SEARCH, | ||
skip: !searchPattern, | ||
variables: { | ||
search: searchPattern, | ||
}, | ||
}, | ||
); | ||
|
||
return data?.users ?? []; | ||
} | ||
``` | ||
|
||
## Releasing | ||
|
||
After making any changes and merging them to main, please do the following: | ||
|
||
* Create a new branch from main and run `npm run update:version` | ||
* Verify the `CHANGELOG.md` generated changes | ||
* Commit, push, and merge to main. | ||
* Create a new [release](https://github.com/nrccua/apollo-rest-utils/releases/new) using the tag generated in the previous steps | ||
* Use the `Auto-generate release notes` button to generate the release notes, and add any context you may deem necessary. | ||
* Create a new | ||
[release](https://github.com/nrccua/apollo-rest-utils/releases/new) using | ||
the tag generated in the previous steps | ||
* Use the `Auto-generate release notes` button to generate the release notes, | ||
and add any context you may deem necessary. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
const defaultConfig = require('@encoura/eslint-config/jest.config'); | ||
const os = require('os'); | ||
|
||
module.exports = { | ||
...defaultConfig, | ||
collectCoverageFrom: ['<rootDir>/lib/**/*.ts', '!<rootDir>/lib/index.ts'], | ||
coverageThreshold: { | ||
global: { | ||
branches: 100, | ||
functions: 100, | ||
lines: 100, | ||
statements: 100, | ||
}, | ||
}, | ||
globals: { | ||
'ts-jest': { | ||
diagnostics: false, | ||
tsconfig: './tsconfig.json', | ||
}, | ||
}, | ||
maxWorkers: os.cpus().length / 2, | ||
preset: 'ts-jest', | ||
roots: ['<rootDir>/lib'], | ||
transform: { | ||
'^.+\\.ts$': 'ts-jest', | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
import { randomUUID } from 'crypto'; | ||
import fs from 'fs'; | ||
import path from 'path'; | ||
|
||
import SwaggerParser from '@apidevtools/swagger-parser'; | ||
|
||
import { generateTypes, generateTypescript, normalizeName, pathToType } from '.'; | ||
|
||
const buildFolder = path.join('.', 'build', 'tests'); | ||
|
||
function createBuildFolder(): void { | ||
if (!fs.existsSync(buildFolder)) { | ||
fs.mkdirSync(buildFolder, { recursive: true }); | ||
} | ||
} | ||
function deleteBuildFolder(): void { | ||
if (fs.existsSync(buildFolder)) { | ||
fs.rmdirSync(buildFolder, { recursive: true }); | ||
} | ||
} | ||
|
||
function doTestImport(tsData: string): unknown { | ||
const filename = `${randomUUID()}.ts`; | ||
|
||
fs.writeFileSync(path.join(buildFolder, filename), tsData); | ||
|
||
// eslint-disable-next-line max-len | ||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, security/detect-non-literal-require, import/no-dynamic-require, global-require, @typescript-eslint/no-var-requires | ||
const endpoints = require(`../../build/tests/${filename}`); | ||
expect(endpoints).toBeTruthy(); // Probably a better test here, but I don't know what it would be | ||
return endpoints; | ||
} | ||
|
||
describe('generateTypescript', () => { | ||
const testSwaggerPath = './test/test_data/generateRoutes/'; | ||
|
||
beforeAll(() => { | ||
createBuildFolder(); | ||
}); | ||
|
||
afterAll(() => { | ||
deleteBuildFolder(); | ||
}); | ||
|
||
it.each(fs.readdirSync(testSwaggerPath).map(d => path.join(testSwaggerPath, d)))('can parse the swagger document %s', async apiPath => { | ||
const api = await SwaggerParser.validate(apiPath); | ||
|
||
const typeImport = await generateTypes(apiPath, path.join(buildFolder, `${randomUUID()}_types.ts`)); | ||
|
||
const tsData = generateTypescript(api, typeImport).replace('apollo-rest-utils', '../../lib'); | ||
|
||
expect(tsData).toBeTruthy(); // Not empty, null, or undefined | ||
|
||
doTestImport(tsData); | ||
}); | ||
}); | ||
|
||
describe('normalizeName', () => { | ||
it.each([ | ||
['/login', 'LOGIN'], | ||
['login', 'LOGIN'], | ||
['/high-schools/search', 'HIGH_SCHOOLS_SEARCH'], | ||
['/invitations/{studentKey}/users/{mentorEmail}', 'INVITATIONS_BY_STUDENTKEY_USERS_BY_MENTOREMAIL'], | ||
['/{userId}/{studentKey}/{email}', 'BY_USERID_BY_STUDENTKEY_BY_EMAIL'], | ||
])('path %s produces name %s', (pathName, expectedEndpointName) => { | ||
expect(normalizeName(pathName)).toEqual(expectedEndpointName); | ||
}); | ||
}); | ||
|
||
describe('pathToType', () => { | ||
it.each([ | ||
['/login', 'LoginResponse'], | ||
['/high-schools/search', 'SearchResponse'], | ||
['/invitations/{studentKey}/users/{mentorEmail}', 'UsersResponse'], | ||
])('path %s produces type name %s', (pathName, expectedTypeName) => { | ||
expect(pathToType(pathName)).toEqual(expectedTypeName); | ||
}); | ||
}); |
Oops, something went wrong.