Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow groups to be the target of a from/to epic step #449

Merged
merged 1 commit into from
Oct 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 4 additions & 6 deletions hugo/src/test/input/regressions/match-error.riddl
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,10 @@ domain PersonalBanking is {
result Title is { value: String }
command Name is { value: String }
group HomePage is {
output AccountDetailsPage is {
presents result Title
} described as "Show a blank page with a title"
input Two is {
acquires command Name
} described as "Show a blank page with a title, collect a Name"
output AccountDetailsPage presents result Title
described as "Show a blank page with a title"
input Two acquires command Name
described as "Show a blank page with a title, collect a Name"
}
} described as "A very simple app for testing"
} briefly "The user interface for the Personal Banking Application"
Expand Down
60 changes: 22 additions & 38 deletions language/src/main/scala/com/reactific/riddl/language/AST.scala
Original file line number Diff line number Diff line change
Expand Up @@ -806,7 +806,7 @@ object AST { // extends ast.AbstractDefinitions with ast.Definitions with ast.Op
super.isAssignmentCompatible(other) || {
other match
case _: Strng => true
case _ => false
case _ => false
}
}
}
Expand Down Expand Up @@ -1522,20 +1522,19 @@ object AST { // extends ast.AbstractDefinitions with ast.Definitions with ast.Op
*/
sealed trait AdaptorDefinition extends Definition

/** Base trait of any definition that is in the content of an Application
*/
/** Base trait of any definition that is in the content of an Application */
sealed trait ApplicationDefinition extends Definition

/** Base trait of any definition that is in the content of a context
*/
/** Base trait of any definition that is in the content of an Application's group */
sealed trait GroupDefinition extends Definition

/** Base trait of any definition that is in the content of a context */
sealed trait ContextDefinition extends Definition

/** Base trait of any definition that is in the content of a domain
*/
/** Base trait of any definition that is in the content of a domain */
sealed trait DomainDefinition extends Definition

/** Base trait of any definition that is in the content of an entity.
*/
/** Base trait of any definition that is in the content of an entity. */
sealed trait EntityDefinition extends Definition

/** Base trait of definitions that are part of a Handler Definition */
Expand Down Expand Up @@ -3305,19 +3304,14 @@ object AST { // extends ast.AbstractDefinitions with ast.Definitions with ast.Op
def format: String = s"${Keywords.epic} ${pathId.format}"
}

/** Sealed trait for all UI elements that derive from it
*/
sealed trait UIElement extends ApplicationDefinition

/** A group of UIElement that can be treated as a whole. For example, a form, a button group, etc.
/** A group of GroupDefinition that can be treated as a whole. For example,
* a form, a button group, etc.
* @param loc
* The location of the group
* @param id
* The unique identifier of the group
* @param types
* Type definitions to define types shared by more than one UIElement
* @param elements
* The list of UIElements
* The list of GroupDefinition
* @param brief
* A brief description of the group
* @param description
Expand All @@ -3326,19 +3320,17 @@ object AST { // extends ast.AbstractDefinitions with ast.Definitions with ast.Op
case class Group(
loc: At,
id: Identifier,
types: Seq[Type] = Seq.empty[Type],
elements: Seq[UIElement] = Seq.empty[UIElement],
alias: String,
elements: Seq[GroupDefinition] = Seq.empty[GroupDefinition],
brief: Option[LiteralString] = None,
description: Option[Description] = None
) extends UIElement {
) extends ApplicationDefinition with GroupDefinition {
override def kind: String = "Group"

override lazy val contents: Seq[ApplicationDefinition] = {
types ++ elements
}
override lazy val contents: Seq[GroupDefinition] = { elements }

/** Format the node to a string */
override def format: String = ""
override def format: String = "group $id"
}

/** A Reference to a Group
Expand All @@ -3357,8 +3349,6 @@ object AST { // extends ast.AbstractDefinitions with ast.Definitions with ast.Op
* Location of the view in the source
* @param id
* unique identifier oof the view
* @param types
* any type definitions the view needs
* @param putOut
* A result reference for the data too be presented
* @param brief
Expand All @@ -3369,17 +3359,15 @@ object AST { // extends ast.AbstractDefinitions with ast.Definitions with ast.Op
case class Output(
loc: At,
id: Identifier,
types: Seq[Type],
alias: String,
putOut: MessageRef,
brief: Option[LiteralString] = None,
description: Option[Description] = None
) extends UIElement {
) extends LeafDefinition with GroupDefinition {
override def kind: String = "Output"

override lazy val contents: Seq[ApplicationDefinition] = types

/** Format the node to a string */
override def format: String = ""
override def format: String = s"${if id.isEmpty then "inoutputput" else id.format} presents ${putOut.format}"
}

