initial commit

This commit is contained in:
Martin Karkowski 2020-08-19 08:36:59 +02:00
parent cfb30180b5
commit 8acf2b454c
17 changed files with 7303 additions and 0 deletions

8
api/README.md Normal file
View File

@ -0,0 +1,8 @@
# ToDo
Interfaces in here should be generated based on the Modules in pages.
## Create frontend
- Using `openapi-generator`:
`npx openapi-generator generate -i http://localhost:3000/api/api-docs -g typescript-inversify -o ./dist/generated`

51
api/helloWorldService.ts Normal file
View File

@ -0,0 +1,51 @@
import { Operation } from "express-openapi";
// ./api-v1/paths/worlds.js
export default function () {
let operations = {
POST
};
// Define the Action.
async function POST(req, res, next) {
res.status(200).json('HELLO ' + req.body.greeter);
}
// Define the apiDoc for this specific Funtion
POST.apiDoc = {
summary: 'Says Hello to the greater',
operationId: 'getHello',
parameters: [
{
name: "body",
in: "body",
description: "Body of the Message",
required: true,
schema: {
description: "The Body. Containing The Greeter Object",
properties: {
greeter: {
type: "string"
}
}
}
}
],
responses: {
200: {
description: 'The Greeting Message',
schema: {
type: 'string',
}
},
default: {
description: 'An error occurred',
schema: {
additionalProperties: true
}
}
}
} as Operation;
return operations;
}

View File

@ -0,0 +1,12 @@
import {generate} from 'openapi-typescript-codegen';
import { startBackend } from '../src/startBackend';
// Start the Backend
const result = startBackend({port: 3001});
generate({
input: 'http://localhost:3001' + result.definitionUri,
output: './pages/interface'
});
result.close()

2
next-env.d.ts vendored Normal file
View File

@ -0,0 +1,2 @@
/// <reference types="next" />
/// <reference types="next/types/global" />

6861
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

49
package.json Normal file
View File

@ -0,0 +1,49 @@
{
"name": "nope-backend",
"version": "1.0.0",
"description": "Nodejs Backend, combining nextjs with openapi",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"generate-frontend-interfaces": "openapi --input http://localhost:3000/api/api-docs --output ./pages/interfaces",
"compile-backend": "tsc -p ./tsconfigBackend.json",
"dev": "next dev",
"build": "next build",
"start": "next start"
},
"repository": {
"type": "git",
"url": "git+https://github.com/anti-held-333/nope-backend.git"
},
"keywords": [
"nextjs",
"openapi"
],
"author": "anti-held-333",
"license": "MIT",
"bugs": {
"url": "https://github.com/anti-held-333/nope-backend/issues"
},
"homepage": "https://github.com/anti-held-333/nope-backend#readme",
"dependencies": {
"bootstrap": "^4.5.2",
"cors": "^2.8.5",
"express": "^4.17.1",
"express-openapi": "^7.0.0",
"inversify": "^5.0.1",
"lodash": "^4.17.20",
"next": "^9.5.2",
"react": "^16.13.1",
"react-bootstrap": "^1.3.0",
"react-dom": "^16.13.1"
},
"devDependencies": {
"@openapitools/openapi-generator-cli": "^1.0.15-4.3.1",
"@types/express": "^4.17.7",
"@types/lodash": "^4.14.159",
"@types/node": "^14.6.0",
"@types/react": "^16.9.46",
"openapi-typescript-codegen": "^0.4.10",
"typescript": "^3.9.7"
}
}

5
pages/_app.tsx Normal file
View File

@ -0,0 +1,5 @@
import 'bootstrap/dist/css/bootstrap.min.css';
export default function App({ Component, pageProps }) {
return <Component {...pageProps} />
}

6
pages/index.tsx Normal file
View File

@ -0,0 +1,6 @@
export default function Home(){
return (
<h1>Welcome</h1>
)
}

25
specs/apiDoc.ts Normal file
View File

@ -0,0 +1,25 @@
/**
* Function to generate the Base Api Documentation
* @param title The Title of the API
* @param version The Version
* @param basePath A Basepath for the API
*/
export function apiDoc(title: string, version: string, basePath: string){
// Default API Doc
const apiDoc = {
swagger: '2.0',
basePath: basePath || '/v1',
info: {
title,
version
},
definitions: {
},
paths: {}
};
return apiDoc;
}
export default apiDoc;

4
specs/enrichDoc.ts Normal file
View File

@ -0,0 +1,4 @@
export function addService(){
}

View File

@ -0,0 +1,50 @@
import { runMiddleware } from './runMiddleware';
import { Application } from 'express';
import { assignIn } from 'lodash';
export interface ICallOptions {
method: "post",
headers?: { 'Content-Type': 'application/json' },
body: {
[index: string]: any
}
}
/**
* Function that will generate an async accessor Function.
* @param app the provided Express App.
*/
export function getBackendAccesors(app: Application){
// Create a Run-Middle-Ware
runMiddleware(app);
// Define a apiCall Function, which could be used to perform internal
// Request on the API.
function apiCall<T>(url: string, options: ICallOptions){
return new Promise<T>((resolve, reject) => {
// Define the Default Options
const defaults: Partial<ICallOptions> = {
headers: { 'Content-Type': 'application/json' }
}
// Mix the Options.
const opts: ICallOptions = assignIn(defaults, options);
// Perform the Call
(app as any).runMiddleware(url,opts,(responseCode: number,body: T) => {
// Based on the Response code, decide whether the call was a Fail or not.
if (responseCode === 200){
// If everything is fine,
// Just return the body.
return resolve(body);
} else {
const error = new Error('Call Ended with responseCode = ' + responseCode.toString());
return reject(error)
}
})
})
}
return apiCall;
}

