Skip to content

Commit baa6def

Browse files
authored
Merge pull request #131 from howToCodeWell/129-openapi-generator
129 openapi generator Closes #129
2 parents 2439a8b + ffcba93 commit baa6def

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

61 files changed

+1624
-552
lines changed

.github/workflows/api-client.yaml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,27 @@ jobs:
3535
working-directory: ./api-client
3636
run: cp .env.sample .env
3737

38+
- name: Download a openapi_spec
39+
uses: dawidd6/action-download-artifact@v2
40+
with:
41+
name: openapi_spec_artifact
42+
workflow: openapi.yaml
43+
workflow_conclusion: success
44+
path: ./docs/generated
45+
46+
- name: Generating Typescript API
47+
working-directory: ./api-client
48+
run: yarn run generate-api
49+
3850
- name: Lint
3951
working-directory: ./api-client
4052
run: yarn run lint
53+
shell: bash
4154

4255
- name: Unit tests
4356
working-directory: ./api-client
4457
run: yarn run test
58+
shell: bash
4559

4660
after-tests:
4761
needs: tests # run after tests

.github/workflows/openapi.yaml

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
# A lot of this was inspired by https://brunoscheufler.com/blog/2022-04-24-required-github-actions-jobs-in-a-monorepo
2+
3+
name: OpenAPI spec
4+
5+
on:
6+
pull_request:
7+
paths:
8+
- 'api/src/Entity'
9+
- '.github/workflows/openapi.yaml'
10+
jobs:
11+
change-detection:
12+
runs-on: ubuntu-latest
13+
outputs:
14+
openapi: ${{ steps.changes.outputs.openapi }}
15+
steps:
16+
- uses: dorny/paths-filter@v2
17+
id: changes
18+
with:
19+
list-files: shell
20+
filters: |
21+
openapi:
22+
- 'api/src/Entity/**'
23+
build:
24+
needs: change-detection
25+
if: needs.change-detection.outputs.openapi == 'true'
26+
runs-on: ubuntu-latest
27+
steps:
28+
- uses: actions/checkout@v2
29+
30+
- name: Start the containers
31+
working-directory: ./api
32+
run: docker-compose -f docker-compose.yml -f docker-compose.test.yml up -d --build
33+
34+
- name: Install composer packages
35+
working-directory: ./api
36+
run: docker-compose -f docker-compose.yml -f docker-compose.test.yml exec -T api composer install
37+
38+
- name: Run Database migrations
39+
working-directory: ./api
40+
run: docker-compose -f docker-compose.yml -f docker-compose.test.yml exec -T api bin/console --no-interaction doctrine:migration:migrate
41+
42+
- name: Generate openapi.yaml
43+
working-directory: ./api
44+
run: docker-compose exec -T api bash -c "bin/console api:openapi:export --yaml" > ../docs/generated/openapi.yaml
45+
46+
- name: Archive openapi artifacts
47+
uses: actions/upload-artifact@v3
48+
with:
49+
name: openapi_spec_artifact
50+
path: |
51+
docs/generated
52+
53+
- name: Stop containers
54+
working-directory: ./api
55+
if: always()
56+
run: docker-compose -f docker-compose.yml -f docker-compose.test.yml down
57+
58+
after-build:
59+
needs: build # run after tests
60+
runs-on: ubuntu-latest
61+
if: success() # only run when all test have passed
62+
# store success output flag for ci job
63+
outputs:
64+
success: ${{ steps.setoutput.outputs.success }}
65+
steps:
66+
- id: setoutput
67+
run: echo "::set-output name=success::true"
68+
69+
dummy-step:
70+
runs-on: ubuntu-latest
71+
needs: change-detection
72+
# runs if API was not changed
73+
if: needs.change-detection.outputs.openapi == 'false'
74+
outputs:
75+
success: ${{ steps.setoutput.outputs.success }}
76+
steps:
77+
- id: setoutput
78+
run: echo "::set-output name=success::true"
79+
80+
81+
# step that will be used for required status check
82+
# make sure it has a unique name! this will always run, but
83+
# after the tests & after-tests steps or the dummy step in case
84+
# the API was not modified
85+
ci_openapi:
86+
runs-on: ubuntu-20.04
87+
if: always()
88+
needs: [build, after-build, dummy-step]
89+
steps:
90+
- run: |
91+
passed="${{ needs.after-build.outputs.success || needs.dummy-step.outputs.success }}"
92+
if [[ $passed == "true" ]]; then
93+
echo "Build passed"
94+
exit 0
95+
else
96+
echo "Build failed"
97+
exit 1
98+
fi

