Kotlin example blog using Elide standalone.
Switch branches/tags
Nothing to show
Clone or download
Latest commit a8c0023 Feb 9, 2018
Permalink
Failed to load latest commit information.
gradle/wrapper Initial commit. Sep 20, 2017
settings Upgrade to elide 3.2.0. Nov 20, 2017
src/main Add LogBack configuration Feb 9, 2018
.gitignore Initial commit. Sep 20, 2017
README.md Add post content Feb 7, 2018
build.gradle Add GraphQL docs. Jan 22, 2018
gradlew Initial commit. Sep 20, 2017
gradlew.bat Initial commit. Sep 20, 2017
settings.gradle Initial commit. Sep 20, 2017

README.md

Elide-Based Post and Commenting System

This is an Elide-based post and commenting system with real user authentication. While authentication is simple, this is intended to be an illustrative example of using Elide. It uses Elide standalone to create the service.

Table of Contents

Building and Running

Building this application only requires one step. It will build a completely self-contained application jar.

$ ./gradlew build

Before running the application, please be sure your MySQL database is setup properly. The application's default settings assume you have a MySQL server running with the following settings:

SettingValue
Hostlocalhost
Port3306
Databaseelide
Userelide
Passwordelide123

If you wish to change these settings, then please modify the hibernate.cfg.xml.

After your MySQL database is up and running, run:

$ java -jar build/libs/sample-app-1.0-SNAPSHOT-all.jar

Your application is now started and your web service should now be running on port 5050.

Explanation

This sample application applies principles useful in building real world apps. Namely, it provides a mechanism for performing real database-backed security as well as usage of finely tuned security rules.

Code Structure

The code is outlined as follows:

src/main/kotlin/com/dennismcwherter/elide/app
├── filters
│   └── UserAuthFilter.kt
├── models
│   ├── Account.kt
│   ├── Comment.kt
│   ├── Post.kt
│   └── package-info.java
└── security
    ├── Settings.kt
    ├── checks
    │   ├── AccountChecks.kt
    │   ├── OwnedEntityChecks.kt
    │   └── UserChecks.kt
    ├── context
    │   ├── AccountPrincipal.kt
    │   └── ApplicationSecurityContext.kt
    └── interfaces
        └── OwnedEntity.kt

A brief explanation of the directories is below:

DirectoryDescription
filtersContains JAX-RS/Jersey web filters
modelsContains JPA annotated API models
securityContains all security related code
security/checksContains all security check implementations
security/contextHolds JAX-RS security context information
security/interfacesHolds common security check interfaces

Where is the API?

The API is generated by http://elide.io using the models found in the models package. For more information about how Elide works, please visit Elide's homepage.

User Authentication

User authentication is performed in the UserAuthFilter. While Elide can properly authorize users, it does not handle authentication out of box. As a result, we use a simple web filter to parse the User and Password headers and determine whether or not proper credentials were provided.

While there are more complex schemes to handle authentication (i.e. microservices via oauth, tokens, etc.) this is a real mechanism that could be used. However, it is likely one would want to make some slight modifications to pass secure, revokable credentials (i.e. tokens) rather than constantly sending the username and password with each request.

Security

Security rule implementations are broken out into the security/checks directory. The idea is that these are single units of computation that can then be combined in our expressions to maximize check re-usability.

We have examples of these checks operating on data as well as user principal objects.

Using the Web API

GraphQL

The GraphQL-based API has a single endpoint:

/graphql/api/v1

You can use one of 2 top-level (i.e. rootable) objects to manipulate this API:

ObjectDescription
accountUsed for all account actions. Can return current user account info.
postUsed for all post actions. Contains all posts.

Additionally, over HTTP we use the json format to send our request. Similarly, we support additional headers for login. See below for an outlined list:

HeaderValue
Content-Typeapplication/json
Acceptapplication/json
User*<your username>
Password*<your password>

* Only required if performing an action as logged in user

JSON-API

The JSON-API-based API supports 2 top-level (i.e. rootable) endpoints:

EndpointDescription
/accountUsed for all account actions. Can return current user account info.
/postUsed for all post actions. Contains all posts.

The /account endpoint can be hit by any user (though only if you are logged in will you see your information); it's also how accounts are created.

The /post endpoint can be read by any user, but can only be updated by logged in users.

If you've looked through the code you will notice that we also have a Comment entity. This entity is not rootable and, therefore, cannot be accessed via /comments. However, with similar access permissions as Post, users can access a post's comments by visiting /post/ID/comments.

Since this is a JSONAPI endpoint, you will have to supply the proper content headers. Similarly, we support login through headers as well:

HeaderValue
Content-Typeapplication/vnd.api+json
Acceptapplication/vnd.api+json
User*<your username>
Password*<your password>

* Only required if performing an action as logged in user

Creating a User

GraphQL

$ curl -X POST \
    http://127.0.0.1:5050/graphql/api/v1 \
    -H 'accept: application/json' \
    -H 'content-type: application/json' \
    -d '{
        "query": "mutation { account(op:UPSERT, data:{name:\"user1\", newPassword:\"123\"}) { edges { node { id name } } } }"
    }
'

JSON-API

$ curl -X POST \
    http://127.0.0.1:5050/api/v1/account \
    -H 'accept: application/vnd.api+json' \
    -H 'content-type: application/vnd.api+json' \
    -d '{
    "data": {
      "type": "account",
      "id": 1,
      "attributes": {
        "name": "user1",
        "newPassword": "123"
      }
    }
  }'

Creating a Post

GraphQL

curl -X POST \
    http://127.0.0.1:5050/graphql/api/v1 \
    -H 'accept: application/json' \
    -H 'content-type: application/json' \
    -H 'password: 123' \
    -H 'user: user1' \
    -d '{
         "query": "mutation { post(op:UPSERT, data:{content:\"This is my very first post!!!\", owner:{id:\"1\"}}) { edges { node { id content owner { edges { node { id } } } } } } }"
     }
'

JSON-API

$ curl -X POST \
    http://127.0.0.1:5050/api/v1/post \
    -H 'accept: application/vnd.api+json' \
    -H 'content-type: application/vnd.api+json' \
    -H 'password: 123' \
    -H 'user: user1' \
    -d '{
    "data": {
      "type": "post",
      "id": 1,
      "attributes": {
        "content": "This is my very first post!!!"
      },
      "relationships": {
        "owner": {
          "data": { "type": "account", "id": "1" }
        }
      }
    }
  }'

Creating a Comment

GraphQL

curl -X POST \
    http://127.0.0.1:5050/graphql/api/v1 \
    -H 'accept: application/json' \
    -H 'content-type: application/json' \
    -H 'password: 123' \
    -H 'user: user1' \
    -d '{
        "query": "mutation { post(ids: \"1\") { edges { node { comments(op:UPSERT, data:{content:\"This is my first comment. It is very nice!\", owner:{id:\"1\"}}) { edges { node { id content owner { edges { node { id } } } } } } } } } }"
    }
'

JSON-API

$ curl -X POST \
  http://127.0.0.1:5050/api/v1/post/1/comments \
  -H 'accept: application/vnd.api+json' \
  -H 'content-type: application/vnd.api+json' \
  -H 'password: 123' \
  -H 'user: user1' \
  -d '{
  "data": {
    "type": "comment",
    "id": 1,
    "attributes": {
      "content": "This is my first comment. It is very nice!"
    },
    "relationships": {
      "owner": {
        "data": { "type": "account", "id": "1" }
      }
    }
  }
}'

Query Posts with Comments

GraphQL

$ curl -X POST \
    http://127.0.0.1:5050/graphql/api/v1 \
    -H 'accept: application/json' \
    -H 'content-type: application/json' \
    -d '{
    "query" : "{ post { edges { node { id content comments { edges { node { id content } } } } } } }" 
}'

JSON-API

$ curl -X GET http://127.0.0.1:5050/api/v1/post?include=comments | python -m json.tool