Skip to content

Commit

Permalink
A version of booking using AppEngine datastore
Browse files Browse the repository at this point in the history
TODO: The AppEngine fake api server that handles the backends in dev
mode still needs to be initialized / run.
  • Loading branch information
robfig committed Nov 18, 2012
1 parent f9b1dfd commit b7330b0
Show file tree
Hide file tree
Showing 42 changed files with 1,895 additions and 0 deletions.
1 change: 1 addition & 0 deletions samples/booking-appengine/.gitignore
@@ -0,0 +1 @@
test-results/
72 changes: 72 additions & 0 deletions samples/booking-appengine/app/controllers/app.go
@@ -0,0 +1,72 @@
package controllers

import (
"appengine/datastore"
"code.google.com/p/go.crypto/bcrypt"
"github.com/robfig/revel"
"github.com/robfig/revel/samples/booking-appengine/app/models"
)

type Application struct {
AppEngineController
}

func (c Application) Index() rev.Result {
if c.Connected() != nil {
return c.Redirect("/hotels")
}
return c.Render()
}

func (c Application) Register() rev.Result {
return c.Render()
}

func (c Application) SaveUser(user models.User, verifyPassword string) rev.Result {
c.Validation.Required(verifyPassword)
c.Validation.Required(verifyPassword == user.Password).
Message("Password does not match")
user.Validate(c.Validation)

if c.Validation.HasErrors() {
c.Validation.Keep()
c.FlashParams()
return c.Redirect("/register")
}

user.HashedPassword, _ = bcrypt.GenerateFromPassword(
[]byte(user.Password), bcrypt.DefaultCost)

ctx := c.Context()
k := datastore.NewKey(ctx, "User", user.Username, 0, nil)
if _, err := datastore.Put(ctx, k, user); err != nil {
panic(err)
}

c.Session["user"] = user.Username
c.Flash.Success("Welcome, " + user.Name)
return c.Redirect("/hotels")
}

func (c Application) Login(username, password string) rev.Result {
user := c.GetUser(username)
if user != nil {
err := bcrypt.CompareHashAndPassword(user.HashedPassword, []byte(password))
if err == nil {
c.Session["user"] = username
c.Flash.Success("Welcome, " + username)
return c.Redirect("/hotels")
}
}

c.Flash.Out["username"] = username
c.Flash.Error("Login failed")
return c.Redirect("/")
}

func (c Application) Logout() rev.Result {
for k := range c.Session {
delete(c.Session, k)
}
return c.Redirect("/")
}
66 changes: 66 additions & 0 deletions samples/booking-appengine/app/controllers/appengine.go
@@ -0,0 +1,66 @@
package controllers

import (
"appengine"
"appengine/datastore"
"github.com/robfig/revel"
"github.com/robfig/revel/samples/booking-appengine/app/models"
)

type AppEngineController struct {
*rev.Controller
}

func (c AppEngineController) Context() appengine.Context {
const key = "appengine.Context"
if ctx, ok := c.Args[key]; ok {
return ctx.(appengine.Context)
}
ctx := appengine.NewContext(c.Request.Request)
c.Args[key] = ctx
return ctx
}

func (c AppEngineController) AddUser() rev.Result {
if user := c.Connected(); user != nil {
c.RenderArgs["user"] = user
}
return nil
}

func (c AppEngineController) Connected() *models.User {
if c.RenderArgs["user"] != nil {
return c.RenderArgs["user"].(*models.User)
}
if username, ok := c.Session["user"]; ok {
return c.GetUser(username)
}
return nil
}

func (c AppEngineController) GetUser(username string) *models.User {
// k := datastore.NewKey(ctx, "User", "", 0, nil)
query := datastore.NewQuery("User").
Filter("Username = ", username).
Limit(1)
t := query.Run(c.Context())

var user models.User
_, err := t.Next(&user)
if err != nil {
if err == datastore.Done {
return nil
}
panic(err)
}

// if err := datastore.Get(ctx, k, &user); err != nil {
// rev.INFO.Println("Couldn't get:", err)
// return nil
// }
return &user
}

func init() {
rev.InterceptMethod(AppEngineController.AddUser, rev.BEFORE)
}
185 changes: 185 additions & 0 deletions samples/booking-appengine/app/controllers/hotels.go
@@ -0,0 +1,185 @@
package controllers

import (
"appengine/datastore"
"code.google.com/p/go.crypto/bcrypt"
"fmt"
"github.com/robfig/revel"
"github.com/robfig/revel/samples/booking-appengine/app/models"
"strings"
)

type Hotels struct {
AppEngineController
}

func (c Hotels) CheckUser() rev.Result {
if user := c.Connected(); user == nil {
c.Flash.Error("Please log in first")
return c.Redirect("/")
}
return nil
}

