data extraction from openapi defintion

This commit is contained in:
Wolfgang Ludger Hottgenroth 2021-11-19 12:39:46 +01:00
parent e616a7cb60
commit 590e9da904
Signed by: wn
GPG Key ID: 6C1E5E531E0D5D7F
4 changed files with 176 additions and 32 deletions

View File

@ -3,12 +3,13 @@ from Cheetah.Template import Template
import glob
import argparse
import os
import re
from yaml.loader import SafeLoader
from generateHelper import CsOperationNameConverter, OpenApiExtractRefType
parser = argparse.ArgumentParser(description="generate.py")
parser.add_argument('--api', '-a',
parser.add_argument('--apiDefinitionFile', '-a',
help='API definition file. Default: openapi.yaml in the current folder.',
required=False,
default='./openapi.yaml')
@ -20,35 +21,35 @@ with an additional .tmpl extension. Default: all template files recursively from
args = parser.parse_args()
with open(args.api) as schemaFile:
schema = yaml.load(schemaFile, Loader=SafeLoader)
with open(args.apiDefinitionFile) as apiDefinitionFile:
apiDefinition = yaml.load(apiDefinitionFile, Loader=SafeLoader)
schema["GENERATED_SQL_COMMENT"] = """
apiDefinition["GENERATED_SQL_COMMENT"] = """
-- ----------------------------------------
-- THIS FILE HAS BEEN GENERATED
-- DO NOT EDIT MANUALLY
-- ----------------------------------------
"""
schema["GENERATED_PYTHON_COMMENT"] = """
apiDefinition["GENERATED_PYTHON_COMMENT"] = """
# -----------------------------------------
# THIS FILE HAS BEEN GENERATED
# DO NOT EDIT MANUALLY
# -----------------------------------------
"""
schema["GENERATED_YAML_COMMENT"] = """
apiDefinition["GENERATED_YAML_COMMENT"] = """
# -----------------------------------------
# THIS FILE HAS BEEN GENERATED
# DO NOT EDIT MANUALLY
# -----------------------------------------
"""
schema["GENERATED_TS_COMMENT"] = """
apiDefinition["GENERATED_TS_COMMENT"] = """
// -----------------------------------------
// THIS FILE HAS BEEN GENERATED
// DO NOT EDIT MANUALLY
// -----------------------------------------
"""
schema["GENERATED_CS_COMMENT"] = """
apiDefinition["GENERATED_CS_COMMENT"] = """
// -----------------------------------------
// THIS FILE HAS BEEN GENERATED
// DO NOT EDIT MANUALLY
@ -56,34 +57,78 @@ schema["GENERATED_CS_COMMENT"] = """
"""
packageName = os.environ["PACKAGE_NAME"]
schema["env"] = { "packagename": packageName }
apiDefinition["env"] = { "packagename": packageName }
statementFinder = re.compile('STATEMENTBEGIN (.*) STATEMENTEND')
statementChecker = {
'get': re.compile('^select', re.IGNORECASE),
'put': re.compile('^update', re.IGNORECASE),
'delete': re.compile('^delete', re.IGNORECASE),
'post': re.compile('^insert', re.IGNORECASE)
}
operations = []
for path in schema['paths'].values():
for path in apiDefinition['paths'].values():
for (method, operation) in path.items():
#print(f"{method=}")
#print(f"{CsOperationNameConverter(operation['operationId'])=}")
# if 200 in
content = operation['responses'][200]['content']['application/json']['schema']
if ('type' in content) and (content['type'] == 'array'):
isList = True
typ = OpenApiExtractRefType(content['items']['$ref'])
resultType = OpenApiExtractRefType(content['items']['$ref'])
else:
isList = False
typ = OpenApiExtractRefType(content['$ref'])
resultType = OpenApiExtractRefType(content['$ref'])
#print(f"{content=}")
#print(f"{typ=}")
#print(f"{resultType=}")
description = None
statement = None
if 'description' in operation:
description = operation['description']
statementFinderResult = statementFinder.search(description)
if statementFinderResult:
statement = statementFinderResult.group(1)
if not statementChecker[method].match(statement):
raise Exception(f"Invalid statement {statement} for method {method}")
inputType = None
if 'requestBody' in operation:
inputType = OpenApiExtractRefType(operation['requestBody']['content']['application/json']['schema']['$ref'])
operations.append({
'description': description,
'method':method,
'func':CsOperationNameConverter(operation['operationId']),
'isList': isList,
'type': typ
'resultType': { 'apiName': resultType, 'csName': CsOperationNameConverter(resultType) } if resultType else {},
'allSelector': operation['operationId'].endswith('all'),
'byIdSelector': operation['operationId'].endswith('byid'),
'statement': statement,
'inputType': { 'apiName': inputType, 'csName': CsOperationNameConverter(inputType) } if inputType else {}
})
#print(f"{operations=}")
schema["operations"] = operations
print(f"{operations=}")
apiDefinition["operations"] = operations
types = {}
for (typeName, typeDefinition) in apiDefinition['components']['schemas'].items():
#print(f"{typeName=}")
typeProperties = []
for itemName in typeDefinition['properties']:
#print(f"{itemName=}")
typeProperties.append({
'sqlName': itemName,
'csName': itemName.capitalize()
})
types[typeName] = {
'sqlName': typeName,
'csName': typeName.capitalize(),
'properties': typeProperties
}
print(f"{types=}")
apiDefinition['types'] = types
for f in glob.glob(args.template, recursive=True):
print(f"process {f}")
tmpl = Template(file=f, searchList=[schema])
tmpl = Template(file=f, searchList=[apiDefinition])
with open(f[:-5], 'w') as outFile:
outFile.write(str(tmpl))

View File

@ -1,8 +1,11 @@
def capitalizeOnlyFirstLetter(i):
return i[0].upper() + i[1:]
def JsNameConverter(i):
return ''.join([x.capitalize() for x in i.split('_')])
return ''.join([x.capitalize() for x in i.split('_')]) if i else i
def CsOperationNameConverter(i):
return ''.join([x.capitalize() for x in i.split('.')])
return ''.join([capitalizeOnlyFirstLetter(x) for x in i.split('.')]) if i else i
def OpenApiExtractRefType(i):
e = i.split('/')

View File

@ -18,13 +18,26 @@ paths:
schema:
type: array
items:
$ref: "#/components/schemas/Test1"
$ref: "#/components/schemas/test1"
404:
description: No test1 items available
# post:
# tags: [ "Regular" ]
# operationId: Regular.test1insert
# summary: Inserts an item into table test1
# requestBody:
# description: test1
# content:
# application/json:
# schema:
# $ref: "#/components/schemas/test1"
# responses:
# 201:
# description: Your items has been inserted
/pdb/v2/test1/{id}:
get:
tags: [ "Regular" ]
operationId: Regular.test2byid
operationId: Regular.test1byid
summary: Returns one entry from table test1 by id
parameters:
- name: id
@ -38,13 +51,49 @@ paths:
content:
application/json:
schema:
$ref: "#/components/schemas/Test1"
$ref: "#/components/schemas/test1"
404:
description: No such test1 item available
/pdb/v2/test1/specificSelectName:
get:
tags: [ "Regular" ]
operationId: Regular.test1specificSelectName
summary: Returns entries from table test1 using a dedicated select statement
description:
STATEMENTBEGIN
select txt
from test1
where nr = ?
STATEMENTEND
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod
tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At
vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren,
no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet,
consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et
dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo
dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem
ipsum dolor sit amet.
requestBody:
description: specificSelectNameType
content:
application/json:
schema:
$ref: "#/components/schemas/specificSelectNameType"
responses:
200:
description: Here are your test1 items
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/specificResultType"
404:
description: No such test1 item available
components:
schemas:
Test1:
test1:
description: A test1 item
type: object
required:
@ -56,3 +105,17 @@ components:
type: string
nr:
type: integer
specificSelectNameType:
description: Specific type to defintion select condition
type: object
required:
- nr
properties:
nr:
type: integer
specificResultType:
description: Specific result type to defintion select clause
type: object
properties:
txt:
type: string

View File

@ -17,18 +17,51 @@ namespace ${env['packagename']}.Implementations
public class RegularApiImplementation : RegularApiController
{
#for $operation in $operations
public override IActionResult ${operation['func']}() {
return new ObjectResult();
#if $operation['method'] == 'get'
public override IActionResult ${operation['func']}( #slurp
#if $operation['byIdSelector']
INSERT CODE TO GET ID #slurp
#elif $operation['inputType']
[FromBody]$operation['inputType']['csName'] inputItem #slurp
#end if
) {
// Statement:
#if not $operation['statement']
// SELECT
#set $sep = ""
#for $property in $types[$operation['resultType']['apiName']]['properties']
// $sep$property['sqlName']
#set $sep = ","
#end for
// FROM $types[$operation['resultType']['apiName']]['sqlName'];
#else
// $operation['statement']
#end if
#if $operation['inputType']
// Input type mapping: $operation['inputType']['apiName'] -> $operation['inputType']['csName']
#for $property in $types[$operation['inputType']['apiName']]['properties']
// $property['sqlName'] -> $property['csName']
#end for
#end if
// Model object to use: $operation['resultType']['apiName'] -> $operation['resultType']['csName']
// Properties to map result:
#for $property in $types[$operation['resultType']['apiName']]['properties']
// $property['sqlName'] -> $property['csName']
#end for
#if $operation['isList']
// result must be mapped in list
#end if
return new ObjectResult();
}
#elif $operation['method'] == 'post'
bla
#else
#raise Exception('invalid method')
#end if
#end for
public override IActionResult CyclesStartCycle([FromBody]MCycle mCycle) {
return StatusCode(200, default(MCycle));
}
public override IActionResult CyclesStopCycle([FromBody]CycleId cycleId) {
throw new NotImplementedException();
}
}
}