/** A reference to an View using a path identifier
Expand All @@ -3399,8 +3387,6 @@ object AST { // extends ast.AbstractDefinitions with ast.Definitions with ast.Op
* Location of the Give
* @param id
* Name of the give
* @param types
* type definitions needed for the Give
* @param putIn
* a Type reference of the type given by the user
* @param brief
Expand All @@ -3411,17 +3397,15 @@ object AST { // extends ast.AbstractDefinitions with ast.Definitions with ast.Op
case class Input(
loc: At,
id: Identifier,
types: Seq[Type],
alias: String,
putIn: MessageRef,
brief: Option[LiteralString] = None,
description: Option[Description] = None
) extends UIElement {
) extends LeafDefinition with GroupDefinition {
override def kind: String = "Input"

override lazy val contents: Seq[Definition] = types

/** Format the node to a string */
override def format: String = ""
override def format: String = s"${if id.isEmpty then "input" else id.format} acquires ${putIn.format}"
}

/** A reference to an Input using a path identifier
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,53 +25,62 @@ private[parsing] trait ApplicationParser {
}
}

private def groupAliases[u: P]: P[Unit] = {
private def groupAliases[u: P]: P[String] = {
P(
StringIn(Keywords.group, "page", "pange", "dialog", "popup", "frame", "column", "window", "section", "tab")
StringIn(Keywords.group, "page", "pane", "dialog", "popup", "frame", "column", "window", "section", "tab").!
)
}

private def groupDefinitions[u:P]: P[Seq[GroupDefinition]] = {
P {
undefined(Seq.empty[GroupDefinition]) | (group | appOutput | appInput).rep(0)
}
}

private def group[u: P]: P[Group] = {
P(
location ~ groupAliases ~/ identifier ~ is ~ open ~ types ~
(group | appOutput | appInput).rep(0) ~ close ~ briefly ~ description
).map { case (loc, id, types, elements, brief, description) =>
Group(loc, id, types, elements, brief, description)
location ~ groupAliases ~ identifier ~/ is ~ open ~
groupDefinitions ~
close ~ briefly ~ description
).map { case (loc, alias, id, elements, brief, description) =>
Group(loc, id, alias, elements, brief, description)
}
}

private def outputAliases[u: P]: P[Unit] = {
private def outputAliases[u: P]: P[String] = {
P(
StringIn(
Keywords.output,
"document",
"list",
"table"
)
"table",
"graph",
"animation",
"picture"
).!
)
}

private def appOutput[u: P]: P[Output] = {
P(
location ~ outputAliases ~/ identifier ~ is ~ open ~ types ~
Keywords.presents ~/ messageRef ~ close ~ briefly ~ description
).map { case (loc, id, types, result, brief, description) =>
Output(loc, id, types, result, brief, description)
location ~ outputAliases ~/ identifier ~ Keywords.presents ~/
messageRef ~ briefly ~ description
).map { case (loc, alias, id, putOut, brief, description) =>
Output(loc, id, alias, putOut, brief, description)
}
}

private def inputAliases[u: P]: P[Unit] = {
private def inputAliases[u: P]: P[String] = {
P(
StringIn(Keywords.input, "form", "textblock", "button", "picklist")
StringIn(Keywords.input, "form", "textblock", "button", "picklist").!
)
}

private def appInput[u: P]: P[Input] = {
P(
location ~ inputAliases ~/ identifier ~ is ~ open ~ types ~
Keywords.acquires ~ messageRef ~ close ~ briefly ~ description
).map { case (loc, id, types, yields, brief, description) =>
Input(loc, id, types, yields, brief, description)
location ~ inputAliases ~/ identifier ~ is ~ Keywords.acquires ~ messageRef ~ briefly ~ description
).map { case (loc, alias, id, putIn, brief, description) =>
Input(loc, id, alias, putIn, brief, description)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,11 @@ private[parsing] trait ReferenceParser extends CommonParser {
.map(tpl => (InputRef.apply _).tupled(tpl))
}

def groupRef[u:P]: P[GroupRef] = {
P(location ~ Keywords.group ~ pathIdentifier)
.map(tpl => (GroupRef.apply _).tupled(tpl))
}

def authorRefs[u: P]: P[Seq[AuthorRef]] = {
P(location ~ by ~ Keywords.author ~ pathIdentifier)
.map(tpl => (AuthorRef.apply _).tupled(tpl))
Expand All @@ -174,16 +179,16 @@ private[parsing] trait ReferenceParser extends CommonParser {
def processorRef[u: P]: P[ProcessorRef[Processor[?, ?]]] = {
P(
adaptorRef | applicationRef | contextRef | entityRef | projectorRef |
repositoryRef | streamletRef
repositoryRef | streamletRef
)
}

def arbitraryInteractionRef[u:P]: P[Reference[Definition]] = {
P( processorRef | sagaRef | inputRef | outputRef )
P( processorRef | sagaRef | inputRef | outputRef | groupRef )
}


def anyInteractionRef[u: P]: P[Reference[Definition]] = {
arbitraryInteractionRef | userRef
arbitraryInteractionRef | userRef
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,10 @@ class ApplicationTest extends ValidatingTest {
| result Title { content: String }
| command Name { content: String }
| group Together is {
| output One is {
| presents result Title
| } described as "Show a blank page with title"
| input Two is {
| acquires command Name
| } described as "yield a Name"
| output One presents result Title
| described as "Show a blank page with title"
| input Two acquires command Name
| described as "yield a Name"
| } described as "Show a title, collect a Name"
| } described as "A very simple app just for testing"
|} described as "Just a parsing convenience"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,14 +77,8 @@ class EpicTest extends ValidatingTest {
|
|application Improving_app is {
| group OrganizationPage is {
| input accept is {
| acquires command
| ImprovingApp.OrganizationContext.CreateOrganization
| }
| output show is {
| presents result
| ImprovingApp.OrganizationContext.OrganizationInfo
| }
| input accept acquires command ImprovingApp.OrganizationContext.CreateOrganization
| output show presents result ImprovingApp.OrganizationContext.OrganizationInfo
| }
|}
|
Expand Down Expand Up @@ -119,12 +113,11 @@ class EpicTest extends ValidatingTest {
parseAndValidateDomain(rpi, shouldFailOnErrors = false) {
case (domain: Domain, _: RiddlParserInput, msgs: Messages.Messages) =>
val errors = msgs.justErrors
info ( errors.format )
info(errors.format)
if errors.isEmpty then
domain mustNot be(empty)
domain.epics mustNot be(empty)
else
fail("Shouldn't be any errors")
else fail(errors.format)
}
}
"handle parallel group" in {
Expand Down Expand Up @@ -152,14 +145,8 @@ class EpicTest extends ValidatingTest {
|
|application Improving_app is {
| group OrganizationPage is {
| input accept is {
| acquires command
| ImprovingApp.OrganizationContext.CreateOrganization
| }
| output show is {
| presents result
| ImprovingApp.OrganizationContext.OrganizationInfo
| }
| input accept acquires command ImprovingApp.OrganizationContext.CreateOrganization
| output show presents result ImprovingApp.OrganizationContext.OrganizationInfo
| }
|}
|
Expand Down Expand Up @@ -227,14 +214,8 @@ class EpicTest extends ValidatingTest {
|
|application Improving_app is {
| group OrganizationPage is {
| input accept is {
| acquires command
| ImprovingApp.OrganizationContext.CreateOrganization
| }
| output show is {
| presents result
| ImprovingApp.OrganizationContext.OrganizationInfo
| }
| input accept acquires command ImprovingApp.OrganizationContext.CreateOrganization
| output show presents result ImprovingApp.OrganizationContext.OrganizationInfo
| }
|}
|
Expand Down Expand Up @@ -270,7 +251,7 @@ class EpicTest extends ValidatingTest {
else
domain mustNot be(empty)
domain.epics mustNot be(empty)
if msgs.nonEmpty then { }
if msgs.nonEmpty then {}
msgs.hasErrors mustBe false
succeed
}
Expand Down
21 changes: 21 additions & 0 deletions testkit/src/test/input/issues/445.riddl
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
domain example is {
user Admin is "fickle"
record Activate { name: String }
application App is {
group NewTenantPage {
group TenantInfoPage {
input NextButton acquires record Activate
}
group InfoList {
???
}
}
}
epic allow_groups_to_receive {
case UserCreatesANewCompany {
user Admin wants to "create a new tenant" so that "the tenant can do stuff"
step from user Admin "presses" to application App,
step from input App.NewTenantPage.TenantInfoPage.NextButton "routes user" to group App.NewTenantPage.InfoList
}
}
}
Loading
Loading