Skip to content

Commit 1e42ef7

Browse files
authored
Merge pull request #19 from intersystems/internal-updates
Release 1.2.1
2 parents 68ef441 + 95ac067 commit 1e42ef7

File tree

6 files changed

+97
-28
lines changed

6 files changed

+97
-28
lines changed

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,15 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [1.2.1] - 2022-10-05
9+
10+
### Fixed
11+
- APPS-15075: Generating OpenAPI doc produces deterministic results
12+
- APPS-13794: OpenAPI generated properly for query/path action parameters
13+
- APPS-13849: OpenAPI doc includes query filter
14+
- APPS-13664: %Dictionary.Classname is treated as string instead of object
15+
- Unsupported actions are always omitted from OpenAPI generation
16+
817
## [1.2.0] - 2022-08-08
918

1019
### Added

cls/_pkg/isc/rest/openAPI.cls

Lines changed: 24 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -683,7 +683,7 @@ Method GetWebApplicationInfo() [ Internal, Private ]
683683
Method CollectResourceInfo() [ Internal, Private ]
684684
{
685685
// Get resource info from the %pkg.isc.rest.resourceMap table
686-
Set query = "select MediaType, ResourceClass, ResourceName from %pkg_isc_rest.resourceMap where DispatchClass = ?"
686+
Set query = "select MediaType, ResourceClass, ResourceName from %pkg_isc_rest.resourceMap where DispatchClass = ? order by ResourceClass"
687687
Set result = ##class(%SQL.Statement).%ExecDirect(,query,..DispatchClass)
688688
If result.%SQLCODE < 0 {
689689
Throw ##class(%Exception.SQL).CreateFromSQLCODE(result.%SQLCODE, result.%Message)
@@ -736,7 +736,7 @@ Method CollectResourceInfo() [ Internal, Private ]
736736
Method CollectActionInfo() [ Internal ]
737737
{
738738
// Get action info from the %pkg.isc.rest.actionMap table
739-
Set query = "select AcceptsOrNUL, ActionName, ActionTarget, HTTPVerb, ImplementationClass, MediaTypeOrNUL, ResourceClass, ResourceName from %pkg_isc_rest.actionMap where DispatchClass = ?"
739+
Set query = "select AcceptsOrNUL, ActionName, ActionTarget, HTTPVerb, ImplementationClass, MediaTypeOrNUL, ResourceClass, ResourceName from %pkg_isc_rest.actionMap where DispatchClass = ? order by ResourceClass, ActionName, HTTPVerb"
740740
Set result = ##class(%SQL.Statement).%ExecDirect(,query,..DispatchClass)
741741
If result.%SQLCODE < 0 {
742742
Throw ##class(%Exception.SQL).CreateFromSQLCODE(result.%SQLCODE, result.%Message)
@@ -1023,7 +1023,7 @@ Method WriteResourceEndpoints() [ Internal, Private ]
10231023
// Parameter generation
10241024
try {
10251025
// The "nice" way to do this: these are the properties that are reported as being OK to query as reported by DBMappedResource
1026-
Set parametersIterator = $ClassMethod(resourceSchema.ClassName, "getProxyColumnList").%GetIterator()
1026+
Set parametersIterator = $ClassMethod(resourceSchema.ClassName, "GetProxyColumnList").%GetIterator()
10271027
} catch (ex) {
10281028
// TODO: Come up with some way to handle classes that don't use DBMappedResource methods, instead of (incorrectly) listing them as having no parameters.
10291029
// TODO: The following methods theoretically perform known operations:
@@ -1503,7 +1503,7 @@ Method WriteActionEndpoints() [ Internal, Private ]
15031503
For i=1:1:..ActionInfo.Count() {
15041504
#dim actionSchema As %pkg.isc.rest.openAPI.actionInfo
15051505
Set actionSchema = ..ActionInfo.GetAt(i)
1506-
If ('actionSchema.Supported) && ((actionSchema.Forbidden && actionSchema.ForbidUnderAllCircumstances) ||
1506+
If ('actionSchema.Supported) || ((actionSchema.Forbidden && actionSchema.ForbidUnderAllCircumstances) ||
15071507
('..IncludeForbiddenEndpoints && '..ForceAuthorizeAllEndpoints && actionSchema.Forbidden)) {
15081508
Continue
15091509
}
@@ -1576,35 +1576,36 @@ Method WriteActionEndpoints() [ Internal, Private ]
15761576
// Query/Path Parameters
15771577
#Dim parameter As %pkg.isc.rest.openAPI.model.parameter
15781578
Set parameter = ""
1579+
Set found = 0
15791580
For j=1:1:methodObj.Parameters.Count() {
15801581
Set existingParameter = methodObj.Parameters.GetAt(j)
15811582
If existingParameter.Name = argument.Name && (existingParameter.In = argument.Source) {
15821583
Set parameter = methodObj.Parameters.GetAt(j)
1584+
Set found = 1
15831585
Quit
15841586
}
15851587
}
15861588
Set schema = argument.Schema
1587-
Set mediatype = ##class(%pkg.isc.rest.openAPI.model.mediaType).%New()
1588-
Do mediatype.SourceClasses.Insert(argument.SourceClass)
1589-
Set mediatype.Schema = schema
15901589
If parameter = "" {
15911590
Set parameter = ##class(%pkg.isc.rest.openAPI.model.parameter).%New()
1592-
Do parameter.Content.SetAt(mediatype, argument.MediaType)
1591+
Set parameter.Schema = schema
15931592
} Else {
1594-
Set existingSchema = parameter.Content.GetNext("")
1595-
If $IsObject(existingSchema) {
1596-
Set baseSchema = ##class(%pkg.isc.rest.openAPI.model.schema).%New()
1597-
Set baseSchema.AutoGenerated = 1
1598-
For j=1:1:existingSchema.SourceClasses.Count() {
1599-
Do baseSchema.SourceClasses.Insert(existingSchema.SourceClasses.GetAt(j))
1593+
Set baseSchema = parameter.Schema
1594+
1595+
// If we have the same action from multiple sources,
1596+
// don't add duplicate "OneOf" entries with the same reference.
1597+
// (Could do deduplication downstream too.)
1598+
Set foundRef = 0
1599+
If (schema.Ref '= "") {
1600+
For j=1:1:baseSchema.OneOf.Count() {
1601+
If baseSchema.OneOf.GetAt(j).Ref = schema.Ref {
1602+
Set foundRef = 1
1603+
}
16001604
}
1601-
Do parameter.Content.Clear()
1602-
Set parameter.Schema = baseSchema
1603-
Do baseSchema.OneOf.Insert(existingSchema)
1604-
} Else {
1605-
Set baseSchema = parameter.Schema
16061605
}
1607-
Do baseSchema.OneOf.Insert(schema)
1606+
If 'foundRef {
1607+
Do baseSchema.OneOf.Insert(schema)
1608+
}
16081609
For j=1:1:schema.SourceClasses.Count() {
16091610
Do baseSchema.SourceClasses.Insert(schema.SourceClasses.GetAt(j))
16101611
}
@@ -1615,7 +1616,9 @@ Method WriteActionEndpoints() [ Internal, Private ]
16151616
//Set parameter.Description = "TODO: ???"
16161617
// Path parameters are always required
16171618
Set parameter.Required = argument.Required || (argument.Source = "path")
1618-
Do methodObj.Parameters.Insert(parameter)
1619+
If 'found {
1620+
Do methodObj.Parameters.Insert(parameter)
1621+
}
16191622
} ElseIf argument.Source = "body-key" {
16201623
// Body-Key Parameters
16211624
Set requestBody = methodObj.RequestBody
@@ -1983,4 +1986,3 @@ Method WriteSection(text, ByRef ws, ByRef timer) [ Internal, Private ]
19831986
}
19841987

19851988
}
1986-

cls/_pkg/isc/rest/openAPI/util.cls

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -260,8 +260,12 @@ ClassMethod GetJSONType(className As %String) As %String
260260
} ElseIf $ClassMethod(className, "%Extends", "%Stream.Object") {
261261
// TODO: Support binary streams as NOT strings...
262262
Set jsonType = "string"
263+
} ElseIf (xsdType '= "") {
264+
Set jsonType = $Select(xsdType="long":"integer",xsdType="boolean":"boolean",$Match(xsdType,"decimal|double|short|byte"):"number",1:"string")
265+
} ElseIf ($$$comClassKeyGet(className,$$$cCLASSclasstype) = $$$cCLASSCLASSTYPEDATATYPE) {
266+
Set jsonType = "string"
263267
} Else {
264-
Set jsonType = $Select(xsdType="":"object",xsdType="long":"integer",xsdType="boolean":"boolean",$Match(xsdType,"decimal|double|short|byte"):"number",1:"string")
268+
Set jsonType = "object"
265269
}
266270
}
267271
Set:className="%Library.DynamicArray" jsonType = "array"
@@ -339,4 +343,3 @@ ClassMethod ReadClassMethodOutput(className, methodName) As %String [ Internal ]
339343
}
340344

341345
}
342-

internal/testing/unit_tests/UnitTest/isc/rest/openAPI.cls

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -440,7 +440,7 @@ Method TestGenerationWithSharedResourceName()
440440
// /unittest-resource/$test-action checks
441441
#Dim actionPathItem As %pkg.isc.rest.openAPI.model.pathItem
442442
Set actionPathItem = openapi.Specification.Paths.GetAt("/unittest-resource/$test-action")
443-
Do $$$AssertEquals(actionPathItem.SourceClasses.Count(), 2, "Action path item has only one source class")
443+
Do $$$AssertEquals(actionPathItem.SourceClasses.Count(), 1, "Action path item has only one source class")
444444
Do $$$AssertEquals(actionPathItem.SourceClasses.GetAt(1), "zUnitTest.isc.rest.class3", "Action path item has the correct source class")
445445
Do $$$AssertEquals(actionPathItem.Get, "", "Action GET operation is not set")
446446
Do $$$AssertEquals(actionPathItem.Put, "", "Action PUT operation is not set")
@@ -449,6 +449,15 @@ Method TestGenerationWithSharedResourceName()
449449
Do $$$AssertEquals(actionPathItem.Trace, "", "Action TRACE operation is not set")
450450
Do $$$AssertEquals(actionPathItem.Head, "", "Action HEAD operation is not set")
451451
Do $$$AssertEquals(actionPathItem.Options, "", "Action OPTIONS operation is not set")
452+
Do $$$AssertEquals(actionPathItem.Post.Tags.GetAt(1),"unittest-resource")
453+
Do $$$AssertEquals(actionPathItem.Post.Parameters.Count(),2)
454+
Do $$$AssertEquals(actionPathItem.Post.Parameters.GetAt(1).In,"query")
455+
Do $$$AssertEquals(actionPathItem.Post.Parameters.GetAt(1).Name,"op")
456+
Do $$$AssertEquals(actionPathItem.Post.Parameters.GetAt(1).Schema.Ref,"#/components/schemas/string_input")
457+
Do $$$AssertEquals(actionPathItem.Post.Parameters.GetAt(2).In,"query")
458+
Do $$$AssertEquals(actionPathItem.Post.Parameters.GetAt(2).Name,"uc")
459+
Do $$$AssertEquals(actionPathItem.Post.Parameters.GetAt(2).Schema.Ref,"#/components/schemas/registered-object_input")
460+
Do $$$AssertTrue($IsObject(actionPathItem.Post.RequestBody.Content.GetAt("application/json")))
452461

453462
// /unittest-resource/{id} checks
454463
#Dim resourceInstancePathItem As %pkg.isc.rest.openAPI.model.pathItem
@@ -1223,6 +1232,26 @@ Method TestPathParameterActions()
12231232
}
12241233
}
12251234

