Skip to content
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
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<quarkus.platform.version>3.31.4</quarkus.platform.version>
<skipITs>true</skipITs>
<surefire-plugin.version>3.5.4</surefire-plugin.version>
<dontcode-common.version>0.4.2</dontcode-common.version>
<dontcode-common.version>0.4.3-SNAPSHOT</dontcode-common.version>
</properties>
<dependencyManagement>
<dependencies>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import net.dontcode.core.project.DontCodeProjectModel;

public record GenerateProjectModel(String response, DontCodeProjectModel model)
public record GenerateProjectModel(String response, String error, DontCodeProjectModel model)
{

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,18 @@
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.quarkus.websockets.next.OnError;
import io.quarkus.websockets.next.OnOpen;
import io.quarkus.websockets.next.OnTextMessage;
import io.quarkus.websockets.next.WebSocket;
import io.quarkus.websockets.next.WebSocketConnection;
import io.smallrye.common.annotation.Blocking;
import io.smallrye.mutiny.Uni;
import io.smallrye.mutiny.infrastructure.Infrastructure;
import jakarta.inject.Inject;
import net.dontcode.core.project.DontCodeProjectModel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;

@WebSocket(path = "/generate")
public class GenerateProjectResource {
private static Logger log = LoggerFactory.getLogger(GenerateProjectResource.class);
Expand All @@ -21,20 +25,26 @@ public class GenerateProjectResource {
public GenerateProjectResource() {
}


@OnOpen
public String onOpen() {
return "Hello, please describe the application you want to generate.";
}

@OnTextMessage()
public String onMessage(String message) {
return projectToString(service.generateProjectJson(message));
public Uni<String> onMessage(WebSocketConnection connection, String message) throws IOException {
return Uni.createFrom().item(() -> {
try {
GenerateProjectModel model = service.generateProjectJson(connection.id(),message);
return projectToString(model);
} catch (Throwable err) {
log.error("Error calling Application Generator", err);
return errorResponse (err);
}
}).runSubscriptionOn(Infrastructure.getDefaultWorkerPool());
}

@OnError
public String onError(Throwable throwable) {
return throwable.getMessage();
return errorResponse(throwable);
}

protected String errorResponse(Throwable throwable) {
return projectToString(new GenerateProjectModel(null, throwable.getMessage(), null));
}

protected String projectToString (GenerateProjectModel prj) {
Expand All @@ -43,6 +53,7 @@ protected String projectToString (GenerateProjectModel prj) {
try {
json = mapper.writeValueAsString(prj);
} catch (JsonProcessingException e) {
System.err.println("Error converting GenerateProjectModel to JSON " + prj);
throw new RuntimeException("Error decoding project", e);
}
return json;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package net.dontcode.prj.generate;

import dev.langchain4j.service.MemoryId;
import dev.langchain4j.service.SystemMessage;
import io.quarkiverse.langchain4j.RegisterAiService;
import jakarta.enterprise.context.SessionScoped;
import jakarta.enterprise.context.RequestScoped;

@RegisterAiService
@SystemMessage("""
Expand All @@ -16,10 +17,10 @@
These fields are included in the "fields" list of each entity, a field can have the following pre-defined types:
"number","string","date","time","date-time","currency","country","money-amount","eur-amount","usd-amount","image","link","rating","recurring-task","task-complete"
A field can be of the type of another entity as well.
Optionally, a field can reference another entity by adding "reference" to the field description and by filling all the necessaries information to link both entities.
Optionally, a field can reference another entity by adding "reference" to the field description and by filling all the necessaries information to link both entities. For now, reference type can only be "OneToMany"
""")
@SessionScoped
@RequestScoped
public interface GenerateProjectService {

GenerateProjectModel generateProjectJson (String msg);
GenerateProjectModel generateProjectJson (@MemoryId String memoryId, String msg);
}
22 changes: 21 additions & 1 deletion src/test/java/net/dontcode/prj/GenerateProjectResourceTest.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package net.dontcode.prj;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.quarkus.test.InjectMock;
import io.quarkus.test.common.http.TestHTTPResource;
import io.quarkus.test.junit.QuarkusTest;
import jakarta.websocket.*;
import net.dontcode.core.Message;
import net.dontcode.core.project.*;
import net.dontcode.prj.generate.GenerateProjectModel;
import net.dontcode.prj.generate.GenerateProjectService;
Expand All @@ -30,7 +33,7 @@ public class GenerateProjectResourceTest {
public void testGeneration() throws DeploymentException, IOException, InterruptedException {
DontCodeProjectEntities[] entities = new DontCodeProjectEntities[]{};
Mockito.when(serviceMock.generateProjectJson(anyString())).thenReturn(
new GenerateProjectModel("Here is an application that would fit",
new GenerateProjectModel("Here is an application that would fit", null,
new DontCodeProjectModel("Test", "Test application.",
new DontCodeProjectContent(
new DontCodeProjectCreation("Test App", DontCodeProjectCreationType.application, entities))))
Expand Down Expand Up @@ -58,6 +61,10 @@ public void testGeneration() throws DeploymentException, IOException, Interrupte
}

Assertions.assertNotNull(ClientTestSession.response);

GenerateProjectModel model=parseResponse (ClientTestSession.response);
Assertions.assertNotNull(model.model().name());

ClientTestSession.response=null;
session.getAsyncRemote().sendText("The application should be named Super Test").get();

Expand All @@ -70,6 +77,8 @@ public void testGeneration() throws DeploymentException, IOException, Interrupte
}

Assertions.assertNotNull(ClientTestSession.response);
model=parseResponse (ClientTestSession.response);
Assertions.assertNotNull(model.model().name());
Mockito.verify(serviceMock, Mockito.times(2)).generateProjectJson(anyString());

} catch (ExecutionException e) {
Expand All @@ -78,6 +87,17 @@ public void testGeneration() throws DeploymentException, IOException, Interrupte
}
}

private GenerateProjectModel parseResponse(String response) {
ObjectMapper mapper = new ObjectMapper();
GenerateProjectModel obj = null;
try {
obj = mapper.readValue(response, GenerateProjectModel.class);
} catch (JsonProcessingException e) {
throw new RuntimeException( "Cannot decode ProjectModel "+ response);
}
return obj;
}

@ClientEndpoint
public static class ClientTestSession {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public void testSimpleApplication () throws DeploymentException, IOException, In
}
Assertions.assertTrue(wsClient.opened, "Session was not opened");

String response=wsClient.waitForMessage(10);
String response=wsClient.waitForMessage(70);
Assertions.assertNotNull(response);

session.getBasicRemote().sendText("Please create a cooking recipe application");
Expand Down
Loading