api-client/.eslintrc.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
module.exports = {
22
extends: [
33
'eslint:recommended',
4-
'plugin:@typescript-eslint/recommended'
4+
'plugin:@typescript-eslint/recommended',
5+
'plugin:import/recommended',
6+
'plugin:import/typescript'
57
],
68
parser: '@typescript-eslint/parser',
79
plugins: [
8-
'@typescript-eslint'
10+
'@typescript-eslint',
11+
'import'
912
],
1013
root: true,
1114
ignorePatterns: [

api-client/.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
node_modules
22
coverage
3-
.env
3+
.env
4+
src/generated

api-client/jest.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import type {Config} from '@jest/types';
22

33
const config: Config.InitialOptions = {
44
collectCoverage: true,
5-
collectCoverageFrom: ["./src/**", "./mockData/**", "!./src/routes.json"],
5+
collectCoverageFrom: ["./src/**", "!./src/generated/openapi/**", "./mockData/**", "!./src/routes.json"],
66
testPathIgnorePatterns: ["./src/routes.json"],
77
// TODO #88 https://github.com/howToCodeWell/code-quiz/issues/88
88
// This needs to be un-commented once we reach 90% code coverage

api-client/openapitools.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"$schema": "./node_modules/@openapitools/openapi-generator-cli/config.schema.json",
3+
"spaces": 2,
4+
"generator-cli": {
5+
"version": "6.2.0"
6+
}
7+
}

api-client/package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,18 @@
99
"scripts": {
1010
"start": "npx ts-node src/server.ts",
1111
"lint": "eslint .",
12-
"test": "jest"
12+
"test": "jest",
13+
"generate-api": "rm -rf src/generated/openapi; openapi-generator-cli generate -i ../docs/generated/openapi.yaml -g typescript-axios -o src/generated/openapi --additional-properties=npmName=restClient,supportsES6=true,npmVersion=8.19.1,withInterfaces=true,typescriptThreePlus=true"
1314
},
1415
"devDependencies": {
16+
"@openapitools/openapi-generator-cli": "^2.5.2",
1517
"@types/jest": "^29.0.3",
1618
"@types/json-server": "^0.14.4",
1719
"@typescript-eslint/eslint-plugin": "^5.38.1",
1820
"@typescript-eslint/parser": "^5.38.1",
1921
"eslint": "^8.24.0",
22+
"eslint-import-resolver-typescript": "^3.5.1",
23+
"eslint-plugin-import": "^2.26.0",
2024
"jest": "^29.0.3",
2125
"json-server": "^0.17.0",
2226
"ts-jest": "^29.0.2"

api-client/src/api/apiClient.ts

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,23 @@
1-
import axios from "axios";
21
import * as dotenv from "dotenv"
2+
3+
import {Configuration, QuestionApi, QuizApi} from "../generated/openapi/"
4+
35
dotenv.config()
46

5-
export const axiosClient = axios.create({
6-
baseURL: `${process.env.API_BASE_PATH}:${process.env.API_PORT}`,
7-
headers: {
8-
'Accept': 'application/json',
9-
'Content-Type': 'application/json'
7+
const configuration = new Configuration({
8+
basePath: `${process.env.API_BASE_PATH}:${process.env.API_PORT}`,
9+
baseOptions: {
10+
headers: {
11+
'Accept': 'application/json',
12+
'Content-Type': 'application/json'
13+
}
1014
}
11-
});
15+
})
16+
17+
const apiClient = {
18+
quizAPI: new QuizApi(configuration),
19+
questionAPI: new QuestionApi(configuration),
20+
configuration: configuration
21+
}
1222

13-
export default {
14-
axiosClient
15-
};
23+
export default apiClient

api-client/src/api/mockClient.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import * as dotenv from "dotenv"
22
import * as jsonServer from "json-server"
3-
import data from "../mockData"
43
import routes from "../routes.json"
4+
import data from "../mockData";
55

66
export function mockApp() {
77
dotenv.config()
@@ -14,6 +14,16 @@ export function mockApp() {
1414
const router = jsonServer.router(data)
1515
const middlewares = jsonServer.defaults()
1616

17+
server.get('/quiz/*', (req, res) => {
18+
const id = (req.params[0])
19+
res.jsonp(data.quiz[id -1])
20+
})
21+
22+
server.get('/question/*', (req, res) => {
23+
const id = (req.params[0])
24+
res.jsonp(data.question[id -1])
25+
})
26+
1727
server.use(middlewares)
1828
server.use(router)
1929
return server.listen(process.env.API_PORT)
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import apiClient from "../apiClient";
2+
3+
export function getById(id: string) {
4+
return apiClient.questionAPI.apiQuestionIdGet(id)
5+
}

0 commit comments

Comments
 (0)