data extraction from openapi defintion
This commit is contained in:
parent
e616a7cb60
commit
590e9da904
79
generate.py
79
generate.py
@ -3,12 +3,13 @@ from Cheetah.Template import Template
|
|||||||
import glob
|
import glob
|
||||||
import argparse
|
import argparse
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
|
|
||||||
from yaml.loader import SafeLoader
|
from yaml.loader import SafeLoader
|
||||||
from generateHelper import CsOperationNameConverter, OpenApiExtractRefType
|
from generateHelper import CsOperationNameConverter, OpenApiExtractRefType
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(description="generate.py")
|
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.',
|
help='API definition file. Default: openapi.yaml in the current folder.',
|
||||||
required=False,
|
required=False,
|
||||||
default='./openapi.yaml')
|
default='./openapi.yaml')
|
||||||
@ -20,35 +21,35 @@ with an additional .tmpl extension. Default: all template files recursively from
|
|||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
|
||||||
with open(args.api) as schemaFile:
|
with open(args.apiDefinitionFile) as apiDefinitionFile:
|
||||||
schema = yaml.load(schemaFile, Loader=SafeLoader)
|
apiDefinition = yaml.load(apiDefinitionFile, Loader=SafeLoader)
|
||||||
|
|
||||||
|
|
||||||
schema["GENERATED_SQL_COMMENT"] = """
|
apiDefinition["GENERATED_SQL_COMMENT"] = """
|
||||||
-- ----------------------------------------
|
-- ----------------------------------------
|
||||||
-- THIS FILE HAS BEEN GENERATED
|
-- THIS FILE HAS BEEN GENERATED
|
||||||
-- DO NOT EDIT MANUALLY
|
-- DO NOT EDIT MANUALLY
|
||||||
-- ----------------------------------------
|
-- ----------------------------------------
|
||||||
"""
|
"""
|
||||||
schema["GENERATED_PYTHON_COMMENT"] = """
|
apiDefinition["GENERATED_PYTHON_COMMENT"] = """
|
||||||
# -----------------------------------------
|
# -----------------------------------------
|
||||||
# THIS FILE HAS BEEN GENERATED
|
# THIS FILE HAS BEEN GENERATED
|
||||||
# DO NOT EDIT MANUALLY
|
# DO NOT EDIT MANUALLY
|
||||||
# -----------------------------------------
|
# -----------------------------------------
|
||||||
"""
|
"""
|
||||||
schema["GENERATED_YAML_COMMENT"] = """
|
apiDefinition["GENERATED_YAML_COMMENT"] = """
|
||||||
# -----------------------------------------
|
# -----------------------------------------
|
||||||
# THIS FILE HAS BEEN GENERATED
|
# THIS FILE HAS BEEN GENERATED
|
||||||
# DO NOT EDIT MANUALLY
|
# DO NOT EDIT MANUALLY
|
||||||
# -----------------------------------------
|
# -----------------------------------------
|
||||||
"""
|
"""
|
||||||
schema["GENERATED_TS_COMMENT"] = """
|
apiDefinition["GENERATED_TS_COMMENT"] = """
|
||||||
// -----------------------------------------
|
// -----------------------------------------
|
||||||
// THIS FILE HAS BEEN GENERATED
|
// THIS FILE HAS BEEN GENERATED
|
||||||
// DO NOT EDIT MANUALLY
|
// DO NOT EDIT MANUALLY
|
||||||
// -----------------------------------------
|
// -----------------------------------------
|
||||||
"""
|
"""
|
||||||
schema["GENERATED_CS_COMMENT"] = """
|
apiDefinition["GENERATED_CS_COMMENT"] = """
|
||||||
// -----------------------------------------
|
// -----------------------------------------
|
||||||
// THIS FILE HAS BEEN GENERATED
|
// THIS FILE HAS BEEN GENERATED
|
||||||
// DO NOT EDIT MANUALLY
|
// DO NOT EDIT MANUALLY
|
||||||
@ -56,34 +57,78 @@ schema["GENERATED_CS_COMMENT"] = """
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
packageName = os.environ["PACKAGE_NAME"]
|
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 = []
|
operations = []
|
||||||
for path in schema['paths'].values():
|
for path in apiDefinition['paths'].values():
|
||||||
for (method, operation) in path.items():
|
for (method, operation) in path.items():
|
||||||
#print(f"{method=}")
|
#print(f"{method=}")
|
||||||
#print(f"{CsOperationNameConverter(operation['operationId'])=}")
|
#print(f"{CsOperationNameConverter(operation['operationId'])=}")
|
||||||
|
# if 200 in
|
||||||
content = operation['responses'][200]['content']['application/json']['schema']
|
content = operation['responses'][200]['content']['application/json']['schema']
|
||||||
if ('type' in content) and (content['type'] == 'array'):
|
if ('type' in content) and (content['type'] == 'array'):
|
||||||
isList = True
|
isList = True
|
||||||
typ = OpenApiExtractRefType(content['items']['$ref'])
|
resultType = OpenApiExtractRefType(content['items']['$ref'])
|
||||||
else:
|
else:
|
||||||
isList = False
|
isList = False
|
||||||
typ = OpenApiExtractRefType(content['$ref'])
|
resultType = OpenApiExtractRefType(content['$ref'])
|
||||||
#print(f"{content=}")
|
#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({
|
operations.append({
|
||||||
|
'description': description,
|
||||||
'method':method,
|
'method':method,
|
||||||
'func':CsOperationNameConverter(operation['operationId']),
|
'func':CsOperationNameConverter(operation['operationId']),
|
||||||
'isList': isList,
|
'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=}")
|
print(f"{operations=}")
|
||||||
schema["operations"] = 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):
|
for f in glob.glob(args.template, recursive=True):
|
||||||
print(f"process {f}")
|
print(f"process {f}")
|
||||||
tmpl = Template(file=f, searchList=[schema])
|
tmpl = Template(file=f, searchList=[apiDefinition])
|
||||||
with open(f[:-5], 'w') as outFile:
|
with open(f[:-5], 'w') as outFile:
|
||||||
outFile.write(str(tmpl))
|
outFile.write(str(tmpl))
|
||||||
|
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
|
def capitalizeOnlyFirstLetter(i):
|
||||||
|
return i[0].upper() + i[1:]
|
||||||
|
|
||||||
def JsNameConverter(i):
|
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):
|
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):
|
def OpenApiExtractRefType(i):
|
||||||
e = i.split('/')
|
e = i.split('/')
|
||||||
|
71
openapi.yaml
71
openapi.yaml
@ -18,13 +18,26 @@ paths:
|
|||||||
schema:
|
schema:
|
||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
$ref: "#/components/schemas/Test1"
|
$ref: "#/components/schemas/test1"
|
||||||
404:
|
404:
|
||||||
description: No test1 items available
|
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}:
|
/pdb/v2/test1/{id}:
|
||||||
get:
|
get:
|
||||||
tags: [ "Regular" ]
|
tags: [ "Regular" ]
|
||||||
operationId: Regular.test2byid
|
operationId: Regular.test1byid
|
||||||
summary: Returns one entry from table test1 by id
|
summary: Returns one entry from table test1 by id
|
||||||
parameters:
|
parameters:
|
||||||
- name: id
|
- name: id
|
||||||
@ -38,13 +51,49 @@ paths:
|
|||||||
content:
|
content:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
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:
|
404:
|
||||||
description: No such test1 item available
|
description: No such test1 item available
|
||||||
|
|
||||||
components:
|
components:
|
||||||
schemas:
|
schemas:
|
||||||
Test1:
|
test1:
|
||||||
description: A test1 item
|
description: A test1 item
|
||||||
type: object
|
type: object
|
||||||
required:
|
required:
|
||||||
@ -56,3 +105,17 @@ components:
|
|||||||
type: string
|
type: string
|
||||||
nr:
|
nr:
|
||||||
type: integer
|
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
|
||||||
|
@ -17,18 +17,51 @@ namespace ${env['packagename']}.Implementations
|
|||||||
public class RegularApiImplementation : RegularApiController
|
public class RegularApiImplementation : RegularApiController
|
||||||
{
|
{
|
||||||
#for $operation in $operations
|
#for $operation in $operations
|
||||||
public override IActionResult ${operation['func']}() {
|
#if $operation['method'] == 'get'
|
||||||
return new ObjectResult();
|
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
|
#end for
|
||||||
|
|
||||||
public override IActionResult CyclesStartCycle([FromBody]MCycle mCycle) {
|
|
||||||
return StatusCode(200, default(MCycle));
|
|
||||||
}
|
|
||||||
|
|
||||||
public override IActionResult CyclesStopCycle([FromBody]CycleId cycleId) {
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user