Skip to content

Commit e4401fb

Browse files
authored
Merge pull request #16 from intersystems/internal-updates
Upstream updates for 1.1.0 release
2 parents 7d2b13f + 8a41fc9 commit e4401fb

File tree

13 files changed

+541
-373
lines changed

13 files changed

+541
-373
lines changed

CHANGELOG.md

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ 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-
## [Unreleased - 1.1.0+snapshot]
8+
## [1.1.0] - 2022-08-08
99

1010
### Added
1111
- APPS-12985: Support removal of certain endpoints at the dispatch class and resource level
@@ -15,22 +15,20 @@ via new `Supports()` method that can be overridden at REST handler and resource
1515
appropriate class members are overridden in subclasses.
1616
- APPS-12782: Support for fallback/default mimetype/representation of a resource when a
1717
regular expression of either `*/*` or `application/*` is used in an Accept header.
18+
- APPS-13359: Add appropriate error handling for unsupported return data types for custom actions.
19+
- APPS-13361: Add support for return type of literals i.e. datatype classes for actions.
20+
- APPS-13650: Add support for %CSP.Stream for return type of actions.
1821

1922
### Changed
2023
- APPS-13152: Locked down methods as final in classes part of public API.
24+
- APPS-13361: Remove constraint of JSON for action return types in handlers (keep it for content for now).
2125

2226
### Fixed
2327
- APPS-13327: Fix a small issue in `$$$OperationAction` macro where lack of
2428
parentheses could cause invalid equality checks against an action name.
25-
26-
### Security
27-
-
28-
29-
### Removed
30-
-
31-
32-
### Deprecated
33-
-
29+
- APPS-13388: Swap `write` for `do` when using `%ToJSON()` to write to the current
30+
device to avoid `<MAXLEN>` errors.
31+
- APPS-13698: Prevent spurious compilation errors validating default representations
3432

3533
## [1.0.1] - 2022-05-25
3634
- Last released version before CHANGELOG existed.

cls/_pkg/isc/rest/handler.cls

Lines changed: 18 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ ClassMethod GetDocumentationBuildStatus() As %Status [ Final ]
211211
} Catch {
212212
Set buildParameters.userContext = $Get($$$DocParameterBuildUserGbl,"")
213213
}
214-
Write status.%ToJSON()
214+
Do status.%ToJSON()
215215
Return $$$OK
216216
}
217217

@@ -245,7 +245,7 @@ ClassMethod BuildDocumentationEndpoint() As %Status [ Final ]
245245
Do ..ReportHttpStatusCode(..#HTTP423LOCKED)
246246
Set %response.ContentType = "application/json"
247247
Set status = {"statusUrl":"/build-documentation","statusMethod":"GET","buildStarted":false}
248-
Write status.%ToJSON()
248+
Do status.%ToJSON()
249249
Return $$$OK
250250
}
251251
Set $$$DocLocksBuildGbl = 1
@@ -257,7 +257,7 @@ ClassMethod BuildDocumentationEndpoint() As %Status [ Final ]
257257
Set %response.ContentType = "application/json"
258258
Set status = {"statusUrl":"/build-documentation","statusMethod":"GET"}
259259
Do status.%Set("buildStarted", $Test, "boolean")
260-
Write status.%ToJSON()
260+
Do status.%ToJSON()
261261
Return $$$OK
262262
}
263263