func (c Hotels) Index() rev.Result {
ctx := c.Context()
t := datastore.NewQuery("Booking").
Filter("Username =", c.Connected().Username).
Run(ctx)

var bookings []*models.Booking
for {
var booking models.Booking
_, err := t.Next(&booking)
if err == datastore.Done {
break
}
if err != nil {
panic(err)
}

bookings = append(bookings, &booking)
}

for _, b := range bookings {
b.Hotel = c.loadHotelById(b.HotelId)
}

return c.Render(bookings)
}

func (c Hotels) List(search string, size, page int) rev.Result {
if page == 0 {
page = 1
}
nextPage := page + 1
search = strings.TrimSpace(search)
search = strings.ToLower(search)

query := datastore.NewQuery("Hotel").
Limit(size).
Offset((page - 1) * size)
if search != "" {
query = query.Filter("Name = ", search)
}

ctx := c.Context()
var hotels []*models.Hotel
for t := query.Run(ctx); ; {
var hotel models.Hotel
_, err := t.Next(&hotel)
if err == datastore.Done {
break
}
if err != nil {
panic(err)
}

hotels = append(hotels, &hotel)
}

return c.Render(hotels, search, size, page, nextPage)
}

func (c Hotels) loadHotelById(id int64) *models.Hotel {
ctx := c.Context()
k := datastore.NewKey(ctx, "Hotel", "", id, nil)
var hotel models.Hotel
if err := datastore.Get(ctx, k, &hotel); err != nil {
panic(err)
}
return &hotel
}

func (c Hotels) Show(id int64) rev.Result {
var title string
hotel := c.loadHotelById(id)
if hotel == nil {
title = "Not found"
// TODO: return c.NotFound("Hotel does not exist")
} else {
title = hotel.Name
}
return c.Render(title, hotel)
}

func (c Hotels) Settings() rev.Result {
return c.Render()
}

func (c Hotels) SaveSettings(password, verifyPassword string) rev.Result {
models.ValidatePassword(c.Validation, password)
c.Validation.Required(verifyPassword).
Message("Please verify your password")
c.Validation.Required(verifyPassword == password).
Message("Your password doesn't match")
if c.Validation.HasErrors() {
c.Validation.Keep()
return c.Redirect("/hotels/settings")
}

user := c.Connected()
user.HashedPassword, _ = bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)

ctx := c.Context()
key := datastore.NewKey(ctx, "User", user.Username, 0, nil)
if _, err := datastore.Put(ctx, key, &user); err != nil {
panic(err)
}

c.Flash.Success("Password updated")
return c.Redirect("/hotels")
}

func (c Hotels) ConfirmBooking(id int64, booking models.Booking) rev.Result {
hotel := c.loadHotelById(id)
title := fmt.Sprintf("Confirm %s booking", hotel.Name)
booking.Hotel = hotel
booking.User = c.Connected()
booking.Validate(c.Validation)

if c.Validation.HasErrors() || c.Params.Get("revise") != "" {
c.Validation.Keep()
c.FlashParams()
return c.Redirect("/hotels/%d/booking", id)
}

if c.Params.Get("confirm") != "" {
ctx := c.Context()
key := datastore.NewKey(ctx, "Booking", "", 0, nil)
key, err := datastore.Put(ctx, key, &booking)
if err != nil {
panic(err)
}

bookingId := key.IntID()
c.Flash.Success("Thank you, %s, your confirmation number for %s is %d",
booking.User.Name, hotel.Name, bookingId)
return c.Redirect("/hotels")
}

return c.Render(title, hotel, booking)
}

func (c Hotels) CancelBooking(id int64) rev.Result {
ctx := c.Context()
key := datastore.NewKey(ctx, "Booking", "", id, nil)
if err := datastore.Delete(ctx, key); err != nil {
panic(err)
}
c.Flash.Success(fmt.Sprintln("Booking cancelled for confirmation number", id))
return c.Redirect("/hotels")
}

func (c Hotels) Book(id int64) rev.Result {
hotel := c.loadHotelById(id)
title := "Book " + hotel.Name
// if hotel == nil {
// return c.NotFound("Hotel does not exist")
// }
return c.Render(title, hotel)
}

func init() {
rev.InterceptMethod(Hotels.CheckUser, rev.BEFORE)
}
13 changes: 13 additions & 0 deletions samples/booking-appengine/app/jobs/check.go
@@ -0,0 +1,13 @@
package jobs

import (
"github.com/robfig/revel/jobs"
)

// This job checks nightly that hotels have not been overbooked.
func checkStuff() {
}

func init() {
jobs.OnAppStart(checkStuff)
}

0 comments on commit b7330b0

Please sign in to comment.