1235+
Method TestQueryFilters()
1236+
{
1237+
Set openapi = ..SetupTestingEnvironment()
1238+
#Dim openapi As %pkg.isc.rest.openAPI
1239+
Set openapi.DEBUG = 0
1240+
Do $$$AssertStatusOK(..SetupClass("zUnitTest.isc.rest.class1", ["%Persistent","%pkg.isc.rest.model.adaptor"], {"RESOURCENAME":"unittest-resource"}, "Demo",{"Type":"%String"},{"%JSONFIELDNAME":"demo"}, ,,, , ["CheckPermissionAllowAll:CheckPermission","GetCollectionCustom:GetCollection"]))
1241+
Do $$$AssertStatusOK(..CompileClass("zUnitTest.isc.rest.class1"))
1242+
Do $$$AssertTrue(openapi.GetSpecificationI($$$NULLOREF, "/UnitTest/isc/rest/openAPI/api/"), "Specification generation completed without errors")
1243+
1244+
Do openapi.Specification.%JSONExportToString(.str)
1245+
Do $$$LogMessage(str)
1246+
1247+
Set onlySearchParam = openapi.Specification.Paths.GetAt("/unittest-resource").Get.Parameters.GetAt(1)
1248+
Do $$$AssertTrue($IsObject(onlySearchParam))
1249+
Do $$$AssertEquals(onlySearchParam.In,"query")
1250+
Do $$$AssertEquals(onlySearchParam.Name,"demo")
1251+
Do $$$AssertEquals(onlySearchParam.Style,"deepObject")
1252+
Do $$$AssertEquals(onlySearchParam.Schema.Ref,"#/components/schemas/QueryFilter")
1253+
}
1254+
12261255
Method TestMultiHandlers()
12271256
{
12281257
Do $$$AssertStatusOK(..SetupClass("zUnitTest.isc.rest.handler1", ["%pkg.isc.rest.handler"],, ,,, ,,, , ["AuthenticationStrategy","CheckResourcePermitted"]))
@@ -1498,4 +1527,3 @@ XData IntegerIdentityActionMap [ XMLNamespace = "http://www.intersystems.com/_pk
14981527
}
14991528

15001529
}
1501-