@@ -377,15 +377,15 @@ ClassMethod Supports(pEndpoint As %String, pHTTPVerb As %String, pRequest As %CS
377377
/// Check whether a given operation is supported for the resource.
378378
/// Returns 0 if the operation is not supported and sets the response status
379379
/// to 404 Not Found. <br />
380-
ClassMethod CheckSupports(pResourceClass As %Dictionary.CacheClassname, pOperation As %String, pType As %String) As %Boolean [ Final, Internal, Private ]
380+
ClassMethod CheckSupports(pResourceClass As %Dictionary.Classname, pOperation As %String, pType As %String) As %Boolean [ Final, Internal, Private ]
381381
{
382382
// Wrap with $Get() on %request in case this is called during Open API generation
383383
// and %request is not available.
384384
Return $ClassMethod(pResourceClass, "Supports", pOperation, pType, $Get(%request))
385385
}
386386

387387
/// Checks both strategy- and resource-level permissions
388-
ClassMethod CheckAllPermissions(pResourceClass As %Dictionary.CacheClassname, pID As %String, pOperation As %String, pUserContext As %String, ByRef pURLParams) As %Boolean [ Final ]
388+
ClassMethod CheckAllPermissions(pResourceClass As %Dictionary.Classname, pID As %String, pOperation As %String, pUserContext As %String, ByRef pURLParams) As %Boolean [ Final ]
389389
{
390390
Set strategy = ..AuthenticationStrategy()
391391
Set authorized = 1
@@ -504,7 +504,7 @@ ClassMethod Create(resourceName As %String) As %Status [ Final ]
504504

505505
/// Wrapper around <method>Supports</method> for the <method>Create</method>
506506
/// operation. Use this wrapper so it can be invoked from Open API generation.
507-
ClassMethod CheckSupportsCreate(pResourceClass As %Dictionary.CacheClassname) [ CodeMode = expression, Final, Internal ]
507+
ClassMethod CheckSupportsCreate(pResourceClass As %Dictionary.Classname) [ CodeMode = expression, Final, Internal ]
508508
{
509509
..CheckSupports(pResourceClass, $$$OperationCreate, $$$TypeOperationClass)
510510
}
@@ -557,7 +557,7 @@ ClassMethod CollectionQuery(resourceName As %String) As %Status [ Final ]
557557

558558
/// Wrapper around <method>Supports</method> for the <method>CollectionQuery</method>
559559
/// operation. Use this wrapper so it can be invoked from Open API generation.
560-
ClassMethod CheckSupportsCollectionQuery(pResourceClass As %Dictionary.CacheClassname) [ CodeMode = expression, Final, Internal ]
560+
ClassMethod CheckSupportsCollectionQuery(pResourceClass As %Dictionary.Classname) [ CodeMode = expression, Final, Internal ]
561561
{
562562
..CheckSupports(pResourceClass, $$$OperationQuery, $$$TypeOperationClass)
563563
}
@@ -593,7 +593,7 @@ ClassMethod Retrieve(resourceName As %String, id As %String) As %Status [ Final
593593

594594
/// Wrapper around <method>Supports</method> for the <method>Retrieve</method>
595595
/// operation. Use this wrapper so it can be invoked from Open API generation.
596-
ClassMethod CheckSupportsRetrieve(pResourceClass As %Dictionary.CacheClassname) [ CodeMode = expression, Final, Internal ]
596+
ClassMethod CheckSupportsRetrieve(pResourceClass As %Dictionary.Classname) [ CodeMode = expression, Final, Internal ]
597597
{
598598
..CheckSupports(pResourceClass, $$$OperationRead, $$$TypeOperationInstance)
599599
}
@@ -634,7 +634,7 @@ ClassMethod Construct(resourceName As %String) As %Status [ Final ]
634634
/// Wrapper around <method>Supports</method> for the <method>Construct</method>
635635
/// operation. Use this wrapper so it can be invoked from Open API generation.
636636
/// $$$OperationRead with no ID, or $$$OperationActionNew, is usable as validation for this special case.
637-
ClassMethod CheckSupportsConstruct(pResourceClass As %Dictionary.CacheClassname) [ CodeMode = expression, Final, Internal ]
637+
ClassMethod CheckSupportsConstruct(pResourceClass As %Dictionary.Classname) [ CodeMode = expression, Final, Internal ]
638638
{
639639
..CheckSupports(pResourceClass, $$$OperationRead, $$$TypeOperationClass) ||
640640
..CheckSupports(pResourceClass, $$$OperationActionNew, $$$TypeOperationClass)
@@ -678,7 +678,7 @@ ClassMethod Update(resourceName As %String, id As %String) As %Status [ Final ]
678678

679679
/// Wrapper around <method>Supports</method> for the <method>Update</method>
680680
/// operation. Use this wrapper so it can be invoked from Open API generation.
681-
ClassMethod CheckSupportsUpdate(pResourceClass As %Dictionary.CacheClassname) [ CodeMode = expression, Final, Internal ]
681+
ClassMethod CheckSupportsUpdate(pResourceClass As %Dictionary.Classname) [ CodeMode = expression, Final, Internal ]
682682
{
683683
..CheckSupports(pResourceClass, $$$OperationUpdate, $$$TypeOperationInstance)
684684
}
@@ -718,7 +718,7 @@ ClassMethod Delete(resourceName As %String, id As %String) As %Status [ Final ]
718718

719719
/// Wrapper around <method>Supports</method> for the <method>Delete</method>
720720
/// operation. Use this wrapper so it can be invoked from Open API generation.
721-
ClassMethod CheckSupportsDelete(pResourceClass As %Dictionary.CacheClassname) [ CodeMode = expression, Final, Internal ]
721+
ClassMethod CheckSupportsDelete(pResourceClass As %Dictionary.Classname) [ CodeMode = expression, Final, Internal ]
722722
{
723723
..CheckSupports(pResourceClass, $$$OperationDelete, $$$TypeOperationInstance)
724724
}
@@ -765,7 +765,7 @@ ClassMethod DispatchClassAction(resourceName As %String, action As %String) As %
765765

766766
/// Wrapper around <method>Supports</method> for the <method>DispatchClassAction</method>
767767
/// operation. Use this wrapper so it can be invoked from Open API generation.
768-
ClassMethod CheckSupportsDispatchClassAction(pResourceClass As %Dictionary.CacheClassname, pAction As %String) [ CodeMode = expression, Final, Internal ]
768+
ClassMethod CheckSupportsDispatchClassAction(pResourceClass As %Dictionary.Classname, pAction As %String) [ CodeMode = expression, Final, Internal ]
769769
{
770770
..CheckSupports(pResourceClass, $$$OperationAction(pAction), $$$TypeOperationClass)
771771
}
@@ -813,12 +813,12 @@ ClassMethod DispatchInstanceAction(resourceName As %String, id As %String, actio
813813

814814
/// Wrapper around <method>Supports</method> for the <method>DispatchInstanceAction</method>
815815
/// operation. Use this wrapper so it can be invoked from Open API generation.
816-
ClassMethod CheckSupportsDispatchInstanceAction(pResourceClass As %Dictionary.CacheClassname, pAction As %String) [ CodeMode = expression, Final, Internal ]
816+
ClassMethod CheckSupportsDispatchInstanceAction(pResourceClass As %Dictionary.Classname, pAction As %String) [ CodeMode = expression, Final, Internal ]
817817
{
818818
..CheckSupports(pResourceClass, $$$OperationAction(pAction), $$$TypeOperationInstance)
819819
}
820820

821-
ClassMethod FindActionClass(pResource As %String, pAction As %String, pTarget As %String, Output pResourceClass As %Dictionary.CacheClassname) As %Dictionary.CacheClassname [ Final, Private ]
821+
ClassMethod FindActionClass(pResource As %String, pAction As %String, pTarget As %String, Output pResourceClass As %Dictionary.Classname) As %Dictionary.Classname [ Final, Private ]
822822
{
823823
#dim %response As %CSP.Response
824824
#dim %request As %CSP.Request
@@ -827,19 +827,6 @@ ClassMethod FindActionClass(pResource As %String, pAction As %String, pTarget As
827827
Set contentType = ..GetMediaTypeFromContentType()
828828
If (acceptsList = "") {
829829
Set acceptsList = $ListBuild($Char(0))
830-
} Else {
831-
// Validation
832-
Set ptr = 0
833-
While $ListNext(acceptsList,ptr,accepts) {
834-
If (accepts = "*/*") || (accepts = "application/*") {
835-
// TODO: Support this with fallback
836-
Continue
837-
}
838-
If '$Match(accepts,..#AllowedMediaTypeRegex) {
839-
Do ..ReportHttpStatusCode(..#HTTP406NOTACCEPTABLE, $$$ERROR($$$GeneralError,"Only JSON-based media types are supported."))
840-
Return ""
841-
}
842-
}
843830
}
844831

845832
Set contentTypeList = $ListBuild(contentType)
@@ -886,19 +873,19 @@ ClassMethod FindActionClass(pResource As %String, pAction As %String, pTarget As
886873
Return ""
887874
}
888875

889-
ClassMethod FindAcceptedClass(pResource As %String) As %Dictionary.CacheClassname [ Final, Private ]
876+
ClassMethod FindAcceptedClass(pResource As %String) As %Dictionary.Classname [ Final, Private ]
890877
{
891878
#dim %request As %CSP.Request
892879
Quit ..FindClass(..GetMediaTypeListFromAcceptHeader(),pResource,..#HTTP406NOTACCEPTABLE,1)
893880
}
894881

895-
ClassMethod FindContentClass(pResource As %String) As %Dictionary.CacheClassname [ Final, Private ]
882+
ClassMethod FindContentClass(pResource As %String) As %Dictionary.Classname [ Final, Private ]
896883
{
897884
#dim %request As %CSP.Request
898885
Quit ..FindClass($ListBuild(..GetMediaTypeFromContentType()),pResource,..#HTTP415UNSUPPORTEDMEDIATYPE)
899886
}
900887

901-
ClassMethod FindClass(pMediaTypeList As %Library.List, pResource As %String, pStatusWhenInvalid As %String, pLookForDefault As %Boolean = 0) As %Dictionary.CacheClassname [ Final, Private ]
888+
ClassMethod FindClass(pMediaTypeList As %Library.List, pResource As %String, pStatusWhenInvalid As %String, pLookForDefault As %Boolean = 0) As %Dictionary.Classname [ Final, Private ]
902889
{
903890
Set pointer = 0
904891
Set resourceClass = ""
@@ -1088,7 +1075,7 @@ ClassMethod outputStatus(pSC As %Status) As %Status [ Internal ]
10881075
Set tSC = ..StatusToJSON(pSC, .tJSON)
10891076
If $$$ISERR(tSC) Quit
10901077
// Write the JSON to the output device
1091-
Write tJSON.%ToJSON()
1078+
Do tJSON.%ToJSON()
10921079
} Else {
10931080
// Set plain text
10941081
Set %response.ContentType = ..#CONTENTTYPETEXT

cls/_pkg/isc/rest/model/action/t/action.cls

Lines changed: 99 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,13 @@ Method %OnValidateObject() As %Status [ Private, ServerOnly = 1 ]
4343
}
4444

4545
If (..query '= "") {
46-
If (..method = "POST") {
46+
// query
47+
If (..method '= "GET") {
4748
Set msg = $$$FormatText("Action %1: must use GET method with a query.",..name)
4849
Return $$$ERROR($$$GeneralError,msg)
4950
}
5051
} Else {
52+
// call
5153
If (..modelClass '= "") {
5254
Set msg = $$$FormatText("Action %1: modelClass may only be defined for query actions",..name)
5355
Return $$$ERROR($$$GeneralError,msg)
@@ -341,52 +343,113 @@ Method Generate(pSourceClass As %String, Output pCodeArray, Output pExpectedCont
341343
}
342344
}
343345

346+
Set resultVariableName = "result"
344347
If (..query '= "") {
345-
Set methodCall = "set result = ##class(%pkg.isc.rest.model.queryResult).FromClassQuery("_$$$QUOTE(tResultClass)_","_$$$QUOTE(class)_","_$$$QUOTE(method)_",args...)"
348+
Set methodCall = "Set "_resultVariableName_" = ##class(%pkg.isc.rest.model.queryResult).FromClassQuery("_$$$QUOTE(tResultClass)_","_$$$QUOTE(class)_","_$$$QUOTE(method)_",args...)"
346349
} Else {
347350
If (returnType = "") {
348351
Set methodCall = "Do "
349352
} Else {
350-
Set methodCall = "Set result = "
353+
Set methodCall = "Set "_resultVariableName_" = "
351354
}
352355
Set methodCall = methodCall_$Case(..target,
353356
"instance":"pInstance.",
354357
"class":"##class("_class_").")_method_"(args...)"
355358
}
356359
$$$GENERATE(methodCall)
357360
If (returnType = "%Library.Status") {
358-
$$$GENERATE("$$$ThrowOnError(result)")
361+
$$$GENERATE("$$$ThrowOnError("_resultVariableName_")")
359362
} ElseIf (returnType '= "") {
360-
If $ClassMethod(returnType,"%Extends","%Library.DynamicAbstractObject") {
361-
Set pResultContentType = "application/json"
362-
Set exportCommand = "Write result.%ToJSON()"
363-
} ElseIf $ClassMethod(returnType,"%Extends","%pkg.isc.rest.model.resource") {
364-
Set pResultContentType = $Parameter(returnType,"MEDIATYPE")
365-
Set exportCommand = "Do result.JSONExport()"
366-
} ElseIf $ClassMethod(returnType,"%Extends","%pkg.isc.rest.model.queryResult") {
367-
Set pResultContentType = $Parameter(tResultClass,"MEDIATYPE")
368-
Set exportCommand = "$$$ThrowOnError(result.JSONExport())"
369-
}ElseIf $ClassMethod(returnType,"%Extends","%Stream.Object") {
370-
Set pResultContentType = "application/octet-stream"
371-
Set exportCommand = "Do result.OutputToDevice()"
372-
} ElseIf $Parameter(returnType,"%JSONENABLED") {
373-
Set pResultContentType = "application/json"
374-
Set exportCommand = "Do result.%JSONExport()"
375-
} Else {
376-
$$$GENERATE("// Unknown handling for return type: "_returnType)
377-
$$$GENERATE("Set %response.Status = "_$$$QUOTE(##class(%CSP.REST).#HTTP204NOCONTENT))
378-
Quit
363+
Set exportCommandList = ..ComputeExportCommandList(
364+
resultVariableName, returnType, tResultClass, .pResultContentType
365+
)
366+
If (pResultContentType '= "") {
367+
$$$GENERATE("Set %response.ContentType = "_$$$QUOTE(pResultContentType))
368+
}
369+
$$$GENERATE("If $Data("_resultVariableName_") {")
370+
Set ptr = 0
371+
While $ListNext(exportCommandList,ptr,exportCommand) {
372+
$$$GENERATE(" "_exportCommand)
379373
}
380-
$$$GENERATE("Set %response.ContentType = "_$$$QUOTE(pResultContentType))
381-
$$$GENERATE("If $IsObject(result) {")
382-
$$$GENERATE(" "_exportCommand)
383374
$$$GENERATE("} Else {")
384375
$$$GENERATE(" Set %response.Status = "_$$$QUOTE(##class(%CSP.REST).#HTTP204NOCONTENT))
385376
$$$GENERATE("}")
386377
}
387378
}
388379

389-
Method GetFormalSpecMap(ByRef pModelClass As %String, Output pClass, Output pMethod, Output pArgArray, Output pNameMap, Output pReturnType)
380+
Method ComputeExportCommandList(pResultVariableName As %String, pReturnType As %Dictionary.Classname, pResultClass As %Dictionary.Classname, Output pResultContentType As %String) As %Library.List [ Internal ]
381+
{
382+
Set pResultContentType = ""
383+
Set exportCommandList = ""
384+
Set classType = $$$comClassKeyGet(pReturnType,$$$cCLASSclasstype)
385+
Set unknownReturnTypeError = "Unknown handling for return type: " _ $$$QUOTE(pReturnType) _ " for action " _ $$$QUOTE(..name)
386+
If (classType = $$$cCLASSCLASSTYPEDATATYPE) {
387+
// Data types
388+
If (pReturnType = "%Library.List") {
389+
// Specific handling for $ListBuild as a dynamic array
390+
Set pResultContentType = ##class(%CSP.REST).#CONTENTTYPEJSON
391+
Set exportCommandList = $ListBuild(
392+
"Set jsonResult = []",
393+
"Set ptr = 0",
394+
"While $ListNext("_pResultVariableName_",ptr,value) {",
395+
" Do jsonResult.%Push(value)",
396+
"}",
397+
"Do "_pResultVariableName_".%ToJSON()"
398+
)
399+
} Else {
400+
// All other data types are simple text to write out
401+
Set pResultContentType = ##class(%CSP.REST).#CONTENTTYPETEXT
402+
Set exportCommandList = $ListBuild("Write "_pResultVariableName)
403+
}
404+
} Else {
405+
If $System.CLS.IsMthd(pReturnType, "%Extends") {
406+
If $ClassMethod(pReturnType, "%Extends", "%Collection.AbstractList") {
407+
// Lists
408+
$$$ThrowStatus($$$ERROR($$$GeneralError,unknownReturnTypeError))
409+
} ElseIf $ClassMethod(pReturnType, "%Extends", "%Collection.AbstractArray") {
410+
// Arrays
411+
$$$ThrowStatus($$$ERROR($$$GeneralError,unknownReturnTypeError))
412+
} Else {
413+
// Singular Objects i.e. not collections
414+
If $ClassMethod(pReturnType,"%Extends","%Library.DynamicAbstractObject") {
415+
Set pResultContentType = "application/json"
416+
Set exportCommandList = $ListBuild("Do "_pResultVariableName_".%ToJSON()")
417+
} ElseIf $ClassMethod(pReturnType,"%Extends","%pkg.isc.rest.model.resource") {
418+
Set pResultContentType = $Parameter(pReturnType,"MEDIATYPE")
419+
Set exportCommandList = $ListBuild("Do "_pResultVariableName_".JSONExport()")
420+
} ElseIf $ClassMethod(pReturnType,"%Extends","%pkg.isc.rest.model.queryResult") {
421+
Set pResultContentType = $Parameter(pResultClass,"MEDIATYPE")
422+
Set exportCommandList = $ListBuild("$$$ThrowOnError("_pResultVariableName_".JSONExport())")
423+
} ElseIf $ClassMethod(pReturnType,"%Extends","%CSP.Stream") {
424+
// MUST be before %Stream.Object as this is a special case of that
425+
Set pResultContentType = ""
426+
Set exportCommandList = $ListBuild(
427+
"Set %response.ContentType = "_pResultVariableName_".ContentType",
428+
"Do "_pResultVariableName_".OutputToDevice()"
429+
)
430+
} ElseIf $ClassMethod(pReturnType,"%Extends","%Stream.Object") {
431+
Set pResultContentType = "application/octet-stream"
432+
Set exportCommandList = $ListBuild("Do "_pResultVariableName_".OutputToDevice()")
433+
} ElseIf $Parameter(pReturnType,"%JSONENABLED") {
434+
Set pResultContentType = "application/json"
435+
Set exportCommandList = $ListBuild("Do "_pResultVariableName_".%JSONExport()")
436+
} Else {
437+
$$$ThrowStatus($$$ERROR($$$GeneralError,unknownReturnTypeError))
438+
}
439+
}
440+
} Else {
441+
If $Parameter(pReturnType,"%JSONENABLED") {
442+
Set pResultContentType = "application/json"
443+
Set exportCommandList = $ListBuild("Do "_pResultVariableName_".%JSONExport()")
444+
} Else {
445+
$$$ThrowStatus($$$ERROR($$$GeneralError,unknownReturnTypeError))
446+
}
447+
}
448+
}
449+
Return exportCommandList
450+
}
451+
452+
Method GetFormalSpecMap(ByRef pModelClass As %String, Output pClass As %Dictionary.Classname, Output pMethod As %Dictionary.Identifier, Output pArgArray, Output pNameMap, Output pReturnType As %Dictionary.Classname) [ Internal ]
390453
{
391454
Kill pClass,pMethod,pArgArray,pNameMap,returnType
392455

@@ -399,6 +462,10 @@ Method GetFormalSpecMap(ByRef pModelClass As %String, Output pClass, Output pMet
399462
Set pClass = pModelClass
400463
Set pMethod = ..query
401464
}
465+
If '$$$comMemberDefined(pClass,$$$cCLASSquery,pMethod) {
466+
Set msg = $$$FormatText("Query '%1' is missing in class '%2' but expected as part of action '%3'",pMethod,pClass,..name)
467+
$$$ThrowStatus($$$ERROR($$$GeneralError,msg))
468+
}
402469

403470
Set formalspec = $$$comMemberKeyGet(pClass,$$$cCLASSquery,pMethod,$$$cQUERYformalspecparsed)
404471
Set pointer = 0
@@ -419,6 +486,11 @@ Method GetFormalSpecMap(ByRef pModelClass As %String, Output pClass, Output pMet
419486
Set pClass = pModelClass
420487
Set pMethod = ..call
421488
}
489+
490+
If '$$$comMemberDefined(pClass,$$$cCLASSmethod,pMethod) {
491+
Set msg = $$$FormatText("Method '%1' is missing in class '%2' but expected as part of action '%3'",pMethod,pClass,..name)
492+
$$$ThrowStatus($$$ERROR($$$GeneralError,msg))
493+
}
422494

423495
Set formalspec = $$$comMemberKeyGet(pClass,$$$cCLASSmethod,pMethod,$$$cMETHformalspecparsed)
424496
Set pointer = 0

0 commit comments

Comments
 (0)