F# API #18
@ -58,11 +58,14 @@ module Entities =
|
|||||||
m.Property(fun e -> e.requestId).IsRequired () |> ignore
|
m.Property(fun e -> e.requestId).IsRequired () |> ignore
|
||||||
m.Property(fun e -> e.asOf).IsRequired () |> ignore
|
m.Property(fun e -> e.asOf).IsRequired () |> ignore
|
||||||
m.Property(fun e -> e.status).IsRequired() |> ignore
|
m.Property(fun e -> e.status).IsRequired() |> ignore
|
||||||
|
m.Property(fun e -> e.text) |> ignore
|
||||||
m.HasOne(fun e -> e.request)
|
m.HasOne(fun e -> e.request)
|
||||||
.WithMany(fun r -> r.history :> IEnumerable<History>)
|
.WithMany(fun r -> r.history :> IEnumerable<History>)
|
||||||
.HasForeignKey(fun e -> e.requestId :> obj)
|
.HasForeignKey(fun e -> e.requestId :> obj)
|
||||||
|> ignore)
|
|> ignore)
|
||||||
|> ignore
|
|> ignore
|
||||||
|
let typ = mb.Model.FindEntityType(typeof<History>)
|
||||||
|
let prop = typ.FindProperty("text")
|
||||||
mb.Model.FindEntityType(typeof<History>).FindProperty("text").SetValueConverter (OptionConverter<string> ())
|
mb.Model.FindEntityType(typeof<History>).FindProperty("text").SetValueConverter (OptionConverter<string> ())
|
||||||
|
|
||||||
/// Note is a note regarding a prayer request that does not result in an update to its text
|
/// Note is a note regarding a prayer request that does not result in an update to its text
|
||||||
@ -171,13 +174,9 @@ open System.Linq
|
|||||||
open System.Threading.Tasks
|
open System.Threading.Tasks
|
||||||
|
|
||||||
/// Data context
|
/// Data context
|
||||||
type AppDbContext (opts : DbContextOptions<AppDbContext>) as self =
|
type AppDbContext (opts : DbContextOptions<AppDbContext>) =
|
||||||
inherit DbContext (opts)
|
inherit DbContext (opts)
|
||||||
|
|
||||||
/// Register a disconnected entity with the context, having the given state
|
|
||||||
let registerAs state (e : 'TEntity when 'TEntity : not struct) =
|
|
||||||
self.Entry<'TEntity>(e).State <- state
|
|
||||||
|
|
||||||
[<DefaultValue>]
|
[<DefaultValue>]
|
||||||
val mutable private history : DbSet<History>
|
val mutable private history : DbSet<History>
|
||||||
[<DefaultValue>]
|
[<DefaultValue>]
|
||||||
@ -209,13 +208,17 @@ type AppDbContext (opts : DbContextOptions<AppDbContext>) as self =
|
|||||||
]
|
]
|
||||||
|> List.iter (fun x -> x mb)
|
|> List.iter (fun x -> x mb)
|
||||||
|
|
||||||
|
/// Register a disconnected entity with the context, having the given state
|
||||||
|
member private this.RegisterAs<'TEntity when 'TEntity : not struct> state e =
|
||||||
|
this.Entry<'TEntity>(e).State <- state
|
||||||
|
|
||||||
/// Add an entity instance to the context
|
/// Add an entity instance to the context
|
||||||
member __.AddEntry e =
|
member this.AddEntry e =
|
||||||
registerAs EntityState.Added e
|
this.RegisterAs EntityState.Added e
|
||||||
|
|
||||||
/// Update the entity instance's values
|
/// Update the entity instance's values
|
||||||
member __.UpdateEntry e =
|
member this.UpdateEntry e =
|
||||||
registerAs EntityState.Modified e
|
this.RegisterAs EntityState.Modified e
|
||||||
|
|
||||||
/// Retrieve all answered requests for the given user
|
/// Retrieve all answered requests for the given user
|
||||||
member this.AnsweredRequests userId : JournalRequest seq =
|
member this.AnsweredRequests userId : JournalRequest seq =
|
||||||
@ -227,7 +230,7 @@ type AppDbContext (opts : DbContextOptions<AppDbContext>) as self =
|
|||||||
member this.JournalByUserId userId : JournalRequest seq =
|
member this.JournalByUserId userId : JournalRequest seq =
|
||||||
upcast this.Journal
|
upcast this.Journal
|
||||||
.Where(fun r -> r.userId = userId && r.lastStatus <> "Answered")
|
.Where(fun r -> r.userId = userId && r.lastStatus <> "Answered")
|
||||||
.OrderBy(fun r -> r.asOf)
|
.OrderByDescending(fun r -> r.asOf)
|
||||||
|
|
||||||
/// Retrieve a request by its ID and user ID
|
/// Retrieve a request by its ID and user ID
|
||||||
member this.TryRequestById reqId userId : Task<Request option> =
|
member this.TryRequestById reqId userId : Task<Request option> =
|
||||||
|
@ -32,8 +32,8 @@ module Error =
|
|||||||
module private Helpers =
|
module private Helpers =
|
||||||
|
|
||||||
open Microsoft.AspNetCore.Http
|
open Microsoft.AspNetCore.Http
|
||||||
open Microsoft.AspNetCore.Authorization
|
|
||||||
open System.Threading.Tasks
|
open System.Threading.Tasks
|
||||||
|
open System.Security.Claims
|
||||||
|
|
||||||
/// Get the database context from DI
|
/// Get the database context from DI
|
||||||
let db (ctx : HttpContext) =
|
let db (ctx : HttpContext) =
|
||||||
@ -41,7 +41,12 @@ module private Helpers =
|
|||||||
|
|
||||||
/// Get the user's "sub" claim
|
/// Get the user's "sub" claim
|
||||||
let user (ctx : HttpContext) =
|
let user (ctx : HttpContext) =
|
||||||
ctx.User.Claims |> Seq.tryFind (fun u -> u.Type = "sub")
|
ctx.User.Claims |> Seq.tryFind (fun u -> u.Type = ClaimTypes.NameIdentifier)
|
||||||
|
|
||||||
|
/// Get the current user's ID
|
||||||
|
// NOTE: this may raise if you don't run the request through the authorize handler first
|
||||||
|
let userId ctx =
|
||||||
|
((user >> Option.get) ctx).Value
|
||||||
|
|
||||||
/// Return a 201 CREATED response
|
/// Return a 201 CREATED response
|
||||||
let created next ctx =
|
let created next ctx =
|
||||||
@ -51,20 +56,17 @@ module private Helpers =
|
|||||||
let jsNow () =
|
let jsNow () =
|
||||||
DateTime.Now.Subtract(DateTime (1970, 1, 1)).TotalSeconds |> int64 |> (*) 1000L
|
DateTime.Now.Subtract(DateTime (1970, 1, 1)).TotalSeconds |> int64 |> (*) 1000L
|
||||||
|
|
||||||
|
/// Handler to return a 403 Not Authorized reponse
|
||||||
let notAuthorized : HttpHandler =
|
let notAuthorized : HttpHandler =
|
||||||
setStatusCode 403 >=> fun _ _ -> Task.FromResult<HttpContext option> None
|
setStatusCode 403 >=> fun _ _ -> Task.FromResult<HttpContext option> None
|
||||||
|
|
||||||
/// Handler to require authorization
|
/// Handler to require authorization
|
||||||
let authorize : HttpHandler =
|
let authorize : HttpHandler =
|
||||||
fun next ctx ->
|
fun next ctx -> match user ctx with Some _ -> next ctx | None -> notAuthorized next ctx
|
||||||
task {
|
|
||||||
let auth = ctx.GetService<IAuthorizationService>()
|
/// Flip JSON result so we can pipe into it
|
||||||
let! result = auth.AuthorizeAsync (ctx.User, "LoggedOn")
|
let asJson<'T> next ctx (o : 'T) =
|
||||||
Console.WriteLine (sprintf "*** Auth succeeded = %b" result.Succeeded)
|
json o next ctx
|
||||||
match result.Succeeded with
|
|
||||||
| true -> return! next ctx
|
|
||||||
| false -> return! notAuthorized next ctx
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// Strongly-typed models for post requests
|
/// Strongly-typed models for post requests
|
||||||
@ -107,9 +109,9 @@ module Journal =
|
|||||||
let journal : HttpHandler =
|
let journal : HttpHandler =
|
||||||
authorize
|
authorize
|
||||||
>=> fun next ctx ->
|
>=> fun next ctx ->
|
||||||
match user ctx with
|
userId ctx
|
||||||
| Some u -> json ((db ctx).JournalByUserId u.Value) next ctx
|
|> (db ctx).JournalByUserId
|
||||||
| None -> Error.notFound next ctx
|
|> asJson next ctx
|
||||||
|
|
||||||
|
|
||||||
/// /api/request URLs
|
/// /api/request URLs
|
||||||
@ -119,17 +121,17 @@ module Request =
|
|||||||
|
|
||||||
/// POST /api/request
|
/// POST /api/request
|
||||||
let add : HttpHandler =
|
let add : HttpHandler =
|
||||||
fun next ctx ->
|
authorize
|
||||||
|
>=> fun next ctx ->
|
||||||
task {
|
task {
|
||||||
match user ctx with
|
|
||||||
| Some u ->
|
|
||||||
let! r = ctx.BindJsonAsync<Models.Request> ()
|
let! r = ctx.BindJsonAsync<Models.Request> ()
|
||||||
let db = db ctx
|
let db = db ctx
|
||||||
let reqId = Cuid.Generate ()
|
let reqId = Cuid.Generate ()
|
||||||
|
let usrId = userId ctx
|
||||||
let now = jsNow ()
|
let now = jsNow ()
|
||||||
{ Request.empty with
|
{ Request.empty with
|
||||||
requestId = reqId
|
requestId = reqId
|
||||||
userId = u.Value
|
userId = usrId
|
||||||
enteredOn = now
|
enteredOn = now
|
||||||
snoozedUntil = 0L
|
snoozedUntil = 0L
|
||||||
}
|
}
|
||||||
@ -142,21 +144,19 @@ module Request =
|
|||||||
}
|
}
|
||||||
|> db.AddEntry
|
|> db.AddEntry
|
||||||
let! _ = db.SaveChangesAsync ()
|
let! _ = db.SaveChangesAsync ()
|
||||||
let! req = db.TryJournalById reqId u.Value
|
let! req = db.TryJournalById reqId usrId
|
||||||
match req with
|
match req with
|
||||||
| Some rqst -> return! (setStatusCode 201 >=> json rqst) next ctx
|
| Some rqst -> return! (setStatusCode 201 >=> json rqst) next ctx
|
||||||
| None -> return! Error.notFound next ctx
|
| None -> return! Error.notFound next ctx
|
||||||
| None -> return! Error.notFound next ctx
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// POST /api/request/[req-id]/history
|
/// POST /api/request/[req-id]/history
|
||||||
let addHistory reqId : HttpHandler =
|
let addHistory reqId : HttpHandler =
|
||||||
fun next ctx ->
|
authorize
|
||||||
|
>=> fun next ctx ->
|
||||||
task {
|
task {
|
||||||
match user ctx with
|
|
||||||
| Some u ->
|
|
||||||
let db = db ctx
|
let db = db ctx
|
||||||
let! req = db.TryRequestById reqId u.Value
|
let! req = db.TryRequestById reqId (userId ctx)
|
||||||
match req with
|
match req with
|
||||||
| Some _ ->
|
| Some _ ->
|
||||||
let! hist = ctx.BindJsonAsync<Models.HistoryEntry> ()
|
let! hist = ctx.BindJsonAsync<Models.HistoryEntry> ()
|
||||||
@ -170,17 +170,15 @@ module Request =
|
|||||||
let! _ = db.SaveChangesAsync ()
|
let! _ = db.SaveChangesAsync ()
|
||||||
return! created next ctx
|
return! created next ctx
|
||||||
| None -> return! Error.notFound next ctx
|
| None -> return! Error.notFound next ctx
|
||||||
| None -> return! Error.notFound next ctx
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// POST /api/request/[req-id]/note
|
/// POST /api/request/[req-id]/note
|
||||||
let addNote reqId : HttpHandler =
|
let addNote reqId : HttpHandler =
|
||||||
fun next ctx ->
|
authorize
|
||||||
|
>=> fun next ctx ->
|
||||||
task {
|
task {
|
||||||
match user ctx with
|
|
||||||
| Some u ->
|
|
||||||
let db = db ctx
|
let db = db ctx
|
||||||
let! req = db.TryRequestById reqId u.Value
|
let! req = db.TryRequestById reqId (userId ctx)
|
||||||
match req with
|
match req with
|
||||||
| Some _ ->
|
| Some _ ->
|
||||||
let! notes = ctx.BindJsonAsync<Models.NoteEntry> ()
|
let! notes = ctx.BindJsonAsync<Models.NoteEntry> ()
|
||||||
@ -193,74 +191,65 @@ module Request =
|
|||||||
let! _ = db.SaveChangesAsync ()
|
let! _ = db.SaveChangesAsync ()
|
||||||
return! created next ctx
|
return! created next ctx
|
||||||
| None -> return! Error.notFound next ctx
|
| None -> return! Error.notFound next ctx
|
||||||
| None -> return! Error.notFound next ctx
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// GET /api/requests/answered
|
/// GET /api/requests/answered
|
||||||
let answered : HttpHandler =
|
let answered : HttpHandler =
|
||||||
fun next ctx ->
|
authorize
|
||||||
match user ctx with
|
>=> fun next ctx ->
|
||||||
| Some u -> json ((db ctx).AnsweredRequests u.Value) next ctx
|
userId ctx
|
||||||
| None -> Error.notFound next ctx
|
|> (db ctx).AnsweredRequests
|
||||||
|
|> asJson next ctx
|
||||||
|
|
||||||
/// GET /api/request/[req-id]
|
/// GET /api/request/[req-id]
|
||||||
let get reqId : HttpHandler =
|
let get reqId : HttpHandler =
|
||||||
fun next ctx ->
|
authorize
|
||||||
|
>=> fun next ctx ->
|
||||||
task {
|
task {
|
||||||
match user ctx with
|
let! req = (db ctx).TryRequestById reqId (userId ctx)
|
||||||
| Some u ->
|
|
||||||
let! req = (db ctx).TryRequestById reqId u.Value
|
|
||||||
match req with
|
match req with
|
||||||
| Some r -> return! json r next ctx
|
| Some r -> return! json r next ctx
|
||||||
| None -> return! Error.notFound next ctx
|
| None -> return! Error.notFound next ctx
|
||||||
| None -> return! Error.notFound next ctx
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// GET /api/request/[req-id]/complete
|
/// GET /api/request/[req-id]/complete
|
||||||
let getComplete reqId : HttpHandler =
|
let getComplete reqId : HttpHandler =
|
||||||
fun next ctx ->
|
authorize
|
||||||
|
>=> fun next ctx ->
|
||||||
task {
|
task {
|
||||||
match user ctx with
|
let! req = (db ctx).TryCompleteRequestById reqId (userId ctx)
|
||||||
| Some u ->
|
|
||||||
let! req = (db ctx).TryCompleteRequestById reqId u.Value
|
|
||||||
match req with
|
match req with
|
||||||
| Some r -> return! json r next ctx
|
| Some r -> return! json r next ctx
|
||||||
| None -> return! Error.notFound next ctx
|
| None -> return! Error.notFound next ctx
|
||||||
| None -> return! Error.notFound next ctx
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// GET /api/request/[req-id]/full
|
/// GET /api/request/[req-id]/full
|
||||||
let getFull reqId : HttpHandler =
|
let getFull reqId : HttpHandler =
|
||||||
fun next ctx ->
|
authorize
|
||||||
|
>=> fun next ctx ->
|
||||||
task {
|
task {
|
||||||
match user ctx with
|
let! req = (db ctx).TryFullRequestById reqId (userId ctx)
|
||||||
| Some u ->
|
|
||||||
let! req = (db ctx).TryFullRequestById reqId u.Value
|
|
||||||
match req with
|
match req with
|
||||||
| Some r -> return! json r next ctx
|
| Some r -> return! json r next ctx
|
||||||
| None -> return! Error.notFound next ctx
|
| None -> return! Error.notFound next ctx
|
||||||
| None -> return! Error.notFound next ctx
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// GET /api/request/[req-id]/notes
|
/// GET /api/request/[req-id]/notes
|
||||||
let getNotes reqId : HttpHandler =
|
let getNotes reqId : HttpHandler =
|
||||||
fun next ctx ->
|
authorize
|
||||||
|
>=> fun next ctx ->
|
||||||
task {
|
task {
|
||||||
match user ctx with
|
let! notes = (db ctx).NotesById reqId (userId ctx)
|
||||||
| Some u ->
|
|
||||||
let! notes = (db ctx).NotesById reqId u.Value
|
|
||||||
return! json notes next ctx
|
return! json notes next ctx
|
||||||
| None -> return! Error.notFound next ctx
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// POST /api/request/[req-id]/snooze
|
/// POST /api/request/[req-id]/snooze
|
||||||
let snooze reqId : HttpHandler =
|
let snooze reqId : HttpHandler =
|
||||||
fun next ctx ->
|
authorize
|
||||||
|
>=> fun next ctx ->
|
||||||
task {
|
task {
|
||||||
match user ctx with
|
|
||||||
| Some u ->
|
|
||||||
let db = db ctx
|
let db = db ctx
|
||||||
let! req = db.TryRequestById reqId u.Value
|
let! req = db.TryRequestById reqId (userId ctx)
|
||||||
match req with
|
match req with
|
||||||
| Some r ->
|
| Some r ->
|
||||||
let! until = ctx.BindJsonAsync<Models.SnoozeUntil> ()
|
let! until = ctx.BindJsonAsync<Models.SnoozeUntil> ()
|
||||||
@ -269,5 +258,4 @@ module Request =
|
|||||||
let! _ = db.SaveChangesAsync ()
|
let! _ = db.SaveChangesAsync ()
|
||||||
return! setStatusCode 204 next ctx
|
return! setStatusCode 204 next ctx
|
||||||
| None -> return! Error.notFound next ctx
|
| None -> return! Error.notFound next ctx
|
||||||
| None -> return! Error.notFound next ctx
|
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
<PackageReference Include="Giraffe.TokenRouter" Version="0.1.0-beta-110" />
|
<PackageReference Include="Giraffe.TokenRouter" Version="0.1.0-beta-110" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.App" />
|
<PackageReference Include="Microsoft.AspNetCore.App" />
|
||||||
<PackageReference Include="NCuid.NetCore" Version="1.0.1" />
|
<PackageReference Include="NCuid.NetCore" Version="1.0.1" />
|
||||||
|
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="2.1.1.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -2,18 +2,21 @@ namespace MyPrayerJournal.Api
|
|||||||
|
|
||||||
open Microsoft.AspNetCore.Builder
|
open Microsoft.AspNetCore.Builder
|
||||||
open Microsoft.AspNetCore.Hosting
|
open Microsoft.AspNetCore.Hosting
|
||||||
|
open System
|
||||||
|
|
||||||
|
|
||||||
/// Configuration functions for the application
|
/// Configuration functions for the application
|
||||||
module Configure =
|
module Configure =
|
||||||
|
|
||||||
|
open Giraffe
|
||||||
|
open Giraffe.TokenRouter
|
||||||
open Microsoft.AspNetCore.Authentication.JwtBearer
|
open Microsoft.AspNetCore.Authentication.JwtBearer
|
||||||
open Microsoft.AspNetCore.Server.Kestrel.Core
|
open Microsoft.AspNetCore.Server.Kestrel.Core
|
||||||
|
open Microsoft.EntityFrameworkCore
|
||||||
open Microsoft.Extensions.Configuration
|
open Microsoft.Extensions.Configuration
|
||||||
open Microsoft.Extensions.DependencyInjection
|
open Microsoft.Extensions.DependencyInjection
|
||||||
open Microsoft.Extensions.Logging
|
open Microsoft.Extensions.Logging
|
||||||
open Giraffe
|
open MyPrayerJournal
|
||||||
open Giraffe.TokenRouter
|
|
||||||
|
|
||||||
/// Set up the configuration for the app
|
/// Set up the configuration for the app
|
||||||
let configuration (ctx : WebHostBuilderContext) (cfg : IConfigurationBuilder) =
|
let configuration (ctx : WebHostBuilderContext) (cfg : IConfigurationBuilder) =
|
||||||
@ -29,21 +32,22 @@ module Configure =
|
|||||||
|
|
||||||
/// Configure dependency injection
|
/// Configure dependency injection
|
||||||
let services (sc : IServiceCollection) =
|
let services (sc : IServiceCollection) =
|
||||||
sc.AddGiraffe () |> ignore
|
|
||||||
// mad props to Andrea Chiarelli @ https://auth0.com/blog/securing-asp-dot-net-core-2-applications-with-jwts/
|
|
||||||
use sp = sc.BuildServiceProvider()
|
use sp = sc.BuildServiceProvider()
|
||||||
let cfg = sp.GetRequiredService<IConfiguration>().GetSection "Auth0"
|
let cfg = sp.GetRequiredService<IConfiguration> ()
|
||||||
sc.AddAuthentication(
|
sc.AddGiraffe()
|
||||||
|
.AddAuthentication(
|
||||||
|
/// Use HTTP "Bearer" authentication with JWTs
|
||||||
fun opts ->
|
fun opts ->
|
||||||
opts.DefaultAuthenticateScheme <- JwtBearerDefaults.AuthenticationScheme
|
opts.DefaultAuthenticateScheme <- JwtBearerDefaults.AuthenticationScheme
|
||||||
opts.DefaultChallengeScheme <- JwtBearerDefaults.AuthenticationScheme)
|
opts.DefaultChallengeScheme <- JwtBearerDefaults.AuthenticationScheme)
|
||||||
.AddJwtBearer (
|
.AddJwtBearer(
|
||||||
|
/// Configure JWT options with Auth0 options from configuration
|
||||||
fun opts ->
|
fun opts ->
|
||||||
opts.Authority <- sprintf "https://%s/" cfg.["Domain"]
|
let jwtCfg = cfg.GetSection "Auth0"
|
||||||
opts.Audience <- cfg.["Audience"]
|
opts.Authority <- sprintf "https://%s/" jwtCfg.["Domain"]
|
||||||
opts.TokenValidationParameters.ValidateAudience <- false)
|
opts.Audience <- jwtCfg.["Id"])
|
||||||
|> ignore
|
|> ignore
|
||||||
sc.AddAuthorization (fun opts -> opts.AddPolicy ("LoggedOn", fun p -> p.RequireClaim "sub" |> ignore))
|
sc.AddDbContext<AppDbContext>(fun opts -> opts.UseNpgsql(cfg.GetConnectionString "mpj") |> ignore)
|
||||||
|> ignore
|
|> ignore
|
||||||
|
|
||||||
/// Routes for the available URLs within myPrayerJournal
|
/// Routes for the available URLs within myPrayerJournal
|
||||||
@ -96,12 +100,11 @@ module Configure =
|
|||||||
|
|
||||||
module Program =
|
module Program =
|
||||||
|
|
||||||
open System
|
|
||||||
open System.IO
|
open System.IO
|
||||||
|
|
||||||
let exitCode = 0
|
let exitCode = 0
|
||||||
|
|
||||||
let CreateWebHostBuilder args =
|
let CreateWebHostBuilder _ =
|
||||||
let contentRoot = Directory.GetCurrentDirectory ()
|
let contentRoot = Directory.GetCurrentDirectory ()
|
||||||
WebHostBuilder()
|
WebHostBuilder()
|
||||||
.UseContentRoot(contentRoot)
|
.UseContentRoot(contentRoot)
|
||||||
|
@ -31,12 +31,12 @@ export default {
|
|||||||
* Add a new prayer request
|
* Add a new prayer request
|
||||||
* @param {string} requestText The text of the request to be added
|
* @param {string} requestText The text of the request to be added
|
||||||
*/
|
*/
|
||||||
addRequest: requestText => http.post('request/', { requestText }),
|
addRequest: requestText => http.post('request', { requestText }),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all answered requests, along with the text they had when it was answered
|
* Get all answered requests, along with the text they had when it was answered
|
||||||
*/
|
*/
|
||||||
getAnsweredRequests: () => http.get('request/answered'),
|
getAnsweredRequests: () => http.get('requests/answered'),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a prayer request (full; includes all history)
|
* Get a prayer request (full; includes all history)
|
||||||
@ -64,7 +64,7 @@ export default {
|
|||||||
/**
|
/**
|
||||||
* Get all prayer requests and their most recent updates
|
* Get all prayer requests and their most recent updates
|
||||||
*/
|
*/
|
||||||
journal: () => http.get('journal/'),
|
journal: () => http.get('journal'),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update a prayer request
|
* Update a prayer request
|
||||||
|
Loading…
Reference in New Issue
Block a user