3
src/index.ts Normal file
View File

@ -0,0 +1,3 @@
import { startBackend } from "./startBackend";
startBackend({port: 3001});

109
src/runMiddleware.ts Normal file
View File

@ -0,0 +1,109 @@
// Adapted File from 'run-middleware'
import * as _ from 'lodash';
export function runMiddleware(app) {
app.use((req, res, next) => {
req.runMiddleware = (path, options, callback) => {
if (_.isFunction(options)) {
callback = options;
options = {};
}
options.original_req = req;
options.original_res = res;
app.runMiddleware(path, options, callback);
};
next();
});
if (app.runMiddleware) return; // Do not able to add us twice
app.runMiddleware = function (path, options, callback) {
if (callback) callback = _.once(callback);
if (typeof options == "function") {
callback = options;
options = null;
}
options = options || {};
options.url = path;
var new_req, new_res;
if (options.original_req) {
new_req = options.original_req;
for (var i in options) {
if (i == "original_req") continue;
new_req[i] = options[i];
}
} else {
new_req = createReq(path, options);
}
new_res = createRes(callback);
app(new_req, new_res);
};
/* end - APP.runMiddleware*/
};
function createReq(path, options) {
if (!options) options = {};
var req = _.extend(
{
method: "GET",
host: "",
cookies: {},
query: {},
url: path,
headers: {},
},
options
);
req.method = req.method.toUpperCase();
// req.connection=_req.connection
return req;
}
function createRes(callback) {
var res: any = {
_removedHeader: {},
};
// res=_.extend(res,require('express/lib/response'));
var headers = {};
var code = 200;
res.set = res.header = function (x, y){
if (arguments.length === 2) {
res.setHeader(x, y);
} else {
for (var key in x) {
res.setHeader(key, x[key]);
}
}
return res;
}
res.setHeader = function (x, y) {
headers[x] = y;
headers[x.toLowerCase()] = y;
return res;
};
res.get = function (x) {
return headers[x]
}
res.redirect = function (_code, url) {
if (!_.isNumber(_code)) {
code = 301;
url = _code;
} else {
code = _code;
}
res.setHeader("Location", url);
res.end();
// callback(code,url)
};
res.status = function (number) {
code = number;
return res;
};
res.end = res.send = res.write = function (data) {
if (callback) callback(code, data, headers);
// else if (!options.quiet){
// _res.send(data)
// }
};
return res;
}

51
src/startBackend.ts Normal file
View File

@ -0,0 +1,51 @@
import { apiDoc } from '../specs/apiDoc';
import * as express from "express";
import * as bodyParser from "body-parser";
import { initialize } from "express-openapi";
import { getBackendAccesors } from './getBackendAccessors';
import { assignIn } from 'lodash';
export function startBackend(options: {
port?: number,
basePath?: string,
}= {}) {
const app: express.Application = (express as any)();
// Define the Default Options
const defaults = {
port: 3001,
basePath: '/api'
}
// Mix the Options.
const opts = assignIn(defaults, options);
app.use(bodyParser.json());
initialize({
apiDoc: apiDoc('Test API', '0.0.1', opts.basePath),
app,
paths: './dist/api',
routesGlob: '**/*.{ts,js}',
routesIndexFileRegExp: /(?:index)?\.[tj]s$/
});
app.use(((err, req, res, next) => {
res.status(err.status).json(err);
}) as express.ErrorRequestHandler);
const server = app.listen(opts.port);
const accessor = getBackendAccesors(app);
return {
app,
// Accessor for the Server
accessor,
// Function to extract the Definitions.
definitionUri: opts.basePath + '/api-docs',
// Function to Close the Server
close() {
server.close();
}
}
}

29
tsconfig.json Normal file
View File

@ -0,0 +1,29 @@
{
"compilerOptions": {
"target": "es5",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"strict": false,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve"
},
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx"
],
"exclude": [
"node_modules"
]
}

35
tsconfigBackend.json Normal file
View File

@ -0,0 +1,35 @@
{
"compilerOptions": {
"target": "es2017",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"allowUnusedLabels": true,
"charset": "utf-8",
"experimentalDecorators": true,
"module": "commonjs",
"moduleResolution": "node",
"noFallthroughCasesInSwitch": false,
"noImplicitReturns": true,
"outDir": "./dist",
"pretty": false,
"removeComments": true,
"rootDir": "./",
"stripInternal": true
},
"include": [
"next-env.d.ts",
"**/*.ts",
"helpers",
"api",
"src"
],
"exclude": [
"node_modules",
"pages",
".next"
]
}

3
types/IJSONSchema.ts Normal file
View File

@ -0,0 +1,3 @@
export interface IJSONSchema {
}