github.com/pbberlin/tools@v0.0.0-20160910141205-7aa5421c2169/dsu/ancestored_gb_entries/persistence.go (about)

     1  // Package ancestored_gb_entries demonstrates putting all entries
     2  // under one ancestor, making them a strongly consistent 'entity group'
     3  package ancestored_gb_entries
     4  
     5  import (
     6  	"net/http"
     7  	"time"
     8  
     9  	"appengine"
    10  	ds "appengine/datastore"
    11  	"appengine/user"
    12  
    13  	"bytes"
    14  	"fmt"
    15  	"strings"
    16  	//"reflect"
    17  
    18  	"github.com/pbberlin/tools/net/http/loghttp"
    19  	"github.com/pbberlin/tools/util"
    20  )
    21  
    22  /*
    23  	default_guestbook[T:Guestbook]
    24  		\
    25  		 - Entry_1
    26  		 - Entry_2
    27  
    28  
    29  
    30  */
    31  
    32  const (
    33  	kind_guestbk string = "class_parent_gb" // "classname" of the parent
    34  	GbEntryKind  string = "gbEntry"         // "classname" of a guestbookk entry
    35  
    36  )
    37  
    38  type GbsaveEntry struct {
    39  	Author            string
    40  	Content           string `datastore:"Content,noindex" json:"content"`
    41  	Date              time.Time
    42  	unsaved           string `datastore:"-"`
    43  	TypeShiftingField int
    44  	Comment1          string
    45  }
    46  
    47  // just for experiment
    48  // we retrieve into a different structure then we saved into
    49  //
    50  type GbEntryRetr struct {
    51  	Author            string
    52  	Content           string
    53  	Date              time.Time
    54  	Field2            string
    55  	TypeShiftingField uint8
    56  	Comment1          string
    57  }
    58  
    59  // returns entity group - or parent - key
    60  //   to store and retrieve all guestbook entries.
    61  //   the content of this parent is nil
    62  //   it only servers as umbrella for the entries
    63  func keyParent(c appengine.Context) (r *ds.Key) {
    64  
    65  	// strPart could be varied for multiple guestbooks.
    66  	// Either strPart XOR intPart must be zero
    67  	const strPart = "instance_parent_gb"
    68  	const intPart = int64(0)
    69  	r = ds.NewKey(c, kind_guestbk, strPart, intPart, nil)
    70  	return
    71  }
    72  
    73  // implementing the "stringer" interface
    74  func (g GbEntryRetr) String() string {
    75  
    76  	b1 := new(bytes.Buffer)
    77  
    78  	b1.WriteString(g.Author + "<br>\n")
    79  	b1.WriteString(g.Content + "<br>\n")
    80  	f2 := "2006-01-02 (Jan 02)"
    81  	s2 := g.Date.Format(f2)
    82  	b1.WriteString(s2 + "<br>\n")
    83  
    84  	return b1.String()
    85  }
    86  
    87  func SaveEntry(w http.ResponseWriter, r *http.Request, m map[string]interface{}) {
    88  
    89  	contnt, ok := m["content"].(string)
    90  	loghttp.E(w, r, ok, false, "need a key 'content' with a string")
    91  
    92  	c := appengine.NewContext(r)
    93  
    94  	g := GbsaveEntry{
    95  		Content:           contnt,
    96  		Date:              time.Now(),
    97  		TypeShiftingField: 2,
    98  	}
    99  	if u := user.Current(c); u != nil {
   100  		g.Author = u.String()
   101  	}
   102  	g.Comment1 = "comment"
   103  
   104  	/* We set the same parent key on every GB entry entity
   105  	   to ensure each GB entry is in the same entity group.
   106  
   107  	   Queries across the single entity group will be consistent.
   108  	   However, we should limit write rate to single entity group ~1/sec.
   109  
   110  		NewIncompleteKey(appengine.Context, kind string , parent *Key)
   111  			has neither a string key, nor an integer key
   112  		 	only a "kind" (classname) and a parent
   113  		Upon usage the datastore generates an integer key
   114  	*/
   115  
   116  	incomplete := ds.NewIncompleteKey(c, GbEntryKind, keyParent(c))
   117  	concreteNewKey, err := ds.Put(c, incomplete, &g)
   118  	loghttp.E(w, r, err, false)
   119  	_ = concreteNewKey // we query entries via keyParent - via parent
   120  
   121  }
   122  
   123  func ListEntries(w http.ResponseWriter,
   124  	r *http.Request) (gbEntries []GbEntryRetr, report string) {
   125  
   126  	c := appengine.NewContext(r)
   127  	/* High Replication Datastore:
   128  	Ancestor queries are strongly consistent.
   129  	Queries spanning MULTIPLE entity groups are EVENTUALLY consistent.
   130  	If .Ancestor was omitted from this query, there would be slight chance
   131  	that recent GB entry would not show up in a query.
   132  	*/
   133  	q := ds.NewQuery(GbEntryKind).Ancestor(keyParent(c)).Order("-Date").Limit(10)
   134  	gbEntries = make([]GbEntryRetr, 0, 10)
   135  	keys, err := q.GetAll(c, &gbEntries)
   136  
   137  	if fmt.Sprintf("%T", err) == fmt.Sprintf("%T", new(ds.ErrFieldMismatch)) {
   138  		//s := fmt.Sprintf("%v %T  vs %v %T <br>\n",err,err,ds.ErrFieldMismatch{},ds.ErrFieldMismatch{})
   139  		loghttp.E(w, r, err, true)
   140  		err = nil // ignore this one - it's caused by our deliberate differences between gbsaveEntry and gbEntrieRetr
   141  	}
   142  	loghttp.E(w, r, err, false)
   143  
   144  	// for investigative purposes,
   145  	// we
   146  	var b1 bytes.Buffer
   147  	var sw string
   148  	var descrip []string = []string{"class", "path", "key_int_guestbk"}
   149  	for i0, v0 := range keys {
   150  		sKey := fmt.Sprintf("%v", v0)
   151  		v1 := strings.Split(sKey, ",")
   152  		sw = fmt.Sprintf("key %v", i0)
   153  		b1.WriteString(sw)
   154  		for i2, v2 := range v1 {
   155  			d := descrip[i2]
   156  			sw = fmt.Sprintf(" \t %v:  %q ", d, v2)
   157  			b1.WriteString(sw)
   158  		}
   159  		b1.WriteString("\n")
   160  	}
   161  	report = b1.String()
   162  
   163  	for _, gbe := range gbEntries {
   164  		s := gbe.Comment1
   165  		if len(s) > 0 {
   166  			if pos := strings.Index(s, "0300"); pos > 1 {
   167  				i1 := util.Max(pos-4, 0)
   168  				i2 := util.Min(pos+24, len(s))
   169  				s1 := s[i1:i2]
   170  				s1 = strings.Replace(s1, "3", "E", -1)
   171  				report = fmt.Sprintf("%v -%v", report, s1)
   172  			}
   173  		}
   174  	}
   175  
   176  	return
   177  }