internal/testing/unit_tests/UnitTest/isc/rest/openAPI/util.cls

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,34 @@ Method TestGetJSON()
4141
Do ..TeardownClass("zUnitTest.isc.rest.class1")
4242
}
4343

44+
Method TestGetJSONType()
45+
{
46+
#Define GetJSONType ##class(%pkg.isc.rest.openAPI.util).GetJSONType
47+
48+
// Objects
49+
Do $$$AssertEquals($$$GetJSONType("%RegisteredObject"),"object")
50+
Do $$$AssertEquals($$$GetJSONType("%SystemBase"),"object")
51+
Do $$$AssertEquals($$$GetJSONType($classname()),"object")
52+
53+
// Normal datatypes
54+
Do $$$AssertEquals($$$GetJSONType("%String"),"string")
55+
Do $$$AssertEquals($$$GetJSONType("%Binary"),"string")
56+
Do $$$AssertEquals($$$GetJSONType("%Numeric"),"number")
57+
Do $$$AssertEquals($$$GetJSONType("%Integer"),"number")
58+
Do $$$AssertEquals($$$GetJSONType("%Float"),"number")
59+
Do $$$AssertEquals($$$GetJSONType("%Double"),"double")
60+
Do $$$AssertEquals($$$GetJSONType("%Date"),"string")
61+
Do $$$AssertEquals($$$GetJSONType("%Boolean"),"boolean")
62+
63+
// %List is treated as a string; we have a special datatype to project as array
64+
Do $$$AssertEquals($$$GetJSONType("%List"),"string")
65+
Do $$$AssertEquals($$$GetJSONType("%pkg.isc.json.dataType.list"),"array")
66+
67+
// Special cases: datatype classes without %JSONTYPE are strings
68+
Do $$$AssertEquals($$$GetJSONType("%Dictionary.Classname"),"string")
69+
Do $$$AssertEquals($$$GetJSONType("%Dictionary.CacheClassname"),"string")
70+
}
71+
4472
Method TestFromJSON()
4573
{
4674
#define JSONAdaptor ##class(UnitTest.isc.rest.openAPI.compatibility).GetJSONAdaptorClass()
@@ -76,4 +104,3 @@ ClassMethod SampleMethod()
76104
}
77105

78106
}
79-

module.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<Document name="isc.rest.ZPM">
33
<Module>
44
<Name>isc.rest</Name>
5-
<Version>1.2.0</Version>
5+
<Version>1.2.1</Version>
66
<Packaging>module</Packaging>
77
<Dependencies>
88
<ModuleReference>

0 commit comments

Comments
 (0)