kyoto-framework / kyoto

Asynchronous frontends with Go
https://docs.kyoto.codes
MIT License
654 stars 28 forks source link

Prototype of functional way to define pages/components #94

Closed yznts closed 2 years ago

yznts commented 2 years ago

Current working prototype looks like this:

...

func PageIndex(b *kyoto.Builder) {
    b.Template(func() *template.Template {
        return template.Must(template.New("page.index.html").ParseGlob("*.html"))
    })
    b.Init(func() {
        b.State.Set("Title", "Kyoto in a functional way")
        b.Component("UUID1", ComponentUUID)
        b.Component("UUID2", ComponentUUID)
    })
}

func ComponentUUID(b *kyoto.Builder) {
    b.Async(func() error {
        // Execute request
        resp, err := http.Get("http://httpbin.org/uuid")
        if err != nil {
            return err
        }
        // Defer closing of response body
        defer resp.Body.Close()
        // Decode response
        data := map[string]string{}
        json.NewDecoder(resp.Body).Decode(&data)
        // Set state
        b.State.Set("UUID", data["uuid"])
        // Return
        return nil
    })
}
gedw99 commented 2 years ago

Would be good is this design incorporates security.

For example what pages can a user see based on their roles.

One approach is https://github.com/cerbos/cerbos

You dont have to use a DB BTW. you can just define things on the file system.

Example usage: Policy definition: https://github.com/cerbos/demo-rest/blob/main/cerbos/policies/store_roles.yaml

Using that policy: https://github.com/cerbos/demo-rest/blob/main/service/service.go#L142

Because its pure golang this can be used server side and client side thanks to WASM, etc.


You can use this to also define pages and components perhaps too but i have not done that before.

You can also use cerbos through you whole stack to do things like "this user can see this data if they are of role X and their first name starts with "bob", etc. Its really agnostic


One thing to note is that Cerbos does NOT support GRPCWebProxy. https://github.com/cerbos/cerbos/blob/main/go.mod If you use GRPCWebProxy like i highlighted in the other issue then it will work with no other servers and also client side as wasm. here is the lib for that: https://github.com/golioth/grpc-websocket-proxy

yznts commented 2 years ago

Roles and policies are definetely out of scope of this issue (and this library). It would be nice to see this functionality as an external module (just like uikit).

In this issue I'd like to provide a new way to define pages and components (with full backward compatibility and support of a structs approach). I also in process of refactoring the project architecture during this time (related issue https://github.com/kyoto-framework/kyoto/issues/93). Current working prototype looks similar to this:

...

func PageIndex(b *kyoto.Builder) {
    b.Template(func() *template.Template {
        return template.Must(template.New("page.index.html").ParseGlob("*.html"))
    })
    b.Init(func() {
        b.State.Set("Title", "Kyoto in a functional way")
        b.Component("UUID1", ComponentUUID)
        b.Component("UUID2", ComponentUUID)
    })
}

func ComponentUUID(b *kyoto.Builder) {
    b.Async(func() error {
        // Execute request
        resp, err := http.Get("http://httpbin.org/uuid")
        if err != nil {
            return err
        }
        // Defer closing of response body
        defer resp.Body.Close()
        // Decode response
        data := map[string]string{}
        json.NewDecoder(resp.Body).Decode(&data)
        // Set state
        b.State.Set("UUID", data["uuid"])
        // Return
        return nil
    })
}

I'm still thinking about possible issues related to this approach and solving compatibility issues, but overall idea is similar to code I provided. It would be nice to have feature you're asking for. But first, let's wait for architecture migration to scheduler because it will provide more abilities for external integrations.

gedw99 commented 2 years ago

Ok Security is later.

Here is a virtual structure ( in your case golang structs) Sites have Pages ( one to many ) Pages have Sections ( one to many ). You map a Role array to this later as a property of a Section. Sections have Components ( one to many )

It looks alot like a CMS pattern, and you are sort of boarding on it. You can use this approach for Design time or Runtime based systems.

The idea is that you define a Section in a page as a Placeholder. Then you can map Components into that Section.

Security can then easily be added, because you have a loose coupling between virtual structure ( Pages and Sections and Roles/Users) and the components. Because of this you can change the Security at runtime, which is def a need even for kyoto.

gedw99 commented 2 years ago

Forgot that a Pages has Pages ( one to many ) .. ..

yznts commented 2 years ago

Functional definitions will be delivered with a new architecture refactor as par of the issue https://github.com/kyoto-framework/kyoto/issues/93

yznts commented 2 years ago

@gedw99 It would be possible to implement Roles as a separate module with a new architecture. Now, main lib idea is to provide extendable core.

gedw99 commented 2 years ago

:)