Docker
We already have the database running in Docker, why don't we do the same with the web app?
Let's briefly go through the overall architecture of Suave Music Store running on Docker (the database part is already configured):
- We'll use two separate docker images:
- First image for the database,
- Second image for the actual F# app,
- The db image will be build on top of the official postgres image,
- The db image, upon build initialization, will create our
suavemusicstore
database from script, - The app image will extend the official fsharp image,
- The app image will copy compiled binaries to the image,
- The app image will depend on the db container, that's why we'll provide a link between those.
That's the big picture, let's now go straight to essential configuration:
Database connection string
To run on docker, we'll have to provide a proper connection string.
Up until now, function getContext
in Db module utilized the same connection string that was used for type provider.
But as we'll run the app container in isolation, we have to specify a proper connection string:
Db.fs
29: 30: 31: 32: 33: |
|
Db.fs
35:
|
|
Server suavemusicstore_db
is the docker link name that we'll apply when firing up containers.
Docker networking infrastructure takes care of matching the link name with destination host, which will be run in a separate container.
In real world (yeah, I knew I'd use this phrase one day) we'd probably move the connection strings to some kind of configuration file.
Server http binding
At the moment when starting web server (last line of App module), the defaultConfig
is used, which in turn uses HTTP binding to 127.0.0.1
(localhost) IP address.
From this Stack Overflow answer we can read that "binding inside container to localhost usually prevent from accepting connections".
Solution here is to accept requests from all IPs instead.
This can be done by providing 0.0.0.0
address:
App.fs
322: 323: 324: |
|
App.fs
326:
|
|
The snippets above copies all fields from the defaultConfig
and overrides the binding to 0.0.0.0:8083
.
Server image
Database Docker image is already in place, under postgres
directory.
For the Web image, let's create new Dockerfile in project's root directory. It will make use of the official fsharp image:
FROM fsharp:4.0
COPY ./bin/Debug/net461 /app
EXPOSE 8083
WORKDIR /app
CMD ["mono", "SuaveMusicStore.exe"]
It will use the COPY
instruction as well, but this time we'll copy the whole directory with compiled binaries.
Because we're going to bind to port 8083, we need to declare that in the Dockerfile with the EXPOSE
instruction.
CMD
stands for command that is executed when we spin up a container from this image, and the preceding WORKDIR
instruction simply states what the working directory should be when running all subsequent RUN
all CMD
instructions.
I chose to copy compiled binaries to the image instead of compiling the app inside docker. If desired however, one should be able to do the opposite and compile sources within the F# image itself.
Building the image
To build the docker image, we can invoke following commands:
> .\build.cmd
> docker build -t theimowski/suavemusicstore_app:0.1 .
Above snippet:
- runs our
build.cmd
script (build.sh
for Mac and Linux) to compile the application - builds app (server) image with a proper tag from current directory (
.
)
Note: make sure you have compiled the app before building the image.
After running the commands and typing docker images
, you should spot the newly built image next to previous one:
REPOSITORY TAG IMAGE ID CREATED SIZE
theimowski/suavemusicstore_app 0.1 2fc4970e9b34 50 seconds ago 633.2 MB
theimowski/suavemusicstore_db 0.1 143b21b4a88c 2 days ago 264.6 MB
Spinning up web container
Now that we have the second image in place, it's finally time to run a container. To do so, we can invoke following command:
> docker run -p 8083:8083 -d --name suavemusicstore_app `
--link suavemusicstore_db:suavemusicstore_db theimowski/suavemusicstore_app:0.1
The command above consists of a few arguments:
-p 8083:8083
instructs to expose the 8083 port from the container to the docker host,-d
stands for detached (background) mode,--name
assigns a friendly name to the running container,--link suavemusicstore_db:suavemusicstore_db
lets the app container communicate with the db container using the name of db container. Note the alias for the link (suavemusicstore_db
) must be the same as in the Db module for thegetContext
function,- last argument is the tag of the image.
If everything went fine, we should now be able to see two running containers with the docker ps
command:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
3eb8ba5ec672 theimowski/suavemusicstore_app:0.1 "mono SuaveMusicStore" 43 seconds ago Up 43 seconds 0.0.0.0:8083->8083/tcp suavemusicstore_app
28abd8d491d8 theimowski/suavemusicstore_db:0.1 "/docker-entrypoint.s" 53 seconds ago Up 52 seconds 5432/tcp suavemusicstore_db
To try out the application, open up your browser and navigate to the endpoint on docker host:
- When running Docker Toolbox on Win / Mac, check out
docker-machine ip <docker VM name>
for docker host IP, and the endpoint should be something likehttp://192.168.99.100:8083/
- When running Docker natively on Linux, the endpoint should be just
http://localhost:8083/
Phew! We did it, Suave Music Store is now running fully on Docker - How cool is that?
namespace FSharp
--------------------
namespace Microsoft.FSharp
namespace FSharp.Data
--------------------
namespace Microsoft.FSharp.Data
type LiteralAttribute =
inherit Attribute
new : unit -> LiteralAttribute
Full name: Microsoft.FSharp.Core.LiteralAttribute
--------------------
new : unit -> LiteralAttribute
Full name: SuaveMusicStore.Db.TPConnectionString
Full name: SuaveMusicStore.Db.Sql
Full name: FSharp.Data.Sql.SqlDataProvider
<summary>Typed representation of a database</summary>
<param name='ConnectionString'>The connection string for the SQL database</param>
<param name='ConnectionStringName'>The connection string name to select from a configuration file</param>
<param name='DatabaseVendor'> The target database vendor</param>
<param name='IndividualsAmount'>The amount of sample entities to project into the type system for each SQL entity type. Default 1000.</param>
<param name='UseOptionTypes'>If true, F# option types will be used in place of nullable database columns. If false, you will always receive the default value of the column's type even if it is null in the database.</param>
<param name='ResolutionPath'>The location to look for dynamically loaded assemblies containing database vendor specific connections and custom types.</param>
<param name='Owner'>The owner of the schema for this provider to resolve (Oracle Only)</param>
<param name='CaseSensitivityChange'>Should we do ToUpper or ToLower when generating table names?</param>
<param name='TableNames'>Comma separated table names list to limit a number of tables in big instances. The names can have '%' sign to handle it as in the 'LIKE' query (Oracle and MSSQL Only)</param>
<param name='OdbcQuote'>Odbc quote characters: Quote characters for the table and column names: `alias`, [alias]</param>
<param name='SQLiteLibrary'>Use System.Data.SQLite or Mono.Data.SQLite or select automatically (SQLite only)</param>
| MSSQLSERVER = 0
| SQLITE = 1
| POSTGRESQL = 2
| MYSQL = 3
| ORACLE = 4
| MSACCESS = 5
| ODBC = 6
| FIREBIRD = 7
Full name: FSharp.Data.Sql.Common.DatabaseProviderTypes
| ORIGINAL = 0
| TOUPPER = 1
| TOLOWER = 2
Full name: FSharp.Data.Sql.Common.CaseSensitivityChange
Full name: SuaveMusicStore.Db.DbContext
member ClearUpdates : unit -> List<SqlEntity>
member CreateConnection : unit -> IDbConnection
member GetUpdates : unit -> List<SqlEntity>
member Public : publicSchema
member SubmitUpdates : unit -> Unit
member SubmitUpdatesAsync : unit -> Async<Unit>
nested type public.albumdetails.Individuals
nested type public.albumdetailsEntity
nested type public.albums.Individuals
nested type public.albumsEntity
...
Full name: FSharp.Data.Sql.SqlDataProvider,DatabaseVendor="2",ConnectionString="Server=192.168.99.100;Database=suavemusicstore;User Id=suave;Password=1234;",CaseSensitivityChange="0".dataContext
Full name: SuaveMusicStore.Db.Album
Full name: SuaveMusicStore.Db.Genre
Full name: SuaveMusicStore.Db.AlbumDetails
Full name: SuaveMusicStore.Db.Artist
Full name: SuaveMusicStore.Db.User
Full name: SuaveMusicStore.Db.CartDetails
Full name: SuaveMusicStore.Db.Cart
Full name: SuaveMusicStore.Db.BestSeller
Full name: SuaveMusicStore.Db.DockerConnectionString
Full name: SuaveMusicStore.Db.getContext
<summary>Returns an instance of the SQL Provider using the static parameters</summary>
SqlDataProvider<...>.GetDataContext(transactionOptions: Transactions.TransactionOptions) : SqlDataProvider<...>.dataContext
<summary>Returns an instance of the SQL Provider</summary>
<param name='transactionOptions'>TransactionOptions for the transaction created on SubmitChanges.</param>
SqlDataProvider<...>.GetDataContext(connectionString: string) : SqlDataProvider<...>.dataContext
<summary>Returns an instance of the SQL Provider</summary>
<param name='connectionString'>The database connection string</param>
SqlDataProvider<...>.GetDataContext(connectionString: string, transactionOptions: Transactions.TransactionOptions) : SqlDataProvider<...>.dataContext
<summary>Returns an instance of the SQL Provider</summary>
<param name='connectionString'>The database connection string</param>
<param name='transactionOptions'>TransactionOptions for the transaction created on SubmitChanges.</param>
SqlDataProvider<...>.GetDataContext(connectionString: string, resolutionPath: string) : SqlDataProvider<...>.dataContext
<summary>Returns an instance of the SQL Provider</summary>
<param name='connectionString'>The database connection string</param>
<param name='resolutionPath'>The location to look for dynamically loaded assemblies containing database vendor specific connections and custom types</param>
SqlDataProvider<...>.GetDataContext(connectionString: string, resolutionPath: string, transactionOptions: Transactions.TransactionOptions) : SqlDataProvider<...>.dataContext
<summary>Returns an instance of the SQL Provider</summary>
<param name='connectionString'>The database connection string</param>
<param name='resolutionPath'>The location to look for dynamically loaded assemblies containing database vendor specific connections and custom types</param>
<param name='transactionOptions'>TransactionOptions for the transaction created on SubmitChanges.</param>
Full name: SuaveMusicStore.Db.getGenres
Full name: Microsoft.FSharp.Collections.list<_>
<summary> The base table genres belonging to schema public</summary>
module Seq
from FSharp.Data.Sql
--------------------
module Seq
from Microsoft.FSharp.Collections
Full name: Microsoft.FSharp.Collections.Seq.toList
Full name: SuaveMusicStore.Db.getAlbumsForGenre
Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.query
<summary> The base table albums belonging to schema public</summary>
Calls Linq.QueryBuilder.Join
<summary> integer</summary>
Calls Linq.QueryBuilder.Where
<summary> character varying(120)</summary>
Calls Linq.QueryBuilder.Select
Full name: SuaveMusicStore.Db.getAlbumDetails
Full name: Microsoft.FSharp.Core.option<_>
<summary> The view albumdetails belonging to schema public</summary>
<summary> integer</summary>
Full name: Microsoft.FSharp.Collections.Seq.tryHead
Full name: SuaveMusicStore.Db.getAlbumsDetails
module List
from FSharp.Data.Sql
--------------------
module List
from Microsoft.FSharp.Collections
--------------------
type List<'T> =
| ( [] )
| ( :: ) of Head: 'T * Tail: 'T list
interface IEnumerable
interface IEnumerable<'T>
member GetSlice : startIndex:int option * endIndex:int option -> 'T list
member Head : 'T
member IsEmpty : bool
member Item : index:int -> 'T with get
member Length : int
member Tail : 'T list
static member Cons : head:'T * tail:'T list -> 'T list
static member Empty : 'T list
Full name: Microsoft.FSharp.Collections.List<_>
Full name: Microsoft.FSharp.Collections.List.sortBy
<summary> character varying(120)</summary>
Full name: SuaveMusicStore.Db.getAlbum
<summary> integer</summary>
Full name: SuaveMusicStore.Db.getBestSellers
<summary> The view bestsellers belonging to schema public</summary>
Full name: SuaveMusicStore.Db.deleteAlbum
<summary>Save changes to data-source. May throws errors: To deal with non-saved items use GetUpdates() and ClearUpdates().</summary>
Full name: SuaveMusicStore.Db.getArtists
<summary> The base table artists belonging to schema public</summary>
Full name: SuaveMusicStore.Db.createAlbum
SqlDataProvider<...>.dataContext.publicSchema.public.albums.Create(data: System.Collections.Generic.IEnumerable<string * obj>) : SqlDataProvider<...>.dataContext.public.albumsEntity
<summary>Item array of database columns:
artistid,genreid,price,title</summary>
SqlDataProvider<...>.dataContext.publicSchema.public.albums.Create(artistid: int, genreid: int, price: decimal, title: string) : SqlDataProvider<...>.dataContext.public.albumsEntity
Full name: Microsoft.FSharp.Core.Operators.ignore
Full name: SuaveMusicStore.Db.updateAlbum
<summary> integer</summary>
<summary> integer</summary>
<summary> numeric</summary>
<summary> character varying(160)</summary>
Full name: SuaveMusicStore.Db.validateUser
<summary> The base table users belonging to schema public</summary>
<summary> character varying(200)</summary>
<summary> character varying(200)</summary>
Full name: SuaveMusicStore.Db.getCart
<summary> The base table carts belonging to schema public</summary>
<summary> character varying(50)</summary>
<summary> integer</summary>
Full name: SuaveMusicStore.Db.addToCart
<summary> integer</summary>
SqlDataProvider<...>.dataContext.publicSchema.public.carts.Create(data: System.Collections.Generic.IEnumerable<string * obj>) : SqlDataProvider<...>.dataContext.public.cartsEntity
<summary>Item array of database columns:
albumid,cartid,count,datecreated</summary>
SqlDataProvider<...>.dataContext.publicSchema.public.carts.Create(albumid: int, cartid: string, count: int, datecreated: System.DateTime) : SqlDataProvider<...>.dataContext.public.cartsEntity
type DateTime =
struct
new : ticks:int64 -> DateTime + 10 overloads
member Add : value:TimeSpan -> DateTime
member AddDays : value:float -> DateTime
member AddHours : value:float -> DateTime
member AddMilliseconds : value:float -> DateTime
member AddMinutes : value:float -> DateTime
member AddMonths : months:int -> DateTime
member AddSeconds : value:float -> DateTime
member AddTicks : value:int64 -> DateTime
member AddYears : value:int -> DateTime
...
end
Full name: System.DateTime
--------------------
System.DateTime()
(+0 other overloads)
System.DateTime(ticks: int64) : unit
(+0 other overloads)
System.DateTime(ticks: int64, kind: System.DateTimeKind) : unit
(+0 other overloads)
System.DateTime(year: int, month: int, day: int) : unit
(+0 other overloads)
System.DateTime(year: int, month: int, day: int, calendar: System.Globalization.Calendar) : unit
(+0 other overloads)
System.DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int) : unit
(+0 other overloads)
System.DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int, kind: System.DateTimeKind) : unit
(+0 other overloads)
System.DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int, calendar: System.Globalization.Calendar) : unit
(+0 other overloads)
System.DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int, millisecond: int) : unit
(+0 other overloads)
System.DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int, millisecond: int, kind: System.DateTimeKind) : unit
(+0 other overloads)
Full name: SuaveMusicStore.Db.getCartsDetails
<summary> The view cartdetails belonging to schema public</summary>
<summary> character varying(50)</summary>
Full name: SuaveMusicStore.Db.removeFromCart
Full name: SuaveMusicStore.Db.getCarts
Full name: SuaveMusicStore.Db.upgradeCarts
val string : value:'T -> string
Full name: Microsoft.FSharp.Core.Operators.string
--------------------
type string = System.String
Full name: Microsoft.FSharp.Core.string
Full name: SuaveMusicStore.Db.getUser
Full name: SuaveMusicStore.Db.newUser
SqlDataProvider<...>.dataContext.publicSchema.public.users.Create(data: System.Collections.Generic.IEnumerable<string * obj>) : SqlDataProvider<...>.dataContext.public.usersEntity
<summary>Item array of database columns:
email,password,role,username</summary>
SqlDataProvider<...>.dataContext.publicSchema.public.users.Create(email: string, password: string, role: string, username: string) : SqlDataProvider<...>.dataContext.public.usersEntity
Full name: SuaveMusicStore.Db.placeOrder
Full name: Microsoft.FSharp.Collections.List.sumBy
val decimal : value:'T -> decimal (requires member op_Explicit)
Full name: Microsoft.FSharp.Core.Operators.decimal
--------------------
type decimal = System.Decimal
Full name: Microsoft.FSharp.Core.decimal
--------------------
type decimal<'Measure> = decimal
Full name: Microsoft.FSharp.Core.decimal<_>
<summary> integer</summary>
<summary> numeric</summary>
<summary> The base table orders belonging to schema public</summary>
SqlDataProvider<...>.dataContext.publicSchema.public.orders.Create(data: System.Collections.Generic.IEnumerable<string * obj>) : SqlDataProvider<...>.dataContext.public.ordersEntity
<summary>Item array of database columns:
orderdate,total</summary>
SqlDataProvider<...>.dataContext.publicSchema.public.orders.Create(orderdate: System.DateTime, total: decimal) : SqlDataProvider<...>.dataContext.public.ordersEntity
<summary> character varying(256)</summary>
<summary> The base table orderdetails belonging to schema public</summary>
SqlDataProvider<...>.dataContext.publicSchema.public.orderdetails.Create(data: System.Collections.Generic.IEnumerable<string * obj>) : SqlDataProvider<...>.dataContext.public.orderdetailsEntity
<summary>Item array of database columns:
albumid,orderid,quantity,unitprice</summary>
SqlDataProvider<...>.dataContext.publicSchema.public.orderdetails.Create(albumid: int, orderid: int, quantity: int, unitprice: decimal) : SqlDataProvider<...>.dataContext.public.orderdetailsEntity
<summary> integer</summary>
<summary> integer</summary>
from Microsoft.FSharp.Core
Full name: Microsoft.FSharp.Core.Option.iter
Full name: SuaveMusicStore.Path.IntPath
type PrintfFormat<'Printer,'State,'Residue,'Result> =
new : value:string -> PrintfFormat<'Printer,'State,'Residue,'Result>
member Value : string
Full name: Microsoft.FSharp.Core.PrintfFormat<_,_,_,_>
--------------------
type PrintfFormat<'Printer,'State,'Residue,'Result,'Tuple> =
inherit PrintfFormat<'Printer,'State,'Residue,'Result>
new : value:string -> PrintfFormat<'Printer,'State,'Residue,'Result,'Tuple>
Full name: Microsoft.FSharp.Core.PrintfFormat<_,_,_,_,_>
--------------------
new : value:string -> PrintfFormat<'Printer,'State,'Residue,'Result>
--------------------
new : value:string -> PrintfFormat<'Printer,'State,'Residue,'Result,'Tuple>
val int : value:'T -> int (requires member op_Explicit)
Full name: Microsoft.FSharp.Core.Operators.int
--------------------
type int = int32
Full name: Microsoft.FSharp.Core.int
--------------------
type int<'Measure> = int
Full name: Microsoft.FSharp.Core.int<_>
Full name: Microsoft.FSharp.Core.unit
Full name: SuaveMusicStore.Path.withParam
Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.sprintf
Full name: SuaveMusicStore.Path.home
Full name: SuaveMusicStore.Path.Store.overview
Full name: SuaveMusicStore.Path.Store.browse
Full name: SuaveMusicStore.Path.Store.details
Full name: SuaveMusicStore.Path.Store.browseKey
Full name: SuaveMusicStore.Path.Admin.manage
Full name: SuaveMusicStore.Path.Admin.createAlbum
Full name: SuaveMusicStore.Path.Admin.editAlbum
Full name: SuaveMusicStore.Path.Admin.deleteAlbum
Full name: SuaveMusicStore.Path.Account.logon
Full name: SuaveMusicStore.Path.Account.logoff
Full name: SuaveMusicStore.Path.Account.register
from SuaveMusicStore.Path
Full name: SuaveMusicStore.Path.Cart.overview
Full name: SuaveMusicStore.Path.Cart.addAlbum
Full name: SuaveMusicStore.Path.Cart.removeAlbum
Full name: SuaveMusicStore.Path.Cart.checkout
from Suave
{ArtistId: decimal;
GenreId: decimal;
Title: string;
Price: decimal;
ArtUrl: string;}
Full name: SuaveMusicStore.Form.Album
Full name: SuaveMusicStore.Form.album
union case Form.Form: FormProp<'a> list * ServerSideValidation<'a> list -> Form<'a>
--------------------
type Form<'a> = | Form of FormProp<'a> list * ServerSideValidation<'a> list
Full name: Suave.Form.Form<_>
Full name: Suave.Form.maxLength
Full name: Suave.Form.min
Full name: Suave.Form.max
Full name: Suave.Form.step
{Username: string;
Password: Password;}
Full name: SuaveMusicStore.Form.Logon
Logon.Password: Password
--------------------
type Password = | Password of string
Full name: Suave.Form.Password
Full name: SuaveMusicStore.Form.logon
{Username: string;
Email: string;
Password: Password;
ConfirmPassword: Password;}
Full name: SuaveMusicStore.Form.Register
Register.Password: Password
--------------------
type Password = | Password of string
Full name: Suave.Form.Password
union case Password.Password: string -> Password
--------------------
type Password = | Password of string
Full name: Suave.Form.Password
Full name: SuaveMusicStore.Form.pattern
Full name: Suave.Form.passwordRegex
Full name: SuaveMusicStore.Form.passwordsMatch
Full name: SuaveMusicStore.Form.register
{FirstName: string;
LastName: string;
Address: string;
PromoCode: string option;}
Full name: SuaveMusicStore.Form.Checkout
val option : value:string -> txt:string -> selected:bool -> Suave.Html.Node
Full name: Suave.Form.option
--------------------
type 'T option = Option<'T>
Full name: Microsoft.FSharp.Core.option<_>
Full name: SuaveMusicStore.Form.checkout
from Suave
Full name: SuaveMusicStore.View.em
Full name: Suave.Html.tag
Full name: SuaveMusicStore.View.cssLink
Full name: Suave.Html.link
Full name: SuaveMusicStore.View.h2
Full name: SuaveMusicStore.View.ul
Full name: SuaveMusicStore.View.ulAttr
Full name: SuaveMusicStore.View.li
Full name: SuaveMusicStore.View.table
Full name: SuaveMusicStore.View.th
Full name: SuaveMusicStore.View.tr
Full name: SuaveMusicStore.View.td
Full name: SuaveMusicStore.View.strong
Full name: Suave.Html.text
Full name: SuaveMusicStore.View.form
Full name: SuaveMusicStore.View.formInput
Full name: Suave.Form.input
Full name: SuaveMusicStore.View.submitInput
Full name: Suave.Html.input
{Label: string;
Html: Form<'a> -> Node;}
Full name: SuaveMusicStore.View.Field<_>
Field.Html: Form<'a> -> Node
--------------------
type HtmlAttribute = string * string
Full name: Suave.Form.HtmlAttribute
union case Form.Form: FormProp<'a> list * ServerSideValidation<'a> list -> Form<'a>
--------------------
module Form
from SuaveMusicStore
--------------------
type Form<'a> = | Form of FormProp<'a> list * ServerSideValidation<'a> list
Full name: Suave.Form.Form<_>
| Element of Element * Node list
| VoidElement of Element
| Text of string
| Raw of string
| WhiteSpace of string
Full name: Suave.Html.Node
{Legend: string;
Fields: Field<'a> list;}
Full name: SuaveMusicStore.View.Fieldset<_>
{Fieldsets: Fieldset<'a> list;
SubmitText: string;
Form: Form<'a>;}
Full name: SuaveMusicStore.View.FormLayout<_>
FormLayout.Form: Form<'a>
--------------------
module Form
from SuaveMusicStore
--------------------
type Form<'a> = | Form of FormProp<'a> list * ServerSideValidation<'a> list
Full name: Suave.Form.Form<_>
Full name: SuaveMusicStore.View.renderForm
Full name: Suave.Html.div
Full name: SuaveMusicStore.View.home
from SuaveMusicStore
Full name: SuaveMusicStore.Db.BestSeller
Full name: Suave.Html.img
Full name: Suave.Html.a
from SuaveMusicStore
from SuaveMusicStore.Path
Full name: SuaveMusicStore.Path.Store.details
<summary> integer</summary>
<summary> character varying(1024)</summary>
Full name: Suave.Html.span
<summary> character varying(160)</summary>
Full name: SuaveMusicStore.View.store
Full name: Suave.Html.p
module List
from Microsoft.FSharp.Collections
--------------------
type List<'T> =
| ( [] )
| ( :: ) of Head: 'T * Tail: 'T list
interface IEnumerable
interface IEnumerable<'T>
member GetSlice : startIndex:int option * endIndex:int option -> 'T list
member Head : 'T
member IsEmpty : bool
member Item : index:int -> 'T with get
member Length : int
member Tail : 'T list
static member Cons : head:'T * tail:'T list -> 'T list
static member Empty : 'T list
Full name: Microsoft.FSharp.Collections.List<_>
Full name: Microsoft.FSharp.Collections.List.length
Full name: SuaveMusicStore.View.browse
Full name: SuaveMusicStore.Db.Album
<summary> integer</summary>
<summary> character varying(1024)</summary>
<summary> character varying(160)</summary>
Full name: SuaveMusicStore.View.details
Full name: SuaveMusicStore.Db.AlbumDetails
<summary> character varying(160)</summary>
<summary> character varying(1024)</summary>
<summary> character varying(120)</summary>
<summary> character varying(120)</summary>
<summary> numeric</summary>
System.Decimal.ToString(provider: System.IFormatProvider) : string
System.Decimal.ToString(format: string) : string
System.Decimal.ToString(format: string, provider: System.IFormatProvider) : string
Full name: SuaveMusicStore.Path.Cart.addAlbum
<summary> integer</summary>
Full name: SuaveMusicStore.View.truncate
System.String.Substring(startIndex: int, length: int) : string
Full name: SuaveMusicStore.View.manage
from SuaveMusicStore.Path
Full name: SuaveMusicStore.Path.Admin.editAlbum
Full name: SuaveMusicStore.Path.Admin.deleteAlbum
Full name: SuaveMusicStore.View.deleteAlbum
Full name: Suave.Html.br
Full name: SuaveMusicStore.View.createAlbum
Full name: SuaveMusicStore.Form.album
Full name: Suave.Form.HtmlAttribute
Full name: Suave.Form.selectInput
Full name: SuaveMusicStore.View.editAlbum
<summary> integer</summary>
<summary> integer</summary>
Full name: Suave.Form.formatDec
<summary> numeric</summary>
Full name: SuaveMusicStore.View.logon
from SuaveMusicStore.Path
Full name: SuaveMusicStore.Form.logon
Full name: SuaveMusicStore.View.notFound
Full name: SuaveMusicStore.View.partNav
Full name: SuaveMusicStore.View.partUser
val option : value:string -> txt:string -> selected:bool -> Node
Full name: Suave.Form.option
--------------------
type 'T option = Option<'T>
Full name: Microsoft.FSharp.Core.option<_>
Full name: SuaveMusicStore.View.partGenres
Full name: SuaveMusicStore.Db.Genre
<summary> character varying(120)</summary>
Full name: SuaveMusicStore.View.emptyCart
Full name: SuaveMusicStore.View.nonEmptyCart
Full name: SuaveMusicStore.Db.CartDetails
<summary> integer</summary>
<summary> character varying(160)</summary>
<summary> numeric</summary>
<summary> integer</summary>
System.Int32.ToString(provider: System.IFormatProvider) : string
System.Int32.ToString(format: string) : string
System.Int32.ToString(format: string, provider: System.IFormatProvider) : string
Full name: Suave.Html.script
Full name: SuaveMusicStore.View.cart
val list : Db.CartDetails list
--------------------
type 'T list = List<'T>
Full name: Microsoft.FSharp.Collections.list<_>
Full name: SuaveMusicStore.View.register
Full name: SuaveMusicStore.Form.register
Full name: SuaveMusicStore.View.checkout
Full name: SuaveMusicStore.Form.checkout
Full name: SuaveMusicStore.View.checkoutComplete
Full name: SuaveMusicStore.View.index
Full name: Suave.Html.html
Full name: Suave.Html.head
Full name: Suave.Html.title
Full name: Suave.Html.body
Full name: Suave.Html.htmlToString
from SuaveMusicStore
from Suave
from Suave
from Suave
from Suave
from Suave.Model
from Suave
from Suave
from Suave
from Suave.State
from Suave
from Suave
{Username: string;
Role: string;}
Full name: SuaveMusicStore.App.UserLoggedOnSession
val string : value:'T -> string
Full name: Microsoft.FSharp.Core.Operators.string
--------------------
type string = String
Full name: Microsoft.FSharp.Core.string
union case CookieLife.Session: CookieLife
--------------------
type Session =
| NoSession
| CartIdOnly of string
| UserLoggedOn of UserLoggedOnSession
Full name: SuaveMusicStore.App.Session
Full name: SuaveMusicStore.App.session
Full name: Suave.State.CookieStateStore.statefulForSession
Full name: Suave.Http.context
module HttpContext
from Suave.State.CookieStateStore
--------------------
module HttpContext
from Suave.Authentication
--------------------
module HttpContext
from Suave.Http
--------------------
type HttpContext =
{request: HttpRequest;
runtime: HttpRuntime;
connection: Connection;
userState: Map<string,obj>;
response: HttpResult;}
member clientIp : trustProxy:bool -> sources:string list -> IPAddress
member clientPort : trustProxy:bool -> sources:string list -> Port
member clientProto : trustProxy:bool -> sources:string list -> string
member clientIpTrustProxy : IPAddress
member clientPortTrustProxy : Port
member clientProtoTrustProxy : string
member isLocal : bool
static member clientIp_ : Property<HttpContext,IPAddress>
static member clientPort_ : Property<HttpContext,Port>
static member clientProto_ : Property<HttpContext,string>
static member connection_ : Property<HttpContext,Connection>
static member isLocal_ : Property<HttpContext,bool>
static member request_ : Property<HttpContext,HttpRequest>
static member response_ : Property<HttpContext,HttpResult>
static member runtime_ : Property<HttpContext,HttpRuntime>
static member userState_ : Property<HttpContext,Map<string,obj>>
Full name: Suave.Http.HttpContext
Full name: Suave.State.CookieStateStore.HttpContext.state
Full name: SuaveMusicStore.App.html
Full name: SuaveMusicStore.Db.getContext
Full name: Suave.Successful.OK
from SuaveMusicStore
Full name: SuaveMusicStore.View.index
Full name: SuaveMusicStore.View.partNav
Full name: SuaveMusicStore.View.partUser
Full name: SuaveMusicStore.View.partGenres
Full name: SuaveMusicStore.Db.getGenres
from Suave
Full name: Suave.Writers.setMimeType
Full name: SuaveMusicStore.Db.getCartsDetails
Full name: SuaveMusicStore.App.home
Full name: Suave.WebPart.warbler
Full name: SuaveMusicStore.Db.getBestSellers
Full name: SuaveMusicStore.View.home
Full name: SuaveMusicStore.App.browse
Full name: Suave.Http.request
Full name: SuaveMusicStore.Db.getAlbumsForGenre
Full name: SuaveMusicStore.View.browse
Full name: Suave.RequestErrors.BAD_REQUEST
Full name: SuaveMusicStore.App.overview
Full name: Microsoft.FSharp.Collections.List.map
Full name: SuaveMusicStore.View.store
Full name: SuaveMusicStore.App.details
Full name: SuaveMusicStore.Db.getAlbumDetails
Full name: SuaveMusicStore.View.details
Full name: Suave.WebPart.never
Full name: SuaveMusicStore.App.manage
Full name: SuaveMusicStore.Db.getAlbumsDetails
Full name: SuaveMusicStore.View.manage
Full name: SuaveMusicStore.App.bindToForm
Full name: Suave.Model.Binding.bindReq
Full name: Suave.Form.bindForm
Full name: SuaveMusicStore.App.createAlbum
Full name: Suave.WebPart.choose
Full name: Suave.Filters.GET
val decimal : value:'T -> decimal (requires member op_Explicit)
Full name: Microsoft.FSharp.Core.Operators.decimal
--------------------
type decimal = Decimal
Full name: Microsoft.FSharp.Core.decimal
--------------------
type decimal<'Measure> = decimal
Full name: Microsoft.FSharp.Core.decimal<_>
<summary> integer</summary>
Full name: SuaveMusicStore.Db.getArtists
<summary> integer</summary>
<summary> character varying(120)</summary>
Full name: SuaveMusicStore.View.createAlbum
Full name: Suave.Filters.POST
union case Form.Form: FormProp<'a> list * ServerSideValidation<'a> list -> Form<'a>
--------------------
module Form
from Suave
--------------------
module Form
from SuaveMusicStore
--------------------
type Form<'a> = | Form of FormProp<'a> list * ServerSideValidation<'a> list
Full name: Suave.Form.Form<_>
Full name: SuaveMusicStore.Db.createAlbum
from Suave
Full name: Suave.Redirection.FOUND
Full name: SuaveMusicStore.App.editAlbum
Full name: SuaveMusicStore.Db.getAlbum
Full name: SuaveMusicStore.View.editAlbum
Full name: SuaveMusicStore.Db.updateAlbum
Full name: SuaveMusicStore.App.deleteAlbum
Full name: SuaveMusicStore.View.deleteAlbum
Full name: SuaveMusicStore.Db.deleteAlbum
Full name: SuaveMusicStore.App.passHash
inherit HashAlgorithm
static member Create : unit -> SHA256 + 1 overload
Full name: System.Security.Cryptography.SHA256
Security.Cryptography.SHA256.Create(hashName: string) : Security.Cryptography.SHA256
member BodyName : string
member Clone : unit -> obj
member CodePage : int
member DecoderFallback : DecoderFallback with get, set
member EncoderFallback : EncoderFallback with get, set
member EncodingName : string
member Equals : value:obj -> bool
member GetByteCount : chars:char[] -> int + 3 overloads
member GetBytes : chars:char[] -> byte[] + 5 overloads
member GetCharCount : bytes:byte[] -> int + 2 overloads
...
Full name: System.Text.Encoding
Text.Encoding.GetBytes(chars: char []) : byte []
Text.Encoding.GetBytes(chars: char [], index: int, count: int) : byte []
Text.Encoding.GetBytes(chars: nativeptr<char>, charCount: int, bytes: nativeptr<byte>, byteCount: int) : int
Text.Encoding.GetBytes(s: string, charIndex: int, charCount: int, bytes: byte [], byteIndex: int) : int
Text.Encoding.GetBytes(chars: char [], charIndex: int, charCount: int, bytes: byte [], byteIndex: int) : int
Security.Cryptography.HashAlgorithm.ComputeHash(inputStream: IO.Stream) : byte []
Security.Cryptography.HashAlgorithm.ComputeHash(buffer: byte [], offset: int, count: int) : byte []
member Clone : unit -> obj
member CopyTo : array:Array * index:int -> unit + 1 overload
member GetEnumerator : unit -> IEnumerator
member GetLength : dimension:int -> int
member GetLongLength : dimension:int -> int64
member GetLowerBound : dimension:int -> int
member GetUpperBound : dimension:int -> int
member GetValue : [<ParamArray>] indices:int[] -> obj + 7 overloads
member Initialize : unit -> unit
member IsFixedSize : bool
...
Full name: System.Array
Full name: Microsoft.FSharp.Collections.Array.map
Byte.ToString(provider: IFormatProvider) : string
Byte.ToString(format: string) : string
Byte.ToString(format: string, provider: IFormatProvider) : string
type String =
new : value:char -> string + 7 overloads
member Chars : int -> char
member Clone : unit -> obj
member CompareTo : value:obj -> int + 1 overload
member Contains : value:string -> bool
member CopyTo : sourceIndex:int * destination:char[] * destinationIndex:int * count:int -> unit
member EndsWith : value:string -> bool + 2 overloads
member Equals : obj:obj -> bool + 2 overloads
member GetEnumerator : unit -> CharEnumerator
member GetHashCode : unit -> int
...
Full name: System.String
--------------------
String(value: nativeptr<char>) : unit
String(value: nativeptr<sbyte>) : unit
String(value: char []) : unit
String(c: char, count: int) : unit
String(value: nativeptr<char>, startIndex: int, length: int) : unit
String(value: nativeptr<sbyte>, startIndex: int, length: int) : unit
String(value: char [], startIndex: int, length: int) : unit
String(value: nativeptr<sbyte>, startIndex: int, length: int, enc: Text.Encoding) : unit
Full name: Microsoft.FSharp.Core.String.concat
Full name: SuaveMusicStore.App.sessionStore
Full name: SuaveMusicStore.App.returnPathOrHome
Full name: SuaveMusicStore.App.authenticateUser
Full name: SuaveMusicStore.Db.User
Full name: Suave.Authentication.authenticated
| Session
| MaxAge of duration: TimeSpan
Full name: Suave.Cookie.CookieLife
Full name: SuaveMusicStore.Db.upgradeCarts
<summary> character varying(200)</summary>
Full name: Suave.WebPart.succeed
<summary> character varying(50)</summary>
Full name: SuaveMusicStore.App.logon
Full name: SuaveMusicStore.View.logon
Full name: SuaveMusicStore.Db.validateUser
Full name: SuaveMusicStore.App.register
Full name: SuaveMusicStore.View.register
Full name: SuaveMusicStore.Db.getUser
Full name: SuaveMusicStore.Db.newUser
Full name: SuaveMusicStore.App.reset
Full name: Suave.Cookie.unsetPair
Full name: Suave.Authentication.SessionAuthCookie
Full name: Suave.State.CookieStateStore.StateCookie
Full name: SuaveMusicStore.App.redirectWithReturnPath
Full name: SuaveMusicStore.App.loggedOn
Full name: Suave.Authentication.authenticate
Full name: SuaveMusicStore.App.admin
Full name: Suave.RequestErrors.FORBIDDEN
Full name: Suave.RequestErrors.UNAUTHORIZED
Full name: SuaveMusicStore.App.cart
Full name: SuaveMusicStore.View.emptyCart
Full name: SuaveMusicStore.View.cart
Full name: SuaveMusicStore.App.addToCart
type Guid =
struct
new : b:byte[] -> Guid + 4 overloads
member CompareTo : value:obj -> int + 1 overload
member Equals : o:obj -> bool + 1 overload
member GetHashCode : unit -> int
member ToByteArray : unit -> byte[]
member ToString : unit -> string + 2 overloads
static val Empty : Guid
static member NewGuid : unit -> Guid
static member Parse : input:string -> Guid
static member ParseExact : input:string * format:string -> Guid
...
end
Full name: System.Guid
--------------------
Guid()
Guid(b: byte []) : unit
Guid(g: string) : unit
Guid(a: int, b: int16, c: int16, d: byte []) : unit
Guid(a: uint32, b: uint16, c: uint16, d: byte, e: byte, f: byte, g: byte, h: byte, i: byte, j: byte, k: byte) : unit
Guid(a: int, b: int16, c: int16, d: byte, e: byte, f: byte, g: byte, h: byte, i: byte, j: byte, k: byte) : unit
Full name: SuaveMusicStore.Db.addToCart
Full name: SuaveMusicStore.App.removeFromCart
Full name: SuaveMusicStore.Db.getCart
Full name: SuaveMusicStore.Db.removeFromCart
module Html
from Suave
--------------------
type HtmlAttribute = string * string
Full name: Suave.Form.HtmlAttribute
Full name: Suave.Html.htmlToString
Full name: SuaveMusicStore.App.checkout
Full name: SuaveMusicStore.View.checkout
Full name: SuaveMusicStore.Db.placeOrder
Full name: SuaveMusicStore.View.checkoutComplete
Full name: SuaveMusicStore.App.webPart
Full name: Suave.Filters.path
Full name: Suave.Filters.pathScan
Full name: SuaveMusicStore.Path.Cart.removeAlbum
Full name: Suave.Filters.pathRegex
from Suave
Full name: Suave.Files.browseHome
Full name: SuaveMusicStore.View.notFound
Full name: SuaveMusicStore.App.cfg
Full name: Suave.Web.defaultConfig
module HttpBinding
from Suave.Http
--------------------
type HttpBinding =
{scheme: Protocol;
socketBinding: SocketBinding;}
override ToString : unit -> string
member uri : path:string -> query:string -> Uri
static member scheme_ : Property<HttpBinding,Protocol>
static member socketBinding_ : Property<HttpBinding,SocketBinding>
Full name: Suave.Http.HttpBinding
Full name: Suave.Http.HttpBinding.createSimple
Full name: Suave.Web.startWebServer
Show code from this section on GitHub