ml-archive/submissions

Simpler use cases

Opened this issue · 1 comments

Been giving this a go and so far I like what I see in theory, however I'm looking for a simpler approach for my use. We have almost nothing that can go through a submit->validate->store cycle that this node is based on so we need some more customisation and a more simple approach to Submittable. Before I look at making some modifications I wanted to run this by you to see if there's another way.

As an example, here is a basic login form, no update, no save, no create, validation only. At the moment the most simple representation is the following:

struct LoginForm: Submittable {
    struct Submission: SubmissionType {
        let username:String
        let password:String

        func fieldEntries() throws -> [FieldEntry<LoginForm>] {
            return try [
                makeFieldEntry(keyPath: \.username, label: "Username", validators: [.count(3...)], isRequired: true),
                makeFieldEntry(keyPath: \.password, label: "Password", validators: [.count(3...)], isRequired: true),
            ]
        }
        init(_ loginForm: LoginForm?) {
            self.username = ""
            self.password = ""
        }
    }
    struct Create: Decodable { }
    init(_ create: Create) { }
    func update(_ submission: Submission) { }
    init(_ loginForm: LoginForm?) { }
}

If there isn't another way to really cut this down (as we don't need Create, update, or any of the inits really), my proposal would be a second type of SubmissionType that only implements fieldEntries and validation.

In terms of validation, I would propose something like below if there isn't a similar alternative already. The big piece that is missing at the moment is I can't find a way to populateFields with the existing submission from createValid without at least adding some more fields and filling out the inits in LoginForm struct.

func loginPost(_ req: Request) throws -> Future<Response> {
        return try req.content.decode(LoginForm.Submission.self)
            .validate(on: req).map { submission in // Would be a third option to createValid or updateValid 
                //Do some work on submissions, e.g. authenticate
                // ...

                // The work failed, repopulate fields with the submission and fall through to catchFlatMap to
                // render the page again
                try req.populateFields(with: submission)
                throw Abort(.internalServerError)
            }
            .transform(to: req.redirect(to: "somewhere"))
            .catchFlatMap { error in
                return try (req
                .privateContainer
                .make(LeafRenderer.self) as ViewRenderer)
                .render("login")
                .flatMap { view in
                    try view.encode(for: req)
                }
            }
}

Hope this all makes sense :)

A couple of quick notes:

  • the update function will become optional to implement
  • the way your LoginForm is set up does not handle missing values. The intended usage would be for the validated values to live on LoginForm, not Submission. The values for username and password on the SubmissionType would be optional so missing values can be properly indicated. This then creates the need for a Create type to have the non-optional username and password fields. Maybe it's possible to type alias LoginForm to be the Create type?
  • I will be working to integrate Submissions with https://github.com/nodes-vapor/jwt-keychain/tree/vapor-3 soon so I will probably run into similar issues and I'll try to solve them when they come up.