beryx / text-io

A library for creating interactive console applications in Java
http://text-io.beryx.org/
Apache License 2.0
342 stars 45 forks source link

Cookies on WebTextTerminal #32

Open maxbrito500 opened 3 years ago

maxbrito500 commented 3 years ago

Hello,

Was able to make progress quickly, the command line app is looking great thanks to Text-IO.

What is now missing is persistent memory. This can be done on the swing and command line interfaces very easily using files on disk for storage to remember a user that is logged, but not so trivial for the WebTextTerminal because the ContextHandlers don't seem to be exposed for usage. Otherwise would be possible to solve this by extracting URL paths and cookies.

Perhaps exposing the Context object from Ratpack is the simplest way? I imagine it is not easy to create a common mechanism for both spark and ratpack, on this case just needed for RatPack.

How would you recommend that we can access cookies and the URL from within Text-IO for the purpose of persistent user-memory? (to know who is logged inside the app).

Again: many, many thanks. Really good library.

maxbrito500 commented 3 years ago

Found a solution editing file RunnerData.java to include the Context as argument. In case it helps, these were the changes:

Adding on top: private Context context;

And then on bottom:

public void setSessionData(Map<String, String> sessionData, Context context) {
        this.context = context;
        this.sessionData = sessionData;
    }

    public Context getContext() {
        return context;
    }

Then finally inside RatPackTextIoApp to modify the createRunnerData method to include the Context as argument on the runnerData.setSession method:

    private RunnerData createRunnerData(String initData, ContextHolder ctxHolder) {
        RunnerData runnerData = new RunnerData(initData);
        Session session = ctxHolder.context.get(Session.class);
        Map<String, String> sessionData = sessionDataProvider.apply(session);
        runnerData.setSessionData(sessionData, ctxHolder.context);
        return runnerData;
    }

From here the forward becomes then possible to read/write the cookies from each web request and generate the memory persistent data. Should also permit to recognize paths and work a bit more as a webpages. Here is an example for writing a cookie from the Weather.java app:

    @Override
    public void accept(TextIO textIO, RunnerData runnerData) {

        TextTerminal<?> terminal = textIO.getTextTerminal();
        String initData = (runnerData == null) ? null : runnerData.getInitData();
        AppUtil.printGsonMessage(terminal, initData);

        Context context = runnerData.getContext();
        context.getResponse().cookie("whiskey", "make-it-rye");
....
....

Question: Is it worth doing a pull request with these modifications?

On the local command line interfaces is possible to write things on disk, on the web browser we are limited to these methods. An idea could be perhaps to provide a "properties" method that can save these tidybits of data either on disk or as cookies depending on what is used?

Again, thanks for the great library. It is a fantastic piece of work.

siordache commented 3 years ago

That's a great solution. A pull request is more than welcome. Thanks!

maxbrito500 commented 3 years ago

Will do. If possible, need your opinion. It is possible to set the cookies only until the first terminal command is sent. After that it does not seem possible to send cookies any longer using the context object.

For example:

context.getResponse().cookie("key1", "value1");
terminal.println();
context.getResponse().cookie("key2", "value2");

Key1 gets written to the cookies on the browser page. But key2 will be ignored, albeit still listed inside the context list when applying: Set<Cookie> list = context.getResponse().getCookies();

It seems that after a communication with the browser that the context objects loses "context", and some other object is created instead. I was looking where to catch it but I'm unable to find the right location.

Would you have any ideas where to inject the cookie info for the browser?

maxbrito500 commented 3 years ago

Hello,

I've created a hack. Meaning that a static map list is created with the list of current SESSIONIDs and which cookies should be written. Then before sending a reply the cookies are written back on the proper response object. I've started uploading the code, so this technique is visible here: https://github.com/nya-official/nya-server/blob/main/src/main/java/org2/beryx/textio/web/RatpackDataServer.java#L227-L233

It works albeit not exactly an efficient solution. Just sharing in case you have some thoughts on how to solve this better within textIo.

siordache commented 3 years ago

I think your hack is ok. I don't have a better solution.