Update Function GENERATE_SWAGGER_FROM_DBML
This commit is contained in:
@@ -1 +1,386 @@
|
|||||||
牄禗袬蒨@\蚕謨@^@
|
SET PATH *LIBL ;
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION RESTAPI.GENERATE_SWAGGER_FROM_DBML (
|
||||||
|
INAPPVER VARCHAR(10) ,
|
||||||
|
INAPPNAME VARCHAR(128) ,
|
||||||
|
INHOST VARCHAR(256) ,
|
||||||
|
INBASEPATH VARCHAR(256) )
|
||||||
|
RETURNS CLOB(2147483647)
|
||||||
|
LANGUAGE SQL
|
||||||
|
SPECIFIC RESTAPI.GENERATE_SWAGGER_FROM_DBML
|
||||||
|
NOT DETERMINISTIC
|
||||||
|
READS SQL DATA
|
||||||
|
CALLED ON NULL INPUT
|
||||||
|
SET OPTION ALWBLK = *ALLREAD ,
|
||||||
|
ALWCPYDTA = *OPTIMIZE ,
|
||||||
|
COMMIT = *NONE ,
|
||||||
|
DECRESULT = (31, 31, 00) ,
|
||||||
|
DYNDFTCOL = *NO ,
|
||||||
|
DYNUSRPRF = *USER ,
|
||||||
|
SRTSEQ = *HEX
|
||||||
|
BEGIN
|
||||||
|
DECLARE V_DBML CLOB ( 2 G ) ;
|
||||||
|
DECLARE V_SWAGGER CLOB ( 2 G ) ;
|
||||||
|
-- Pull the DBML
|
||||||
|
SET V_DBML = RESTAPI . GENERATE_DBML_V2 ( INAPPVER , INAPPNAME ) ;
|
||||||
|
WITH D ( DOC ) AS (
|
||||||
|
VALUES XMLPARSE ( DOCUMENT V_DBML )
|
||||||
|
) ,
|
||||||
|
-- Programs (endpoints)
|
||||||
|
P AS (
|
||||||
|
SELECT X . NAME AS PGMNAME , X . PATH AS RELPATH ,
|
||||||
|
UPPER ( X . METHOD ) AS HTTPMETHOD , X . OK AS HTTPSUCCESS ,
|
||||||
|
X . FAIL AS HTTPFAIL , X . OUTWRAP AS OUTWRAP
|
||||||
|
FROM D ,
|
||||||
|
XMLTABLE ( '$d/dbml/program' PASSING D . DOC AS "d"
|
||||||
|
COLUMNS NAME VARCHAR ( 128 ) PATH '@name' , PATH VARCHAR (
|
||||||
|
512 ) PATH '@restUriPathTemplate' ,
|
||||||
|
METHOD VARCHAR ( 10 ) PATH '@restHttpRequestMethod' ,
|
||||||
|
OK VARCHAR ( 3 ) PATH '@httpstatusonsuccess' ,
|
||||||
|
FAIL VARCHAR ( 3 ) PATH '@httpstatusonfailure' ,
|
||||||
|
OUTWRAP VARCHAR ( 128 ) PATH '@outputWrapperIdentifier' )
|
||||||
|
AS X
|
||||||
|
) ,
|
||||||
|
-- Params (from <data ... restInXxxParam="...">)
|
||||||
|
PRM AS (
|
||||||
|
SELECT PX . PGMNAME ,
|
||||||
|
COALESCE ( DT . PATHID , DT . FORMID , DT . QUERYID )
|
||||||
|
AS PARAM_NAME ,
|
||||||
|
CASE
|
||||||
|
WHEN DT . PATHID IS NOT NULL THEN 'path'
|
||||||
|
WHEN DT . FORMID IS NOT NULL THEN 'formData'
|
||||||
|
WHEN DT . QUERYID IS NOT NULL THEN 'query'
|
||||||
|
END AS PARAM_IN
|
||||||
|
FROM D ,
|
||||||
|
XMLTABLE ( '$d/dbml/program' PASSING D . DOC AS "d"
|
||||||
|
COLUMNS PGMNAME VARCHAR ( 128 ) PATH '@name' ,
|
||||||
|
PGMNODE XML PATH '.' ) AS PX
|
||||||
|
LEFT JOIN XMLTABLE ( 'sql/data' PASSING PX . PGMNODE
|
||||||
|
COLUMNS PATHID VARCHAR ( 128 ) PATH
|
||||||
|
'@restInPathParam' ,
|
||||||
|
FORMID VARCHAR ( 128 ) PATH '@restInFormParam' ,
|
||||||
|
QUERYID VARCHAR ( 128 ) PATH '@restInQueryParam' ) AS
|
||||||
|
DT
|
||||||
|
ON 1 = 1
|
||||||
|
WHERE COALESCE ( DT . PATHID , DT . FORMID , DT . QUERYID )
|
||||||
|
IS NOT NULL
|
||||||
|
) ,
|
||||||
|
-- Path tokens (non-variable segments), auto-ignoring the app name
|
||||||
|
TOK AS (
|
||||||
|
SELECT P . PGMNAME , S . ORDINAL_POSITION AS POS ,
|
||||||
|
LOWER ( S . ELEMENT ) AS RAW_TOKEN
|
||||||
|
FROM P , TABLE (
|
||||||
|
SYSTOOLS . SPLIT ( P . RELPATH , '/' )
|
||||||
|
) AS S
|
||||||
|
WHERE S . ELEMENT <> ''
|
||||||
|
AND S . ELEMENT NOT LIKE '{%}'
|
||||||
|
AND LOWER ( S . ELEMENT ) <> LOWER ( INAPPNAME ) -- don't hardcode any app; use-- the param
|
||||||
|
) ,
|
||||||
|
-- First two tokens = default tag (e.g., "Users - MAPICS")
|
||||||
|
TOP2 AS (
|
||||||
|
SELECT PGMNAME , RAW_TOKEN , ROW_NUMBER ( ) OVER (
|
||||||
|
PARTITION BY PGMNAME
|
||||||
|
ORDER BY POS
|
||||||
|
) AS RN
|
||||||
|
FROM TOK
|
||||||
|
) ,
|
||||||
|
TAGRAW AS (
|
||||||
|
SELECT T1 . PGMNAME , T1 . RAW_TOKEN AS RAW1 , T2 . RAW_TOKEN AS RAW2
|
||||||
|
FROM TOP2 T1
|
||||||
|
LEFT JOIN TOP2 T2
|
||||||
|
ON T2 . PGMNAME = T1 . PGMNAME
|
||||||
|
AND T2 . RN = 2
|
||||||
|
WHERE T1 . RN = 1
|
||||||
|
) ,
|
||||||
|
-- Pretty display for any raw token using NAME_MAP, else TitleCase
|
||||||
|
NAME_FMT AS (
|
||||||
|
SELECT X . RAW_TOKEN ,
|
||||||
|
COALESCE (
|
||||||
|
NM . DISPLAY , UPPER ( SUBSTR ( X . RAW_TOKEN , 1 , 1 ) ) CONCAT
|
||||||
|
CASE
|
||||||
|
WHEN
|
||||||
|
LENGTH ( X . RAW_TOKEN ) > 1
|
||||||
|
THEN
|
||||||
|
LOWER (
|
||||||
|
REPLACE (
|
||||||
|
REPLACE (
|
||||||
|
SUBSTR ( X . RAW_TOKEN , 2 ) , '-' , ' '
|
||||||
|
) , '_' , ' ' ) )
|
||||||
|
ELSE ''
|
||||||
|
END ) AS DISPLAY
|
||||||
|
FROM (
|
||||||
|
SELECT DISTINCT RAW_TOKEN
|
||||||
|
FROM TOK
|
||||||
|
) X
|
||||||
|
LEFT JOIN RESTAPI . NAME_MAP NM
|
||||||
|
ON NM . RAW_TOKEN = X . RAW_TOKEN
|
||||||
|
) ,
|
||||||
|
-- Verb = last token if it's in VERB_MAP
|
||||||
|
LASTTOK AS (
|
||||||
|
SELECT PGMNAME , MAX ( POS ) AS MAXPOS
|
||||||
|
FROM TOK
|
||||||
|
GROUP BY PGMNAME
|
||||||
|
) ,
|
||||||
|
VERB AS (
|
||||||
|
SELECT L . PGMNAME , T . RAW_TOKEN AS RAW_VERB ,
|
||||||
|
VM . DISPLAY AS VERB_DISPLAY ,
|
||||||
|
CASE
|
||||||
|
WHEN VM . RAW_VERB IS NULL THEN 0
|
||||||
|
ELSE 1
|
||||||
|
END AS IS_VERB , L . MAXPOS
|
||||||
|
FROM LASTTOK L
|
||||||
|
JOIN TOK T
|
||||||
|
ON T . PGMNAME = L . PGMNAME
|
||||||
|
AND T . POS = L . MAXPOS
|
||||||
|
LEFT JOIN RESTAPI . VERB_MAP VM
|
||||||
|
ON VM . RAW_VERB = T . RAW_TOKEN
|
||||||
|
) ,
|
||||||
|
-- Object phrase = last one or two nouns *before* the verb (if verb recognized),--
|
||||||
|
-- else last one or two overall
|
||||||
|
OBJ_SRC AS (
|
||||||
|
SELECT T . PGMNAME , T . POS , T . RAW_TOKEN , V . IS_VERB , V . MAXPOS
|
||||||
|
FROM TOK T
|
||||||
|
LEFT JOIN VERB V
|
||||||
|
ON V . PGMNAME = T . PGMNAME
|
||||||
|
) ,
|
||||||
|
OBJ_NOUNS AS (
|
||||||
|
SELECT PGMNAME , RAW_TOKEN , ROW_NUMBER ( ) OVER (
|
||||||
|
PARTITION BY PGMNAME
|
||||||
|
ORDER BY POS DESC
|
||||||
|
) AS RN_ALLDESC , ROW_NUMBER ( ) OVER (
|
||||||
|
PARTITION BY PGMNAME
|
||||||
|
ORDER BY
|
||||||
|
CASE
|
||||||
|
WHEN IS_VERB = 1 THEN
|
||||||
|
CASE
|
||||||
|
WHEN POS < MAXPOS THEN POS
|
||||||
|
ELSE - 999999
|
||||||
|
END
|
||||||
|
ELSE POS
|
||||||
|
END DESC
|
||||||
|
) AS RN_BEFORE_VERB_DESC
|
||||||
|
FROM OBJ_SRC
|
||||||
|
) ,
|
||||||
|
OBJ_PICK AS (
|
||||||
|
-- Prefer nouns before verb (RN_BEFORE_VERB_DESC), fallback to overall (RN_ALLDE--SC)
|
||||||
|
SELECT O1 . PGMNAME ,
|
||||||
|
COALESCE (
|
||||||
|
NF1 . DISPLAY ,
|
||||||
|
UPPER ( SUBSTR ( O1 . RAW_TOKEN , 1 , 1 ) ) CONCAT
|
||||||
|
LOWER ( SUBSTR ( O1 . RAW_TOKEN , 2 ) ) ) AS OBJ1 ,
|
||||||
|
COALESCE (
|
||||||
|
NF2 . DISPLAY ,
|
||||||
|
UPPER ( SUBSTR ( O2 . RAW_TOKEN , 1 , 1 ) ) CONCAT
|
||||||
|
LOWER ( SUBSTR ( O2 . RAW_TOKEN , 2 ) ) ) AS OBJ2
|
||||||
|
FROM (
|
||||||
|
SELECT *
|
||||||
|
FROM OBJ_NOUNS
|
||||||
|
WHERE RN_BEFORE_VERB_DESC = 1
|
||||||
|
) O1
|
||||||
|
LEFT JOIN (
|
||||||
|
SELECT *
|
||||||
|
FROM OBJ_NOUNS
|
||||||
|
WHERE RN_BEFORE_VERB_DESC = 2
|
||||||
|
) O2
|
||||||
|
ON O2 . PGMNAME = O1 . PGMNAME
|
||||||
|
LEFT JOIN NAME_FMT NF1
|
||||||
|
ON NF1 . RAW_TOKEN = O1 . RAW_TOKEN
|
||||||
|
LEFT JOIN NAME_FMT NF2
|
||||||
|
ON NF2 . RAW_TOKEN = COALESCE ( O2 . RAW_TOKEN , '' )
|
||||||
|
UNION ALL
|
||||||
|
SELECT O1 . PGMNAME ,
|
||||||
|
COALESCE (
|
||||||
|
NF1 . DISPLAY ,
|
||||||
|
UPPER ( SUBSTR ( O1 . RAW_TOKEN , 1 , 1 ) ) CONCAT
|
||||||
|
LOWER ( SUBSTR ( O1 . RAW_TOKEN , 2 ) ) ) AS OBJ1 ,
|
||||||
|
COALESCE (
|
||||||
|
NF2 . DISPLAY ,
|
||||||
|
UPPER ( SUBSTR ( O2 . RAW_TOKEN , 1 , 1 ) ) CONCAT
|
||||||
|
LOWER ( SUBSTR ( O2 . RAW_TOKEN , 2 ) ) ) AS OBJ2
|
||||||
|
FROM (
|
||||||
|
SELECT *
|
||||||
|
FROM OBJ_NOUNS
|
||||||
|
WHERE RN_ALLDESC = 1
|
||||||
|
) O1
|
||||||
|
LEFT JOIN (
|
||||||
|
SELECT *
|
||||||
|
FROM OBJ_NOUNS
|
||||||
|
WHERE RN_ALLDESC = 2
|
||||||
|
) O2
|
||||||
|
ON O2 . PGMNAME = O1 . PGMNAME
|
||||||
|
LEFT JOIN NAME_FMT NF1
|
||||||
|
ON NF1 . RAW_TOKEN = O1 . RAW_TOKEN
|
||||||
|
LEFT JOIN NAME_FMT NF2
|
||||||
|
ON NF2 . RAW_TOKEN = COALESCE ( O2 . RAW_TOKEN , '' )
|
||||||
|
WHERE NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM OBJ_NOUNS Z
|
||||||
|
WHERE Z . PGMNAME = O1 . PGMNAME
|
||||||
|
AND Z . RN_BEFORE_VERB_DESC = 1 )
|
||||||
|
) ,
|
||||||
|
-- Derived tag ("Seg1 - Seg2") using display names
|
||||||
|
TAG_DERIVED AS (
|
||||||
|
SELECT TR . PGMNAME ,
|
||||||
|
CASE
|
||||||
|
WHEN
|
||||||
|
NF2 . DISPLAY IS NOT NULL
|
||||||
|
THEN NF1 . DISPLAY CONCAT ' - ' CONCAT NF2 . DISPLAY
|
||||||
|
ELSE NF1 . DISPLAY
|
||||||
|
END AS TAG_NAME
|
||||||
|
FROM TAGRAW TR
|
||||||
|
LEFT JOIN NAME_FMT NF1
|
||||||
|
ON NF1 . RAW_TOKEN = TR . RAW1
|
||||||
|
LEFT JOIN NAME_FMT NF2
|
||||||
|
ON NF2 . RAW_TOKEN = TR . RAW2
|
||||||
|
) ,
|
||||||
|
-- Summaries: "{VerbDisplay or 'Execute'} {OBJ1 [OBJ2]} [for param1, param2 ...]--"
|
||||||
|
PATH_PARAMS AS (
|
||||||
|
SELECT PGMNAME ,
|
||||||
|
LISTAGG (
|
||||||
|
PARAM_NAME , ', ' ) WITHIN GROUP ( ORDER BY PARAM_NAME )
|
||||||
|
AS PATH_PARAM_LIST
|
||||||
|
FROM PRM
|
||||||
|
WHERE PARAM_IN = 'path'
|
||||||
|
GROUP BY PGMNAME
|
||||||
|
) ,
|
||||||
|
SUMMARY_DERIVED AS (
|
||||||
|
SELECT P . PGMNAME ,
|
||||||
|
RTRIM (
|
||||||
|
COALESCE ( V . VERB_DISPLAY , 'Execute' ) CONCAT ' '
|
||||||
|
CONCAT
|
||||||
|
CASE
|
||||||
|
WHEN
|
||||||
|
OP . OBJ2 IS NOT NULL
|
||||||
|
THEN OP . OBJ2 CONCAT ' ' CONCAT OP . OBJ1
|
||||||
|
ELSE OP . OBJ1
|
||||||
|
END CONCAT
|
||||||
|
COALESCE ( ' for ' CONCAT PP . PATH_PARAM_LIST , '' ) )
|
||||||
|
AS SUMMARY
|
||||||
|
FROM P
|
||||||
|
LEFT JOIN VERB V
|
||||||
|
ON V . PGMNAME = P . PGMNAME
|
||||||
|
LEFT JOIN OBJ_PICK OP
|
||||||
|
ON OP . PGMNAME = P . PGMNAME
|
||||||
|
LEFT JOIN PATH_PARAMS PP
|
||||||
|
ON PP . PGMNAME = P . PGMNAME
|
||||||
|
) ,
|
||||||
|
-- Apply optional per-program overrides
|
||||||
|
META AS (
|
||||||
|
SELECT P . PGMNAME ,
|
||||||
|
COALESCE ( O . TAG_NAME , TD . TAG_NAME ) AS TAG_NAME ,
|
||||||
|
COALESCE ( O . SUMMARY , SD . SUMMARY ) AS SUMMARY
|
||||||
|
FROM P
|
||||||
|
LEFT JOIN TAG_DERIVED TD
|
||||||
|
ON TD . PGMNAME = P . PGMNAME
|
||||||
|
LEFT JOIN SUMMARY_DERIVED SD
|
||||||
|
ON SD . PGMNAME = P . PGMNAME
|
||||||
|
LEFT JOIN RESTAPI . SWAGGER_OVERRIDES O
|
||||||
|
ON O . APPVER = INAPPVER
|
||||||
|
AND O . APPNAME = INAPPNAME
|
||||||
|
AND O . PGMNAME = P . PGMNAME
|
||||||
|
) ,
|
||||||
|
-- Parameters JSON per program
|
||||||
|
PARMS AS (
|
||||||
|
SELECT PGMNAME ,
|
||||||
|
'[' CONCAT
|
||||||
|
COALESCE (
|
||||||
|
LISTAGG (
|
||||||
|
'{"name":"' CONCAT PARAM_NAME CONCAT
|
||||||
|
'","in":"' CONCAT PARAM_IN CONCAT
|
||||||
|
'","required":true,"type":"string"}' , ',' ) ,
|
||||||
|
'' ) CONCAT ']' AS PARAMS_JSON
|
||||||
|
FROM PRM
|
||||||
|
GROUP BY PGMNAME
|
||||||
|
) ,
|
||||||
|
-- Path method JSON per program
|
||||||
|
PATHS AS (
|
||||||
|
SELECT '/v1' CONCAT RELPATH AS FULLPATH ,
|
||||||
|
JSON_OBJECT (
|
||||||
|
LOWER ( HTTPMETHOD ) VALUE JSON_OBJECT (
|
||||||
|
'tags' VALUE JSON_ARRAY ( M . TAG_NAME ) ,
|
||||||
|
'summary' VALUE M . SUMMARY ,
|
||||||
|
'operationId' VALUE M . PGMNAME ,
|
||||||
|
'consumes' VALUE JSON_ARRAY ( '*/*' ) ,
|
||||||
|
'produces' VALUE JSON_ARRAY ( 'application/json' ) ,
|
||||||
|
'parameters' VALUE JSON_ARRAY ( PR . PARAMS_JSON ) ,
|
||||||
|
'responses' VALUE JSON_OBJECT (
|
||||||
|
HTTPSUCCESS VALUE JSON_OBJECT (
|
||||||
|
'description' VALUE 'Successful operation'
|
||||||
|
) , HTTPFAIL VALUE JSON_OBJECT (
|
||||||
|
'description' VALUE 'Operation failure' ,
|
||||||
|
'schema' VALUE JSON_OBJECT (
|
||||||
|
'$ref' VALUE '#/definitions/' ||
|
||||||
|
OUTWRAP
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
) AS METHOD_JSON
|
||||||
|
FROM P
|
||||||
|
LEFT JOIN META M
|
||||||
|
ON M . PGMNAME = P . PGMNAME
|
||||||
|
LEFT JOIN PARMS PR
|
||||||
|
ON PR . PGMNAME = P . PGMNAME
|
||||||
|
) ,
|
||||||
|
PATHS_JSON AS (
|
||||||
|
SELECT
|
||||||
|
'{' CONCAT
|
||||||
|
LISTAGG (
|
||||||
|
'"' CONCAT FULLPATH CONCAT '": ' CONCAT METHOD_JSON ,
|
||||||
|
',' ) CONCAT '}' AS J
|
||||||
|
FROM PATHS
|
||||||
|
) ,
|
||||||
|
TAGS_JSON AS (
|
||||||
|
SELECT
|
||||||
|
'[' CONCAT
|
||||||
|
LISTAGG (
|
||||||
|
'{"name":"' CONCAT TAG_NAME CONCAT
|
||||||
|
'","description":"Operations for ' CONCAT TAG_NAME
|
||||||
|
CONCAT '"}' , ',' ) CONCAT ']' AS J
|
||||||
|
FROM (
|
||||||
|
SELECT DISTINCT TAG_NAME
|
||||||
|
FROM META
|
||||||
|
)
|
||||||
|
) ,
|
||||||
|
DEFS_JSON AS (
|
||||||
|
SELECT
|
||||||
|
'{' CONCAT
|
||||||
|
'"SQLException":{"type":"object","properties":{"SQLState":{"type":"string","maxLength":5},"errorCode":{"type":"integer"},"message":{"type":"string"}}},'
|
||||||
|
CONCAT
|
||||||
|
'"SQLStateInfo":{"type":"object","properties":{"rowsAffectedCounts":{"type":"string"},"SQLError":{"$ref":"#/definitions/SQLException"},"SQLWarnings":{"type":"array","items":{"$ref":"#/definitions/SQLException"}}}},'
|
||||||
|
CONCAT
|
||||||
|
LISTAGG (
|
||||||
|
'"' CONCAT OUTWRAP CONCAT
|
||||||
|
'":{"type":"object","properties":{"SQLStateInfo":{"$ref":"#/definitions/SQLStateInfo"}}}'
|
||||||
|
, ',' ) CONCAT '}' AS J
|
||||||
|
FROM (
|
||||||
|
SELECT DISTINCT OUTWRAP
|
||||||
|
FROM P
|
||||||
|
)
|
||||||
|
)
|
||||||
|
SELECT '{' CONCAT '"swagger":"2.0",' CONCAT '"info":{"title":"'
|
||||||
|
CONCAT UPPER ( SUBSTR ( INAPPNAME , 1 , 1 ) ) CONCAT
|
||||||
|
LOWER ( SUBSTR ( INAPPNAME , 2 ) ) CONCAT ' APIs",' CONCAT
|
||||||
|
'"description":"APIs available for ' CONCAT INAPPNAME CONCAT
|
||||||
|
'",' CONCAT '"version":"1.0.0"},' CONCAT '"host":"' CONCAT
|
||||||
|
INHOST CONCAT '",' CONCAT '"schemes":["http"],' CONCAT
|
||||||
|
'"basePath":"' CONCAT INBASEPATH CONCAT '",' CONCAT '"tags":'
|
||||||
|
CONCAT COALESCE ( ( SELECT J
|
||||||
|
FROM TAGS_JSON ) , '[]' ) CONCAT ',' CONCAT
|
||||||
|
'"definitions":' CONCAT ( SELECT J
|
||||||
|
FROM DEFS_JSON ) CONCAT ',' CONCAT '"paths":' CONCAT
|
||||||
|
( SELECT J
|
||||||
|
FROM PATHS_JSON ) CONCAT '}'
|
||||||
|
INTO V_SWAGGER
|
||||||
|
FROM SYSIBM . SYSDUMMY1 ;
|
||||||
|
RETURN V_SWAGGER ;
|
||||||
|
END ;
|
||||||
|
|
||||||
|
GRANT ALTER , EXECUTE
|
||||||
|
ON SPECIFIC FUNCTION RESTAPI.GENERATE_SWAGGER_FROM_DBML
|
||||||
|
TO AMAPICS WITH GRANT OPTION ;
|
||||||
|
|
||||||
|
GRANT EXECUTE
|
||||||
|
ON SPECIFIC FUNCTION RESTAPI.GENERATE_SWAGGER_FROM_DBML
|
||||||
|
TO PUBLIC ;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user