Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
4d1d018
created stub and test
franciscolopezsancho Sep 24, 2021
99c870b
first test passing
franciscolopezsancho Sep 26, 2021
f5cd830
following suggestions
franciscolopezsancho Sep 27, 2021
ff352a7
following review suggestion
franciscolopezsancho Sep 27, 2021
0010b1c
added ActionTestkitGenerator
franciscolopezsancho Sep 28, 2021
d0edaef
changing order expected,actualValue
franciscolopezsancho Sep 28, 2021
df0528a
Codegen already functional
franciscolopezsancho Oct 4, 2021
28a9b8a
Added test example generation
franciscolopezsancho Oct 4, 2021
f56765a
Merge branch 'main' of github.com:franciscolopezsancho/akkaserverless…
franciscolopezsancho Oct 4, 2021
91c01c0
adding docs
franciscolopezsancho Oct 4, 2021
742af2c
adding FIXME
franciscolopezsancho Oct 4, 2021
f240b9d
formatting
franciscolopezsancho Oct 5, 2021
1dd9b12
adding tag to docs
franciscolopezsancho Oct 5, 2021
b862130
Merge branch 'main' of github.com:franciscolopezsancho/akkaserverless…
franciscolopezsancho Oct 5, 2021
ee4f266
adding missing import
franciscolopezsancho Oct 5, 2021
e79b1c3
changing selectInput/Output from ModelBuilder.Commands
franciscolopezsancho Oct 5, 2021
75deb3d
Update docs/src/modules/java/pages/actions.adoc
franciscolopezsancho Oct 5, 2021
e417a83
Update docs/src/modules/java/pages/actions.adoc
franciscolopezsancho Oct 5, 2021
b221a9e
Update docs/src/modules/java/pages/actions.adoc
franciscolopezsancho Oct 5, 2021
997678f
Update samples/java-eventsourced-counter/src/test/java/com/example/ac…
franciscolopezsancho Oct 5, 2021
1fa4764
adding codegen for RPCs streaming, client and server
franciscolopezsancho Oct 5, 2021
fb1ee72
adding comment after imports
franciscolopezsancho Oct 5, 2021
f4bb328
Update docs/src/modules/java/pages/actions.adoc
franciscolopezsancho Oct 6, 2021
c65d83e
Update docs/src/modules/java/pages/actions.adoc
franciscolopezsancho Oct 6, 2021
c0b2e8a
Update docs/src/modules/java/pages/actions.adoc
franciscolopezsancho Oct 6, 2021
10a7894
Update docs/src/modules/java/pages/actions.adoc
franciscolopezsancho Oct 6, 2021
eee315b
adding suggestions
franciscolopezsancho Oct 6, 2021
ba010e0
Update docs/src/modules/java/pages/actions.adoc
franciscolopezsancho Oct 6, 2021
34524db
Update docs/src/modules/java/pages/actions.adoc
franciscolopezsancho Oct 6, 2021
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
/*
* Copyright 2021 Lightbend Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.lightbend.akkasls.codegen
package java

import com.google.common.base.Charsets
import _root_.java.nio.file.{ Files, Path }

object ActionTestKitGenerator {
import com.lightbend.akkasls.codegen.SourceGeneratorUtils._

def generate(
entity: ModelBuilder.EventSourcedEntity,
service: ModelBuilder.ActionService,
testSourceDirectory: Path,
generatedSourceDirectory: Path): Iterable[Path] = {
var generatedFiles: Seq[Path] = Vector.empty
val packageName = service.fqn.parent.javaPackage
val className = service.fqn.name

val packagePath = packageAsPath(packageName)
val testKitPath = generatedSourceDirectory.resolve(packagePath.resolve(className + "TestKit.java"))
testKitPath.getParent.toFile.mkdirs()
val sourceCode = generateSourceCode(entity, service, packageName, className)
Files.write(testKitPath, sourceCode.getBytes(Charsets.UTF_8))
generatedFiles :+= testKitPath

generatedFiles
}

private[codegen] def generateSourceCode(
entity: ModelBuilder.EventSourcedEntity,
service: ModelBuilder.ActionService,
packageName: String,
className: String): String = {
val imports = generateImports(
commandTypes(service.commands),
"",
otherImports = Seq(
"java.util.ArrayList",
"java.util.List",
"java.util.function.Function",
"java.util.Optional",
"com.akkaserverless.javasdk.action.Action",
"com.akkaserverless.javasdk.action.ActionCreationContext",
"com.akkaserverless.javasdk.testkit.impl.ActionResultImpl",
"com.akkaserverless.javasdk.impl.action.ActionEffectImpl",
"com.example.actions.CounterJournalToTopicAction",
"com.example.actions.CounterTopicApi",
"com.akkaserverless.javasdk.testkit.impl.StubActionCreationContext",
"com.akkaserverless.javasdk.testkit.impl.StubActionContext"))

val testKitClassName = s"${className}TestKit"

println(packageName)
Comment thread
franciscolopezsancho marked this conversation as resolved.
Outdated

s"""$managedComment
|package ${service.fqn.parent.javaPackage};
|
|$imports
|
|public final class $testKitClassName {
|
| private Function<ActionCreationContext, $className> actionFactory;
|
| private CounterJournalToTopicAction createAction() {
| $className action = actionFactory.apply(new StubActionCreationContext());
| action._internalSetActionContext(Optional.of(new StubActionContext()));
| return action;
| };
|
| public static $testKitClassName of(Function<ActionCreationContext, $className> actionFactory) {
| return new $testKitClassName(actionFactory);
| }
|
| private $testKitClassName(Function<ActionCreationContext, $className> actionFactory) {
| this.actionFactory = actionFactory;
| }
|
| private <E> ActionResult<E> interpretEffects(Action.Effect<E> effect) {
| return new ActionResultImpl(effect);
| }
|
| ${Format.indent(generateServices(service, entity), 2)}
|
|}
|""".stripMargin
}

def generateServices(service: ModelBuilder.ActionService, entity: ModelBuilder.EventSourcedEntity): String = {
require(!service.commands.isEmpty, "empty `commands` not allowed")

def selectOutput(command: ModelBuilder.Command): String =
if (command.outputType.name == "Empty") {
"Empty"
} else {
command.outputType.fullName
}

val domainOuterClass = entity.fqn.parent.name

service.commands
.map { command =>
s"""|public ActionResult<${selectOutput(command)}> ${lowerFirst(command.name)}(${domainOuterClass}.${command.inputType.protoName} event) {
Comment thread
franciscolopezsancho marked this conversation as resolved.
Outdated
| Action.Effect<${selectOutput(command)}> effect = createAction().${lowerFirst(command.name)}(event);
| return interpretEffects(effect);
|}
|""".stripMargin + "\n"
}
.mkString("")
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
/*
* Copyright 2021 Lightbend Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.lightbend.akkasls.codegen
package java

import com.google.protobuf.DescriptorProtos.FileDescriptorSet
import com.google.protobuf.DescriptorProtos
import com.google.protobuf.Descriptors

import _root_.java.io.FileInputStream
import _root_.java.nio.file.Paths
import scala.jdk.CollectionConverters._
import scala.util.Using
import com.google.protobuf.ExtensionRegistry
import org.slf4j.LoggerFactory

import scala.collection.mutable
import scala.util.Try

class ActionTestKitGeneratorSuite extends munit.FunSuite {
private val testData = TestData.javaStyle

test(
"it can generate an specific TestKit for the proto files " +
"in test/resources/testkit") {

val packageName = "com.example.actions"
val model = generateModel
val className = "CounterJournalToTopicAction"
val service: ModelBuilder.ActionService = {
model.get.services("com.example.actions.CounterJournalToTopic").asInstanceOf[ModelBuilder.ActionService]

}
val entity: ModelBuilder.EventSourcedEntity = {
model.get.entities("com.example.domain.Counter").asInstanceOf[ModelBuilder.EventSourcedEntity]
}

//This prints the contents of the model, services or entity. Necessary to
Comment thread
franciscolopezsancho marked this conversation as resolved.
Outdated
// see how the user-function.desc (see generateModel below) maps to ModelBuilder objects.
// println(munit.Assertions.munitPrint(entity))

val sourceCode = ActionTestKitGenerator.generateSourceCode(entity, service, packageName, className)

val expected =
"""|/* This code is managed by Akka Serverless tooling.
| * It will be re-generated to reflect any changes to your protobuf definitions.
| * DO NOT EDIT
| */
|package com.example.actions;
|
|import com.akkaserverless.javasdk.action.Action;
|import com.akkaserverless.javasdk.action.ActionCreationContext;
|import com.akkaserverless.javasdk.impl.action.ActionEffectImpl;
|import com.akkaserverless.javasdk.testkit.impl.ActionResultImpl;
|import com.akkaserverless.javasdk.testkit.impl.StubActionContext;
|import com.akkaserverless.javasdk.testkit.impl.StubActionCreationContext;
|import com.example.actions.CounterJournalToTopicAction;
|import com.example.actions.CounterTopicApi;
|import com.example.domain.CounterDomain;
|import com.google.protobuf.Any;
|import com.google.protobuf.Empty;
|import java.util.ArrayList;
|import java.util.List;
|import java.util.Optional;
|import java.util.function.Function;
|
|public final class CounterJournalToTopicActionTestKit {
|
| private Function<ActionCreationContext, CounterJournalToTopicAction> actionFactory;
|
| private CounterJournalToTopicAction createAction() {
| CounterJournalToTopicAction action = actionFactory.apply(new StubActionCreationContext());
| action._internalSetActionContext(Optional.of(new StubActionContext()));
| return action;
| };
|
| public static CounterJournalToTopicActionTestKit of(Function<ActionCreationContext, CounterJournalToTopicAction> actionFactory) {
| return new CounterJournalToTopicActionTestKit(actionFactory);
| }
|
| private CounterJournalToTopicActionTestKit(Function<ActionCreationContext, CounterJournalToTopicAction> actionFactory) {
| this.actionFactory = actionFactory;
| }
|
| private <E> ActionResult<E> interpretEffects(Action.Effect<E> effect) {
| return new ActionResultImpl(effect);
| }
|
| public ActionResult<CounterTopicApi.Increased> increase(CounterDomain.ValueIncreased event) {
| Action.Effect<CounterTopicApi.Increased> effect = createAction().increase(event);
| return interpretEffects(effect);
| }
|
| public ActionResult<CounterTopicApi.Decreased> decrease(CounterDomain.ValueDecreased event) {
| Action.Effect<CounterTopicApi.Decreased> effect = createAction().decrease(event);
| return interpretEffects(effect);
| }
|
| public ActionResult<Empty> ignore(CounterDomain.Any event) {
| Action.Effect<Empty> effect = createAction().ignore(event);
| return interpretEffects(effect);
| }
|
|}""".stripMargin

assertNoDiff(sourceCode, expected)
}

test("it can generate an specific Test stub for the entity") {}

/**
* This ModelBuilder.EventSourcedEntity is equivalent to the entity in
* test/resources/testkit/shoppingcart_domain.proto
*/
def generateModel(): Try[ModelBuilder.Model] = {

Comment thread
franciscolopezsancho marked this conversation as resolved.
val log = LoggerFactory.getLogger(getClass)
implicit val codegenLog = new Log {
override def debug(message: String): Unit = log.debug(message)
override def info(message: String): Unit = log.info(message)
}
implicit val e = TestFullyQualifiedNameExtractor

val testFilesPath = Paths.get(getClass.getClassLoader.getResource("descriptor-sets").toURI)
val descriptorFilePath =
testFilesPath.resolve("java-eventsourced-counter-user-function.desc")

val registry = ExtensionRegistry.newInstance()
registry.add(com.akkaserverless.Annotations.service)
registry.add(com.akkaserverless.Annotations.file)

Using(new FileInputStream(descriptorFilePath.toFile)) { fis =>
val fileDescSet = FileDescriptorSet.parseFrom(fis, registry)
val fileList = fileDescSet.getFileList.asScala

val descriptors: mutable.Seq[Descriptors.FileDescriptor] =
fileList.foldLeft(Array[Descriptors.FileDescriptor]())((acc, file) => accumulatedBuildFrom(acc, file))

val model = ModelBuilder.introspectProtobufClasses(descriptors)
model
}

}

private def accumulatedBuildFrom(
fileDescriptors: Array[Descriptors.FileDescriptor],
file: DescriptorProtos.FileDescriptorProto): Array[Descriptors.FileDescriptor] = {
fileDescriptors ++ List(Descriptors.FileDescriptor.buildFrom(file, fileDescriptors.toArray, true))
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/* This code is managed by Akka Serverless tooling.
* It will be re-generated to reflect any changes to your protobuf definitions.
* DO NOT EDIT
*/

package com.example.actions;
Comment thread
franciscolopezsancho marked this conversation as resolved.
Outdated

import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
import java.util.Optional;
import com.akkaserverless.javasdk.action.Action;
import com.akkaserverless.javasdk.testkit.ActionResult;
import com.akkaserverless.javasdk.action.ActionCreationContext;
import com.akkaserverless.javasdk.testkit.impl.ActionResultImpl;
import com.akkaserverless.javasdk.impl.action.ActionEffectImpl;
import com.example.actions.CounterJournalToTopicAction;
import com.akkaserverless.javasdk.testkit.impl.StubActionCreationContext;
import com.akkaserverless.javasdk.testkit.impl.StubActionContext;
import com.example.actions.CounterTopicApi;
import com.example.domain.CounterDomain;

public final class CounterJournalToTopicActionTestKit {

private Function<ActionCreationContext, CounterJournalToTopicAction> actionFactory;

private CounterJournalToTopicAction createAction() {
CounterJournalToTopicAction action = actionFactory.apply(new StubActionCreationContext());
action._internalSetActionContext(Optional.of(new StubActionContext()));
return action;
};

public static CounterJournalToTopicActionTestKit of(Function<ActionCreationContext, CounterJournalToTopicAction> actionFactory) {
return new CounterJournalToTopicActionTestKit(actionFactory);
}

private CounterJournalToTopicActionTestKit(Function<ActionCreationContext, CounterJournalToTopicAction> actionFactory) {
this.actionFactory = actionFactory;
}

private <E> ActionResult<E> interpretEffects(Action.Effect<E> effect) {
return new ActionResultImpl(effect);
}

public ActionResult<CounterTopicApi.Increased> increase(CounterDomain.ValueIncreased event) {
Action.Effect<CounterTopicApi.Increased> effect = createAction().increase(event);
return interpretEffects(effect);
}

public ActionResult<CounterTopicApi.Decreased> decrease(CounterDomain.ValueDecreased event) {
Action.Effect<CounterTopicApi.Decreased> effect = createAction().decrease(event);
return interpretEffects(effect);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.example.actions;

import com.akkaserverless.javasdk.action.Action;
import com.akkaserverless.javasdk.impl.action.ActionEffectImpl;
import com.akkaserverless.javasdk.testkit.ActionResult;
import com.example.actions.CounterTopicApi;
import com.example.actions.CounterJournalToTopicAction;
import com.google.protobuf.Empty;
import org.junit.Test;
import com.example.actions.CounterJournalToTopicActionTestKit;
import com.example.domain.CounterDomain;

import static org.junit.Assert.*;

public class CounterJournalToTopicActionTest {

@Test
public void increaseTest() {
CounterJournalToTopicActionTestKit testKit = CounterJournalToTopicActionTestKit.of(CounterJournalToTopicAction::new);
int valueToincrease = 1;
ActionResult<CounterTopicApi.Increased> result = testKit.increase(CounterDomain.ValueIncreased.newBuilder().setValue(valueToincrease).build());
assertTrue(result.isReply());
CounterTopicApi.Increased replyMessage = result.getReplyMsg();
assertEquals(replyMessage.getValue(),valueToincrease);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public interface ActionContext extends MetadataContext {
*
* @return The call level metadata.
*/
Metadata metadata();
Metadata metadata(); // TODO this is not necessary
Comment thread
franciscolopezsancho marked this conversation as resolved.
Outdated

/**
* The origin subject of the {@link CloudEvent}. For example, the entity key when the event was
Expand Down
Loading