code dump

This commit is contained in:
Wyatt J. Miller 2022-07-04 19:44:10 -04:00
parent f8165fccbd
commit 562fef9d92
28 changed files with 694 additions and 48 deletions

1
.gitignore vendored
View File

@ -1,5 +1,6 @@
.gradle
build/
gen/
!gradle/wrapper/gradle-wrapper.jar
!**/src/main/**/build/
!**/src/test/**/build/

View File

@ -1,6 +1,8 @@
val ktor_version: String by project
val kotlin_version: String by project
val logback_version: String by project
val ktorm_version: String by project
val postgresql_driver_version: String by project
plugins {
application
@ -27,10 +29,13 @@ dependencies {
implementation("io.ktor:ktor-server-content-negotiation-jvm:$ktor_version")
implementation("io.ktor:ktor-serialization-kotlinx-json-jvm:$ktor_version")
implementation("io.ktor:ktor-server-netty-jvm:$ktor_version")
implementation("org.ktorm:ktorm-core:3.4.1")
implementation("org.ktorm:ktorm-support-postgresql:3.4.1")
implementation("org.postgresql:postgresql:42.2.2")
implementation("io.ktor:ktor-server-auth:$ktor_version")
implementation("io.ktor:ktor-server-auth-jwt:$ktor_version")
implementation("org.ktorm:ktorm-core:$ktorm_version")
implementation("org.ktorm:ktorm-support-postgresql:$ktorm_version")
implementation("org.postgresql:postgresql:$postgresql_driver_version")
implementation("ch.qos.logback:logback-classic:$logback_version")
implementation("org.mindrot:jbcrypt:0.4")
testImplementation("io.ktor:ktor-server-tests-jvm:$ktor_version")
testImplementation("org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version")
}

View File

@ -2,8 +2,9 @@ package com.wyattjmiller
import io.ktor.server.application.*
import com.wyattjmiller.plugins.*
import com.wyattjmiller.routes.AuthorRoute.Companion.authorRoutes
import com.wyattjmiller.routes.RecipeRoute.Companion.recipeRoutes
import io.ktor.server.config.*
import com.wyattjmiller.routes.UserRoute.Companion.userRoutes
fun main(args: Array<String>): Unit =
io.ktor.server.netty.EngineMain.main(args)
@ -12,8 +13,7 @@ fun main(args: Array<String>): Unit =
fun Application.module() {
//val config = this.environment.config
configureRouting()
configureAuthentication()
configureSerialization()
recipeRoutes()
configureRouting()
}

View File

@ -0,0 +1,31 @@
package com.wyattjmiller.auth
import com.auth0.jwt.JWT
import com.auth0.jwt.JWTVerifier
import com.auth0.jwt.algorithms.Algorithm
import com.wyattjmiller.entities.UserLogin
import io.ktor.server.config.*
import java.util.*
class JwtConfig(config: HoconApplicationConfig) {
private val audience = config.property("audience").getString()
private val secret = config.property("secret").getString()
private val issuer = config.property("issuer").getString()
private val expirationDate = System.currentTimeMillis() + 60000
fun generateJwtToken(user: UserLogin): String {
return JWT.create()
.withAudience(audience)
.withIssuer(issuer)
.withClaim("username", user.username)
.withExpiresAt(Date(expirationDate))
.sign(Algorithm.HMAC256(secret))
}
fun verifyJwtToken() : JWTVerifier {
return JWT.require(Algorithm.HMAC256(secret))
.withAudience(audience)
.withIssuer(issuer)
.build()
}
}

View File

@ -1,16 +1,11 @@
package com.wyattjmiller.data
import io.ktor.server.config.*
import org.ktorm.database.Database
class DatabaseManager() {
var database: Database
init {
database = Database.connect("jdbc:postgresql://10.10.10.21:5432/recipefolio",
"org.postgresql.Driver",
user = "wyatt",
password = "wjm"
)
}
var database: Database = Database.connect("jdbc:postgresql://10.10.10.21:5432/recipefolio",
"org.postgresql.Driver",
user = "wyatt",
password = "wjm"
)
}

View File

@ -1,22 +1,42 @@
package com.wyattjmiller.data
import org.ktorm.entity.Entity
import org.ktorm.schema.Table
import org.ktorm.schema.int
import org.ktorm.schema.varchar
import org.ktorm.schema.*
import java.time.LocalDateTime
object DbRecipeTable : Table<DbRecipeEntity>("recipe") {
object DbRecipeTable : Table<Recipes>("recipe") {
val id = int("id").primaryKey().bindTo { it.id }
val name = varchar("name").bindTo { it.name }
val desc = varchar("description").bindTo { it.desc }
val ingredients = varchar("ingredients").bindTo { it.ingredients }
val author = int("author_id").references(DbAuthorTable) { it.author }
val createdTimestamp = datetime("created_timestamp").bindTo { it.createdTimestamp }
}
interface DbRecipeEntity : Entity<DbRecipeEntity> {
companion object : Entity.Factory<DbRecipeEntity>()
object DbAuthorTable : Table<Authors>("author") {
val id = int("id").primaryKey().bindTo { it.id }
val authorName = varchar("author_name").bindTo { it.authorName }
val user = int("user_id").references(DbUserTable) { it.user }
val createdTimestamp = datetime("created_timestamp").bindTo { it.createdTimestamp }
}
val id: Int
interface Recipes : Entity<Recipes> {
companion object : Entity.Factory<Recipes>()
val id: Int?
val name: String
val desc: String
val ingredients: String
}
val author: Authors
val createdTimestamp: LocalDateTime
}
interface Authors : Entity<Authors> {
companion object : Entity.Factory<Authors>()
val id: Int
val authorName: String
val user: Users?
val createdTimestamp: LocalDateTime
}

View File

@ -0,0 +1,33 @@
package com.wyattjmiller.data
import io.ktor.server.auth.*
import org.ktorm.entity.Entity
import org.ktorm.schema.Table
import org.ktorm.schema.datetime
import org.ktorm.schema.int
import org.ktorm.schema.varchar
import java.time.LocalDateTime
object DbUserTable : Table<Users>("user") {
val id = int("id").primaryKey().bindTo { it.id }
val firstName = varchar("first_name").bindTo { it.firstName }
val lastName = varchar("last_name").bindTo { it.lastName }
val username = varchar("username").bindTo { it.username }
val password = varchar("password").bindTo { it.password }
val emailAddress = varchar("email_address").bindTo { it.emailAddress }
val createdTimestamp = datetime("created_timestamp").bindTo { it.createdTimestamp }
//val createdAuthors = int("created_authors").references(DbAuthorTable) { it.createdAuthors }
}
interface Users : Entity<Users> {
companion object : Entity.Factory<Users>()
val id: Int
val firstName: String
val lastName: String
val username: String
val password: String
val emailAddress: String
val createdTimestamp: LocalDateTime
//val createdAuthors get() = listOf(DbAuthorTable.id)
}

View File

@ -0,0 +1,21 @@
package com.wyattjmiller.entities
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.descriptors.PrimitiveKind
@Serializable
@SerialName("Author")
data class Author(
val id: Int?,
val authorName: String,
val user: Int,
var createdTimestamp: String,
)
@Serializable
@SerialName("AuthorDraft")
data class AuthorDraft(
val authorName: String,
val user: Int? = null,
)

View File

@ -1,11 +1,24 @@
package com.wyattjmiller.entities
import com.wyattjmiller.data.Authors
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
@SerialName("Recipe")
data class Recipe(
val id: Int,
val id: Int?,
val name: String,
val desc: String,
val ingredients: String,
var createdTimestamp: String,
val author: Int,
)
@Serializable
@SerialName("RecipeDraft")
data class RecipeDraft(
val name: String,
val desc: String,
val ingredients: String,
val author: Int,
)

View File

@ -1,12 +1,32 @@
package com.wyattjmiller.entities
import kotlinx.serialization.Serializable
import java.sql.Timestamp
@Serializable
data class User(
val id: Int,
val firstName: String,
val lastName: String,
val username: String,
val emailAddress: String,
val password: String,
val createdTimestamp: String,
val createdAuthors: Int,
)
@Serializable
data class UserLogin(
val username: String,
val password: String,
)
@Serializable
data class UserDraft(
val firstName: String,
val lastName: String,
val username: String,
val emailAddress: String,
val password: String,
val createdTimestamp: String
)

View File

@ -0,0 +1,15 @@
{
"openapi": "3.0.3",
"info": {
"title": "RecipeFolio RESTful API",
"description": "Just a reference! Nothing to see here! **Wyatt proceeds to smuggle servers into a closet**",
"version": "0.0.3 SE-B012"
},
"servers": [
{
"url": "https"
}
],
"paths": {
}
}

View File

@ -0,0 +1,30 @@
package com.wyattjmiller.plugins
import com.typesafe.config.ConfigFactory
import com.wyattjmiller.auth.JwtConfig
import io.ktor.server.application.*
import io.ktor.server.auth.*
import io.ktor.server.auth.jwt.*
import io.ktor.server.config.*
fun Application.configureAuthentication() {
val config = HoconApplicationConfig(ConfigFactory.load())
val tokenManager = JwtConfig(config)
install(Authentication) {
jwt {
verifier(tokenManager.verifyJwtToken())
realm = config.property("realm").getString()
validate { jwt ->
if (jwt.payload.getClaim("username").asString().isNotEmpty()) {
JWTPrincipal(jwt.payload)
} else {
null
}
}
}
}
}

View File

@ -1,10 +1,15 @@
package com.wyattjmiller.plugins
import com.wyattjmiller.routes.AuthorRoute.Companion.authorRoutes
import com.wyattjmiller.routes.RecipeRoute.Companion.recipeRoutes
import com.wyattjmiller.routes.UserRoute.Companion.userRoutes
import io.ktor.server.routing.*
import io.ktor.server.application.*
fun Application.configureRouting() {
routing {
this@configureRouting.recipeRoutes()
this@configureRouting.authorRoutes()
this@configureRouting.userRoutes()
}
}

View File

@ -3,17 +3,13 @@ package com.wyattjmiller.plugins
import io.ktor.serialization.kotlinx.json.*
import io.ktor.server.application.*
import io.ktor.server.plugins.contentnegotiation.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import kotlinx.serialization.json.Json
fun Application.configureSerialization() {
install(ContentNegotiation) {
json()
}
routing {
get("/json/kotlinx-serialization") {
call.respond(mapOf("hello" to "world"))
}
json( Json {
explicitNulls = true
prettyPrint = true
})
}
}

View File

@ -0,0 +1,66 @@
package com.wyattjmiller.repositories
import com.wyattjmiller.data.DatabaseManager
import com.wyattjmiller.data.DbAuthorTable
import com.wyattjmiller.entities.Author
import com.wyattjmiller.entities.AuthorDraft
import org.ktorm.dsl.delete
import org.ktorm.dsl.eq
import org.ktorm.dsl.insertAndGenerateKey
import org.ktorm.dsl.update
import org.ktorm.entity.firstOrNull
import org.ktorm.entity.sequenceOf
import org.ktorm.entity.toList
import java.time.LocalDateTime
class AuthorDao : AuthorRepository {
private val db = DatabaseManager().database
override fun getAllAuthors() : List<Author> {
return db
.sequenceOf(DbAuthorTable)
.toList()
.map {
Author(it.id, it.authorName, it.user!!.id, it.createdTimestamp.toString())
}
}
override fun getAuthor(id: Int) : Author {
return db
.sequenceOf(DbAuthorTable)
.firstOrNull {
it.id eq id
}
.let {
Author(it!!.id, it.authorName, it.user!!.id, it.createdTimestamp.toString())
}
}
override fun createAuthor(author: AuthorDraft) {
db.insertAndGenerateKey(DbAuthorTable) {
set(DbAuthorTable.authorName, author.authorName)
set(DbAuthorTable.user, author.user)
set(DbAuthorTable.createdTimestamp, LocalDateTime.now())
} as Int
}
override fun updateAuthor(id: Int, author: AuthorDraft) : Boolean {
val updatedRows = db.update(DbAuthorTable) {
set(DbAuthorTable.authorName, author.authorName)
set(DbAuthorTable.user, author.user)
where {
it.id eq id
}
}
return updatedRows > 0
}
override fun deleteAuthor(id: Int) : Boolean {
val deletedRows = db.delete(DbAuthorTable) {
it.id eq id
}
return deletedRows > 0
}
}

View File

@ -0,0 +1,12 @@
package com.wyattjmiller.repositories
import com.wyattjmiller.entities.Author
import com.wyattjmiller.entities.AuthorDraft
interface AuthorRepository {
fun getAllAuthors() : List<Author>
fun getAuthor(id: Int) : Author?
fun createAuthor(author: AuthorDraft)
fun updateAuthor(id: Int, author: AuthorDraft) : Boolean
fun deleteAuthor(id: Int) : Boolean
}

View File

@ -3,8 +3,13 @@ package com.wyattjmiller.repositories
import com.wyattjmiller.data.DatabaseManager
import com.wyattjmiller.data.DbRecipeTable
import com.wyattjmiller.entities.Recipe
import com.wyattjmiller.entities.RecipeDraft
import org.ktorm.dsl.delete
import org.ktorm.dsl.eq
import org.ktorm.dsl.insertAndGenerateKey
import org.ktorm.dsl.update
import org.ktorm.entity.*
import java.time.LocalDateTime
class RecipeDao : RecipeRepository {
private val db = DatabaseManager()
@ -13,25 +18,52 @@ class RecipeDao : RecipeRepository {
return db.database
.sequenceOf(DbRecipeTable)
.toList()
.map { Recipe(it.id, it.name, it.desc, it.ingredients) }
.map {
Recipe(it.id, it.name, it.desc, it.ingredients, it.createdTimestamp.toString(), it.author.id)
}
}
override fun getRecipe(id: Int) : Recipe {
return db.database
.sequenceOf(DbRecipeTable)
.firstOrNull { it.id eq id }
.let { Recipe(it!!.id, it.name, it.desc, it.ingredients) }
.firstOrNull {
it.id eq id
}
.let {
Recipe(it!!.id, it.name, it.desc, it.ingredients, it.createdTimestamp.toString(), it.author.id)
}
}
override fun createRecipe(recipe: Recipe) {
TODO("Not yet implemented")
override fun createRecipe(recipe: RecipeDraft) : Recipe {
val key = db.database.insertAndGenerateKey(DbRecipeTable) {
set(DbRecipeTable.name, recipe.name)
set(DbRecipeTable.desc, recipe.desc)
set(DbRecipeTable.ingredients, recipe.ingredients)
set(DbRecipeTable.createdTimestamp, LocalDateTime.now())
set(DbRecipeTable.author, recipe.author)
} as Int
return Recipe(key, recipe.name, recipe.desc, recipe.ingredients, LocalDateTime.now().toString(), recipe.author)
}
override fun updateRecipe(id: Int, recipe: Recipe): Boolean {
TODO("Not yet implemented")
override fun updateRecipe(id: Int, recipe: RecipeDraft) : Boolean {
val updatedRows = db.database.update(DbRecipeTable) {
set(DbRecipeTable.name, recipe!!.name)
set(DbRecipeTable.desc, recipe!!.desc)
set(DbRecipeTable.ingredients, recipe!!.ingredients)
where {
it.id eq id
}
}
return updatedRows > 0
}
override fun deleteRecipe(id: Int) {
TODO("Not yet implemented")
override fun deleteRecipe(id: Int) : Boolean {
val deletedRows = db.database.delete(DbRecipeTable) {
it.id eq id
}
return deletedRows > 0
}
}

View File

@ -1,11 +1,12 @@
package com.wyattjmiller.repositories
import com.wyattjmiller.entities.Recipe
import com.wyattjmiller.entities.RecipeDraft
interface RecipeRepository {
fun getAllRecipies() : List<Recipe>
fun getRecipe(id: Int) : Recipe?
fun createRecipe(recipe: Recipe)
fun updateRecipe(id: Int, recipe: Recipe): Boolean
fun deleteRecipe(id: Int)
fun createRecipe(recipe: RecipeDraft) : Recipe
fun updateRecipe(id: Int, recipe: RecipeDraft) : Boolean
fun deleteRecipe(id: Int) : Boolean
}

View File

@ -0,0 +1,75 @@
package com.wyattjmiller.repositories
import com.wyattjmiller.data.DatabaseManager
import com.wyattjmiller.data.DbAuthorTable
import com.wyattjmiller.data.DbUserTable
import com.wyattjmiller.data.DbUserTable.password
import com.wyattjmiller.data.DbUserTable.username
import com.wyattjmiller.entities.Author
import com.wyattjmiller.entities.User
import com.wyattjmiller.entities.UserDraft
import com.wyattjmiller.entities.UserLogin
import org.ktorm.dsl.*
import org.ktorm.entity.firstOrNull
import org.ktorm.entity.sequenceOf
import java.time.LocalDateTime
class UserDao : UserRepository {
private val db = DatabaseManager().database
fun usernameCheck(user: UserLogin) : UserLogin? {
return db.from(DbUserTable)
.select()
.where { username eq user.username }
.map {
// don't know what to do here
UserLogin(user.username, user.password)
}.firstOrNull()
}
fun passwordCheck(user: UserLogin) : UserLogin? {
return db.from(DbUserTable)
.select()
.where { password eq user.password }
.map {
// don't know what to do here
UserLogin(user.username, user.password)
}.firstOrNull()
}
fun getAllUsers() {
TODO("Joke's on you! This function is not implemented yet. -Wyatt")
}
override fun getUser(id: Int): User? {
return db
.sequenceOf(DbUserTable)
.firstOrNull {
it.id eq id
}
.let {
User(it!!.id, it.firstName, it.lastName, it.username, it.password, it.emailAddress, it.createdTimestamp.toString(), 0)
}
}
override fun createUser(user: UserDraft): User {
val key = db.insertAndGenerateKey(DbUserTable) {
set(DbUserTable.firstName, user.firstName)
set(DbUserTable.lastName, user.lastName)
set(DbUserTable.username, user.username)
set(DbUserTable.password, user.password)
set(DbUserTable.emailAddress, user.emailAddress)
set(DbUserTable.createdTimestamp, LocalDateTime.now())
} as Int
return User(key, user.firstName, user.lastName, user.username, user.password, user.emailAddress, LocalDateTime.now().toString(), 0)
}
override fun updateUser(id: Int, user: UserDraft): Boolean {
TODO("Not yet implemented")
}
override fun deleteUser(id: Int): Boolean {
TODO("Not yet implemented")
}
}

View File

@ -0,0 +1,11 @@
package com.wyattjmiller.repositories
import com.wyattjmiller.entities.User
import com.wyattjmiller.entities.UserDraft
interface UserRepository {
fun getUser(id: Int) : User?
fun createUser(user: UserDraft) : User
fun updateUser(id: Int, user: UserDraft) : Boolean
fun deleteUser(id: Int) : Boolean
}

View File

@ -0,0 +1,69 @@
package com.wyattjmiller.routes
import com.wyattjmiller.entities.AuthorDraft
import com.wyattjmiller.repositories.AuthorDao
import io.ktor.http.*
import io.ktor.server.application.*
import io.ktor.server.request.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
class AuthorRoute {
companion object {
private val author = AuthorDao()
private fun Route.getAllAuthors() {
get("/author") {
call.respond(author.getAllAuthors())
}
}
private fun Route.getAuthor() {
get("/author/{id?}") {
val id = call.parameters["id"]?.toInt() ?: return@get call.respond(
HttpStatusCode.BadRequest, "Missing author identifier :("
)
val res = author.getAuthor(id) ?: return@get call.respond(
HttpStatusCode.NotFound, "No recipe found :("
)
call.respond(res)
}
}
private fun Route.createAuthor() {
post("/author") {
val req = call.receive<AuthorDraft>()
val res = author.createAuthor(req)
call.respond(HttpStatusCode.Created)
}
}
private fun Route.updateAuthor() {
put("/author/{id?}") {
val req = call.receive<AuthorDraft>()
val id = call.parameters["id"]?.toIntOrNull() ?: return@put call.respond(
HttpStatusCode.BadRequest, "Missing author identifier :("
)
if (author.updateAuthor(id, req)) {
call.respond(HttpStatusCode.Created)
} else {
call.respond(
HttpStatusCode.NotFound, "No author found :("
)
}
}
}
fun Application.authorRoutes() {
routing {
getAllAuthors()
getAuthor()
createAuthor()
updateAuthor()
}
}
}
}

View File

@ -1,8 +1,10 @@
package com.wyattjmiller.routes
import com.wyattjmiller.entities.RecipeDraft
import com.wyattjmiller.repositories.RecipeDao
import io.ktor.http.*
import io.ktor.server.application.*
import io.ktor.server.request.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
@ -30,10 +32,36 @@ class RecipeRoute {
}
}
private fun Route.createRecipe() {
post("/recipe") {
val req = call.receive<RecipeDraft>()
val res = rec.createRecipe(req)
call.respond(res)
}
}
private fun Route.updateRecipe() {
put("/recipe/{id?}") {
val req = call.receive<RecipeDraft>()
val id = call.parameters["id"]?.toIntOrNull() ?: return@put call.respond(
HttpStatusCode.BadRequest,"Missing recipe identifier :("
)
if (rec.updateRecipe(id, req)) {
call.respond(HttpStatusCode.OK)
} else {
call.respond(HttpStatusCode.NotFound,
"No recipe found :(")
}
}
}
fun Application.recipeRoutes() {
routing {
getAllRecipes()
getRecipe()
createRecipe()
updateRecipe()
}
}
}

View File

@ -0,0 +1,99 @@
package com.wyattjmiller.routes
import com.typesafe.config.ConfigFactory
import com.wyattjmiller.auth.JwtConfig
import com.wyattjmiller.entities.UserDraft
import com.wyattjmiller.entities.UserLogin
import com.wyattjmiller.repositories.UserDao
import io.ktor.http.*
import io.ktor.server.application.*
import io.ktor.server.auth.*
import io.ktor.server.config.*
import io.ktor.server.request.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
class UserRoute {
companion object {
private val user = UserDao()
private val tokenManager = JwtConfig(HoconApplicationConfig(ConfigFactory.load()))
private fun Route.login() {
post("/login") {
val userCreds = call.receive<UserLogin>()
val usernameResult = user.usernameCheck(userCreds)
val passwordResult = user.passwordCheck(userCreds)
if (usernameResult == null || passwordResult == null) {
call.respond(HttpStatusCode.BadRequest)
}
//if (!BCrypt.checkpw(password, res?.password)) {
// call.respond(HttpStatusCode.BadRequest)
// return@post
//}
val token = tokenManager.generateJwtToken(userCreds)
call.respond(HttpStatusCode.OK, token)
}
}
private fun Route.getAllUsers() {
get("/user") {
call.respond(user.getAllUsers())
}
}
private fun Route.getUser() {
authenticate {
get("/user/{id?}") {
val id = call.parameters["id"]?.toInt() ?: return@get call.respond(
HttpStatusCode.BadRequest, "Missing user identifier :("
)
val res = user.getUser(id) ?: return@get call.respond(
HttpStatusCode.NotFound, "No recipe found :("
)
call.respond(res)
}
}
}
private fun Route.createUser() {
post("/user") {
val req = call.receive<UserDraft>()
val res = user.createUser(req)
call.respond(res)
}
}
private fun Route.updateUser() {
authenticate {
put("/user/{id?}") {
val req = call.receive<UserDraft>()
val id = call.parameters["id"]?.toIntOrNull() ?: return@put call.respond(
HttpStatusCode.BadRequest, "Missing author identifier :("
)
if (user.updateUser(id, req)) {
call.respond(HttpStatusCode.Accepted)
} else {
call.respond(
HttpStatusCode.NotFound, "No author found :("
)
}
}
}
}
fun Application.userRoutes() {
routing {
login()
getUser()
createUser()
updateUser()
}
}
}
}

View File

@ -17,3 +17,8 @@ storage {
dbUser = "wyatt"
dbPasswd = "wjm"
}
secret = "!$#terces!$#"
issuer = "http://0.0.0.0:8080/"
audience = "http://0.0.0.0:8080/hello"
realm = "Access to 'hello'"

View File

@ -0,0 +1,25 @@
// Get all authors
GET http://localhost:8080/author
###
// Get one author
GET http://localhost:8080/author/3
###
// Create author
POST http://localhost:8080/author
Content-Type: application/json
{
"authorName": "Aidan Tiernan",
"user": 1
}
###
// Create author without a user
POST http://localhost:8080/author
Content-Type: application/json
{
"authorName": "Cayde Alpers"
}

View File

@ -0,0 +1,26 @@
### Valid login
POST http://localhost:8080/login
Content-Type: application/json
{
"username": "wymiller",
"password": "wjm192837465"
}
### Incorrect username
POST http://localhost:8080/login
Content-Type: application/json
{
"username": "denthead",
"password": "wjm192837465"
}
### Incorrect password
POST http://localhost:8080/login
Content-Type: application/json
{
"username": "wymiller",
"password": "averyisgay"
}

View File

@ -0,0 +1,10 @@
// Get all of the recipes
GET http://localhost:8080/recipe
###
// Get one recipe
GET http://localhost:8080/recipe/2
###
// ANIME NOOOOOODLE
GET http://localhost:8080/recipe/4

View File

@ -0,0 +1,2 @@
// Get user
GET http://localhost:8080/user/1