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