go.mercari.io/datastore@v1.8.2/boom/boom_test.go (about) 1 package boom 2 3 import ( 4 "context" 5 "fmt" 6 "strconv" 7 "strings" 8 "testing" 9 10 "go.mercari.io/datastore" 11 "go.mercari.io/datastore/internal/testutils" 12 ) 13 14 var _ datastore.PropertyTranslator = UserID(0) 15 var _ datastore.PropertyTranslator = DataID(0) 16 var _ datastore.PropertyTranslator = WithAncestorID("") 17 var _ datastore.PropertyTranslator = IntID(0) 18 var _ datastore.PropertyTranslator = StringID("") 19 20 type contextClient struct{} 21 22 type UserID int64 23 type DataID int64 24 type WithAncestorID string 25 26 type IntID int64 27 type StringID string 28 29 func (id UserID) ToPropertyValue(ctx context.Context) (interface{}, error) { 30 client := ctx.Value(contextClient{}).(datastore.Client) 31 key := client.IDKey("User", int64(id), nil) 32 return key, nil 33 } 34 35 func (id UserID) FromPropertyValue(ctx context.Context, p datastore.Property) (dst interface{}, err error) { 36 key, ok := p.Value.(datastore.Key) 37 if !ok { 38 return nil, datastore.ErrInvalidEntityType 39 } 40 return UserID(key.ID()), nil 41 } 42 43 func (id DataID) ToPropertyValue(ctx context.Context) (interface{}, error) { 44 client := ctx.Value(contextClient{}).(datastore.Client) 45 key := client.IDKey("Data", int64(id), nil) 46 return key, nil 47 } 48 49 func (id DataID) FromPropertyValue(ctx context.Context, p datastore.Property) (dst interface{}, err error) { 50 key, ok := p.Value.(datastore.Key) 51 if !ok { 52 return nil, datastore.ErrInvalidEntityType 53 } 54 return DataID(key.ID()), nil 55 } 56 57 func (id WithAncestorID) ToPropertyValue(ctx context.Context) (interface{}, error) { 58 client := ctx.Value(contextClient{}).(datastore.Client) 59 ss := strings.SplitN(string(id), "-", 2) 60 if len(ss) != 2 { 61 return nil, fmt.Errorf("unexpected id format: %s", id) 62 } 63 userID, err := strconv.ParseInt(ss[0], 10, 64) 64 if err != nil { 65 return nil, err 66 } 67 dataID, err := strconv.ParseInt(ss[1], 10, 64) 68 if err != nil { 69 return nil, err 70 } 71 parentKey := client.IDKey("User", userID, nil) 72 key := client.IDKey("Data", dataID, parentKey) 73 return key, nil 74 } 75 76 func (id WithAncestorID) FromPropertyValue(ctx context.Context, p datastore.Property) (dst interface{}, err error) { 77 key, ok := p.Value.(datastore.Key) 78 if !ok { 79 return nil, datastore.ErrInvalidEntityType 80 } 81 userID := key.ParentKey().ID() 82 dataID := key.ID() 83 return WithAncestorID(fmt.Sprintf("%d-%d", userID, dataID)), nil 84 } 85 86 func (id IntID) ToPropertyValue(ctx context.Context) (interface{}, error) { 87 // for boom.KeyError 88 return int64(id), nil 89 } 90 91 func (id IntID) FromPropertyValue(ctx context.Context, p datastore.Property) (dst interface{}, err error) { 92 key, ok := p.Value.(datastore.Key) 93 if !ok { 94 return nil, datastore.ErrInvalidEntityType 95 } 96 return IntID(key.ID()), nil 97 } 98 99 func (id StringID) ToPropertyValue(ctx context.Context) (interface{}, error) { 100 // for boom.KeyError 101 return string(id), nil 102 } 103 104 func (id StringID) FromPropertyValue(ctx context.Context, p datastore.Property) (dst interface{}, err error) { 105 key, ok := p.Value.(datastore.Key) 106 if !ok { 107 return nil, datastore.ErrInvalidEntityType 108 } 109 return StringID(key.Name()), nil 110 } 111 112 func TestBoom_Key(t *testing.T) { 113 ctx, client, cleanUp := testutils.SetupCloudDatastore(t) 114 defer cleanUp() 115 116 type Data struct { 117 ID int64 `datastore:"-" boom:"id"` 118 } 119 120 bm := FromClient(ctx, client) 121 122 key := bm.Key(&Data{111}) 123 if v := key.Kind(); v != "Data" { 124 t.Errorf("unexpected: %v", v) 125 } 126 if v := key.ID(); v != 111 { 127 t.Errorf("unexpected: %v", v) 128 } 129 } 130 131 func TestBoom_KeyWithPropertyTranslator(t *testing.T) { 132 ctx, client, cleanUp := testutils.SetupCloudDatastore(t) 133 defer cleanUp() 134 135 { // IntID with PT 136 type Data struct { 137 ID IntID `datastore:"-" boom:"id"` 138 } 139 140 bm := FromClient(ctx, client) 141 142 _, err := bm.Put(&Data{111}) 143 if err != nil { 144 t.Fatal(err) 145 } 146 147 err = bm.Get(&Data{111}) 148 if err != nil { 149 t.Fatal(err) 150 } 151 } 152 { // StringID with PT 153 type Data struct { 154 ID StringID `datastore:"-" boom:"id"` 155 } 156 157 bm := FromClient(ctx, client) 158 159 _, err := bm.Put(&Data{"a"}) 160 if err != nil { 161 t.Fatal(err) 162 } 163 164 err = bm.Get(&Data{"a"}) 165 if err != nil { 166 t.Fatal(err) 167 } 168 } 169 } 170 171 func TestBoom_KeyWithParent(t *testing.T) { 172 ctx, client, cleanUp := testutils.SetupCloudDatastore(t) 173 defer cleanUp() 174 175 type Data struct { 176 ParentKey datastore.Key `datastore:"-" boom:"parent"` 177 ID int64 `datastore:"-" boom:"id"` 178 } 179 180 bm := FromClient(ctx, client) 181 182 userKey := client.NameKey("User", "test", nil) 183 key := bm.Key(&Data{userKey, 111}) 184 if v := key.ParentKey().Kind(); v != "User" { 185 t.Errorf("unexpected: %v", v) 186 } 187 if v := key.ParentKey().Name(); v != "test" { 188 t.Errorf("unexpected: %v", v) 189 } 190 if v := key.Kind(); v != "Data" { 191 t.Errorf("unexpected: %v", v) 192 } 193 if v := key.ID(); v != 111 { 194 t.Errorf("unexpected: %v", v) 195 } 196 } 197 198 func TestBoom_AllocateID(t *testing.T) { 199 ctx, client, cleanUp := testutils.SetupCloudDatastore(t) 200 defer cleanUp() 201 202 type Data struct { 203 ID IntID `datastore:"-" boom:"id"` 204 } 205 206 bm := FromClient(ctx, client) 207 208 obj := &Data{} 209 key, err := bm.AllocateID(obj) 210 if err != nil { 211 t.Fatal(err) 212 } 213 214 if v := key.Kind(); v != "Data" { 215 t.Errorf("unexpected: %v", v) 216 } 217 if v := key.ID(); v == 0 { 218 t.Errorf("unexpected: %v", v) 219 } 220 if v := int64(obj.ID); v != key.ID() { 221 t.Errorf("unexpected: %v", v) 222 } 223 } 224 225 func TestBoom_AllocateIDs(t *testing.T) { 226 ctx, client, cleanUp := testutils.SetupCloudDatastore(t) 227 defer cleanUp() 228 229 type Data struct { 230 ID IntID `datastore:"-" boom:"id"` 231 } 232 233 bm := FromClient(ctx, client) 234 235 type Spec struct { 236 From interface{} 237 Kind string 238 Assert func(key datastore.Key, spec Spec) 239 } 240 241 specs := []Spec{ 242 // struct 243 {&Data{}, "Data", func(key datastore.Key, spec Spec) { 244 obj := spec.From.(*Data) 245 if v := int64(obj.ID); v != key.ID() { 246 t.Errorf("unexpected: %v", v) 247 } 248 }}, 249 // key without parent 250 {client.IncompleteKey("User", nil), "User", nil}, 251 // key with parent 252 {client.IncompleteKey("Todo", client.NameKey("User", "foo", nil)), "Todo", func(key datastore.Key, spec Spec) { 253 if v := key.ParentKey(); v == nil { 254 t.Fatalf("unexpected: %v", v) 255 } 256 if v := key.ParentKey().Kind(); v != "User" { 257 t.Errorf("unexpected: %v", v) 258 } 259 if v := key.ParentKey().Name(); v != "foo" { 260 t.Errorf("unexpected: %v", v) 261 } 262 }}, 263 // string 264 {"Book", "Book", nil}, 265 } 266 267 srcs := make([]interface{}, 0, len(specs)) 268 for _, spec := range specs { 269 srcs = append(srcs, spec.From) 270 } 271 272 keys, err := bm.AllocateIDs(srcs) 273 if err != nil { 274 t.Fatal(err) 275 } 276 277 if v := len(keys); v != len(specs) { 278 t.Errorf("unexpected: %v", v) 279 } 280 281 for idx, spec := range specs { 282 key := keys[idx] 283 if v := key.Kind(); v != spec.Kind { 284 t.Errorf("unexpected: %v", v) 285 } 286 if v := key.ID(); v == 0 { 287 t.Errorf("unexpected: %v", v) 288 } 289 if spec.Assert != nil { 290 spec.Assert(key, spec) 291 } 292 } 293 } 294 295 func TestBoom_Put(t *testing.T) { 296 ctx, client, cleanUp := testutils.SetupCloudDatastore(t) 297 defer cleanUp() 298 299 type Data struct { 300 ID int64 `datastore:"-" boom:"id"` 301 Str string `` 302 } 303 304 bm := FromClient(ctx, client) 305 306 key, err := bm.Put(&Data{111, "Str"}) 307 if err != nil { 308 t.Fatal(err) 309 } 310 311 if v := key.Kind(); v != "Data" { 312 t.Errorf("unexpected: %v", v) 313 } 314 if v := key.ID(); v != 111 { 315 t.Errorf("unexpected: %v", v) 316 } 317 318 obj := &Data{} 319 err = client.Get(ctx, key, obj) 320 if err != nil { 321 t.Fatal(err) 322 } 323 324 if v := obj.Str; v != "Str" { 325 t.Errorf("unexpected: %v", v) 326 } 327 } 328 329 func TestBoom_PutWithIncomplete(t *testing.T) { 330 ctx, client, cleanUp := testutils.SetupCloudDatastore(t) 331 defer cleanUp() 332 333 type Data struct { 334 ID int64 `datastore:"-" boom:"id"` 335 Str string `` 336 } 337 338 bm := FromClient(ctx, client) 339 340 obj := &Data{Str: "Str"} 341 key, err := bm.Put(obj) 342 if err != nil { 343 t.Fatal(err) 344 } 345 346 if v := key.Kind(); v != "Data" { 347 t.Errorf("unexpected: %v", v) 348 } 349 if v := key.ID(); v == 0 { 350 t.Errorf("unexpected: %v", v) 351 } 352 if v := obj.ID; v != key.ID() { 353 t.Errorf("unexpected: %v", v) 354 } 355 356 obj = &Data{} 357 err = client.Get(ctx, key, obj) 358 if err != nil { 359 t.Fatal(err) 360 } 361 362 if v := obj.Str; v != "Str" { 363 t.Errorf("unexpected: %v", v) 364 } 365 } 366 367 func TestBoom_Get(t *testing.T) { 368 ctx, client, cleanUp := testutils.SetupCloudDatastore(t) 369 defer cleanUp() 370 371 type Data struct { 372 ID int64 `datastore:"-" boom:"id"` 373 Str string `` 374 } 375 376 bm := FromClient(ctx, client) 377 378 key := client.IDKey("Data", 111, nil) 379 _, err := client.Put(ctx, key, &Data{Str: "Str"}) 380 if err != nil { 381 t.Fatal(err) 382 } 383 384 obj := &Data{ID: 111} 385 err = bm.Get(obj) 386 if err != nil { 387 t.Fatal(err) 388 } 389 390 if v := obj.Str; v != "Str" { 391 t.Errorf("unexpected: %v", v) 392 } 393 } 394 395 func TestBoom_DeleteByStruct(t *testing.T) { 396 ctx, client, cleanUp := testutils.SetupCloudDatastore(t) 397 defer cleanUp() 398 399 type Data struct { 400 ID int64 `datastore:"-" boom:"id"` 401 Str string `` 402 } 403 404 bm := FromClient(ctx, client) 405 406 key := client.IDKey("Data", 111, nil) 407 _, err := client.Put(ctx, key, &Data{Str: "Str"}) 408 if err != nil { 409 t.Fatal(err) 410 } 411 412 obj := &Data{ID: 111} 413 err = bm.Delete(obj) 414 if err != nil { 415 t.Fatal(err) 416 } 417 418 err = client.Get(ctx, key, &Data{}) 419 if err != datastore.ErrNoSuchEntity { 420 t.Fatal(err) 421 } 422 } 423 424 func TestBoom_DeleteByKey(t *testing.T) { 425 ctx, client, cleanUp := testutils.SetupCloudDatastore(t) 426 defer cleanUp() 427 428 type Data struct { 429 ID int64 `datastore:"-" boom:"id"` 430 Str string `` 431 } 432 433 bm := FromClient(ctx, client) 434 435 key := client.IDKey("Data", 111, nil) 436 _, err := client.Put(ctx, key, &Data{Str: "Str"}) 437 if err != nil { 438 t.Fatal(err) 439 } 440 441 err = bm.Delete(key) 442 if err != nil { 443 t.Fatal(err) 444 } 445 446 err = client.Get(ctx, key, &Data{}) 447 if err != datastore.ErrNoSuchEntity { 448 t.Fatal(err) 449 } 450 } 451 452 func TestBoom_Count(t *testing.T) { 453 ctx, client, cleanUp := testutils.SetupCloudDatastore(t) 454 defer cleanUp() 455 456 type Data struct { 457 ID int64 `datastore:"-" boom:"id"` 458 Str string `` 459 } 460 461 bm := FromClient(ctx, client) 462 463 key := client.IDKey("Data", 111, nil) 464 _, err := client.Put(ctx, key, &Data{Str: "Str"}) 465 if err != nil { 466 t.Fatal(err) 467 } 468 469 q := bm.NewQuery(bm.Kind(&Data{})) 470 cnt, err := bm.Count(q) 471 if err != nil { 472 t.Fatal(err) 473 } 474 475 if v := cnt; v != 1 { 476 t.Errorf("unexpected: %v", v) 477 } 478 } 479 480 func TestBoom_GetAll(t *testing.T) { 481 ctx, client, cleanUp := testutils.SetupCloudDatastore(t) 482 defer cleanUp() 483 484 type Data struct { 485 ID int64 `datastore:"-" boom:"id"` 486 } 487 488 const size = 100 489 490 bm := FromClient(ctx, client) 491 492 var list []*Data 493 for i := 0; i < size; i++ { 494 list = append(list, &Data{}) 495 } 496 497 _, err := bm.PutMulti(list) 498 if err != nil { 499 t.Fatal(err) 500 } 501 502 q := bm.NewQuery(bm.Kind(&Data{})) 503 { 504 list = make([]*Data, 0) 505 _, err = bm.GetAll(q, &list) 506 if err != nil { 507 t.Fatal(err) 508 } 509 510 if v := len(list); v != size { 511 t.Errorf("unexpected: %v", v) 512 } 513 for _, obj := range list { 514 if v := obj.ID; v == 0 { 515 t.Errorf("unexpected: %v", v) 516 } 517 } 518 } 519 { 520 keys, err := bm.GetAll(q.KeysOnly(), nil) 521 if err != nil { 522 t.Fatal(err) 523 } 524 if v := len(keys); v != size { 525 t.Errorf("unexpected: %v", v) 526 } 527 } 528 } 529 530 func TestBoom_TagID(t *testing.T) { 531 ctx, client, cleanUp := testutils.SetupCloudDatastore(t) 532 defer cleanUp() 533 534 ctx = context.WithValue(ctx, contextClient{}, client) 535 536 bm := FromClient(ctx, client) 537 538 { // ID(IntID) 539 type Data struct { 540 ID int64 `datastore:"-" boom:"id"` 541 } 542 543 key, err := bm.Put(&Data{ID: 1}) 544 if err != nil { 545 t.Fatal(err) 546 } 547 548 if v := key.Kind(); v != "Data" { 549 t.Errorf("unexpected: %v", v) 550 } 551 if v := key.ID(); v != 1 { 552 t.Errorf("unexpected: %v", v) 553 } 554 555 err = bm.Get(&Data{1}) 556 if err != nil { 557 t.Fatal(err) 558 } 559 } 560 { // Name(StringID) 561 type Data struct { 562 ID string `datastore:"-" boom:"id"` 563 } 564 565 key, err := bm.Put(&Data{ID: "a"}) 566 if err != nil { 567 t.Fatal(err) 568 } 569 if v := key.Kind(); v != "Data" { 570 t.Errorf("unexpected: %v", v) 571 } 572 if v := key.Name(); v != "a" { 573 t.Errorf("unexpected: %v", v) 574 } 575 576 err = bm.Get(&Data{"a"}) 577 if err != nil { 578 t.Fatal(err) 579 } 580 } 581 } 582 583 func TestBoom_TagIDWithPropertyTranslator(t *testing.T) { 584 ctx, client, cleanUp := testutils.SetupCloudDatastore(t) 585 defer cleanUp() 586 587 ctx = context.WithValue(ctx, contextClient{}, client) 588 589 bm := FromClient(ctx, client) 590 591 { // Put & Get with boom:"id" 592 type Data struct { 593 ID DataID `datastore:"-" boom:"id"` 594 } 595 596 key, err := bm.Put(&Data{ID: DataID(100)}) 597 if err != nil { 598 t.Fatal(err) 599 } 600 601 if v := key.Kind(); v != "Data" { 602 t.Errorf("unexpected: %v", v) 603 } 604 if v := key.ID(); v != 100 { 605 t.Errorf("unexpected: %v", v) 606 } 607 608 err = bm.Get(&Data{ID: DataID(100)}) 609 if err != nil { 610 t.Fatal(err) 611 } 612 } 613 { // Put & Get with boom:"parent" 614 type Data struct { 615 ParentUserID UserID `datastore:"-" boom:"parent"` 616 ID DataID `datastore:"-" boom:"id"` 617 } 618 619 key, err := bm.Put(&Data{ParentUserID: UserID(20), ID: DataID(100)}) 620 if err != nil { 621 t.Fatal(err) 622 } 623 624 if v := key.Kind(); v != "Data" { 625 t.Errorf("unexpected: %v", v) 626 } 627 if v := key.ID(); v != 100 { 628 t.Errorf("unexpected: %v", v) 629 } 630 if v := key.ParentKey().Kind(); v != "User" { 631 t.Errorf("unexpected: %v", v) 632 } 633 if v := key.ParentKey().ID(); v != 20 { 634 t.Errorf("unexpected: %v", v) 635 } 636 637 err = bm.Get(&Data{ParentUserID: UserID(20), ID: DataID(100)}) 638 if err != nil { 639 t.Fatal(err) 640 } 641 } 642 { // Put & Get with boom:"id" that has ParentKey 643 type Data struct { 644 ID WithAncestorID `datastore:"-" boom:"id"` 645 } 646 647 key, err := bm.Put(&Data{ID: "20-100"}) 648 if err != nil { 649 t.Fatal(err) 650 } 651 652 if v := key.Kind(); v != "Data" { 653 t.Errorf("unexpected: %v", v) 654 } 655 if v := key.ID(); v != 100 { 656 t.Errorf("unexpected: %v", v) 657 } 658 if v := key.ParentKey().Kind(); v != "User" { 659 t.Errorf("unexpected: %v", v) 660 } 661 if v := key.ParentKey().ID(); v != 20 { 662 t.Errorf("unexpected: %v", v) 663 } 664 665 err = bm.Get(&Data{ID: "20-100"}) 666 if err != nil { 667 t.Fatal(err) 668 } 669 } 670 } 671 672 func TestBoom_TagParent(t *testing.T) { 673 ctx, client, cleanUp := testutils.SetupCloudDatastore(t) 674 defer cleanUp() 675 676 ctx = context.WithValue(ctx, contextClient{}, client) 677 678 bm := FromClient(ctx, client) 679 680 type Data struct { 681 ParentKey datastore.Key `datastore:"-" boom:"parent"` 682 ID int64 `datastore:"-" boom:"id"` 683 } 684 685 parentKey := client.NameKey("Parent", "a", nil) 686 key, err := bm.Put(&Data{ParentKey: parentKey, ID: 1}) 687 if err != nil { 688 t.Fatal(err) 689 } 690 691 if v := key.Kind(); v != "Data" { 692 t.Errorf("unexpected: %v", v) 693 } 694 if v := key.ID(); v != 1 { 695 t.Errorf("unexpected: %v", v) 696 } 697 if v := key.ParentKey().Kind(); v != "Parent" { 698 t.Errorf("unexpected: %v", v) 699 } 700 if v := key.ParentKey().Name(); v != "a" { 701 t.Errorf("unexpected: %v", v) 702 } 703 704 err = bm.Get(&Data{ParentKey: parentKey, ID: 1}) 705 if err != nil { 706 t.Fatal(err) 707 } 708 } 709 710 func TestBoom_TagParentWithNilParent(t *testing.T) { 711 ctx, client, cleanUp := testutils.SetupCloudDatastore(t) 712 defer cleanUp() 713 714 ctx = context.WithValue(ctx, contextClient{}, client) 715 716 bm := FromClient(ctx, client) 717 718 type Data struct { 719 ParentKey datastore.Key `datastore:"-" boom:"parent"` 720 ID int64 `datastore:"-" boom:"id"` 721 } 722 723 key, err := bm.Put(&Data{ParentKey: nil, ID: 1}) 724 if err != nil { 725 t.Fatal(err) 726 } 727 728 if v := key.ParentKey(); v != nil { 729 t.Errorf("unexpected: %v", v) 730 } 731 732 err = bm.Get(&Data{ID: 1}) 733 if err != nil { 734 t.Fatal(err) 735 } 736 } 737 738 func TestBoom_TagKind(t *testing.T) { 739 ctx, client, cleanUp := testutils.SetupCloudDatastore(t) 740 defer cleanUp() 741 742 bm := FromClient(ctx, client) 743 744 { 745 type Data struct { 746 Kind string `datastore:"-" boom:"kind,foo"` 747 ID int64 `datastore:"-" boom:"id"` 748 } 749 750 { 751 obj := &Data{} 752 753 if v := bm.Kind(obj); v != "foo" { 754 t.Errorf("unexpected: %v", v) 755 } 756 757 key, err := bm.Put(obj) 758 if err != nil { 759 t.Fatal(err) 760 } 761 if v := key.Kind(); v != "foo" { 762 t.Errorf("unexpected: %v", v) 763 } 764 765 err = bm.Get(obj) 766 if err != nil { 767 t.Fatal(err) 768 } 769 if v := bm.Kind(obj); v != "foo" { 770 t.Errorf("unexpected: %v", v) 771 } 772 } 773 { 774 obj := &Data{Kind: "BAR"} 775 776 if v := bm.Kind(obj); v != "BAR" { 777 t.Errorf("unexpected: %v", v) 778 } 779 780 key, err := bm.Put(obj) 781 if err != nil { 782 t.Fatal(err) 783 } 784 if v := key.Kind(); v != "BAR" { 785 t.Errorf("unexpected: %v", v) 786 } 787 788 err = bm.Get(obj) 789 if err != nil { 790 t.Fatal(err) 791 } 792 if v := bm.Kind(obj); v != "BAR" { 793 t.Errorf("unexpected: %v", v) 794 } 795 } 796 } 797 { 798 type Data struct { 799 Kind string `datastore:"-" boom:"kind"` 800 ID int64 `datastore:"-" boom:"id"` 801 } 802 803 { 804 obj := &Data{} 805 806 if v := bm.Kind(obj); v != "Data" { 807 t.Errorf("unexpected: %v", v) 808 } 809 810 key, err := bm.Put(obj) 811 if err != nil { 812 t.Fatal(err) 813 } 814 if v := key.Kind(); v != "Data" { 815 t.Errorf("unexpected: %v", v) 816 } 817 818 err = bm.Get(obj) 819 if err != nil { 820 t.Fatal(err) 821 } 822 if v := bm.Kind(obj); v != "Data" { 823 t.Errorf("unexpected: %v", v) 824 } 825 } 826 { 827 obj := &Data{Kind: "BAR"} 828 829 if v := bm.Kind(obj); v != "BAR" { 830 t.Errorf("unexpected: %v", v) 831 } 832 833 key, err := bm.Put(obj) 834 if err != nil { 835 t.Fatal(err) 836 } 837 if v := key.Kind(); v != "BAR" { 838 t.Errorf("unexpected: %v", v) 839 } 840 841 err = bm.Get(obj) 842 if err != nil { 843 t.Fatal(err) 844 } 845 if v := bm.Kind(obj); v != "BAR" { 846 t.Errorf("unexpected: %v", v) 847 } 848 } 849 } 850 } 851 852 var _ datastore.PropertyTranslator = CustomID(0) 853 854 type CustomID int64 855 type contextBoom struct{} 856 857 type HasCustomID struct { 858 ID CustomID `datastore:"-" boom:"id"` 859 Name string 860 } 861 862 func (id CustomID) ToPropertyValue(ctx context.Context) (interface{}, error) { 863 bm, ok := ctx.Value(contextBoom{}).(*Boom) 864 if !ok { 865 return nil, fmt.Errorf("unexpected type: %T", ctx.Value("boom")) 866 } 867 key := bm.Client.IDKey(bm.Kind(&HasCustomID{}), int64(id), nil) 868 return key, nil 869 } 870 871 func (CustomID) FromPropertyValue(ctx context.Context, p datastore.Property) (interface{}, error) { 872 bm, ok := ctx.Value(contextBoom{}).(*Boom) 873 if !ok { 874 return nil, fmt.Errorf("unexpected type: %T", ctx.Value("boom")) 875 } 876 key, ok := p.Value.(datastore.Key) 877 if !ok { 878 return nil, fmt.Errorf("unexpected type: %T", p.Value) 879 } 880 if v := bm.Kind(&HasCustomID{}); v != key.Kind() { 881 return nil, fmt.Errorf("unexpected kind: %s", v) 882 } 883 884 return CustomID(key.ID()), nil 885 } 886 887 func TestBoom_KindWithPropertyTranslator(t *testing.T) { 888 ctx, client, cleanUp := testutils.SetupCloudDatastore(t) 889 defer cleanUp() 890 891 bm := FromClient(ctx, client) 892 ctx = context.WithValue(ctx, contextBoom{}, bm) 893 bm.Context = ctx 894 895 obj := &HasCustomID{ 896 Name: "foobar", 897 } 898 key, err := bm.Put(obj) 899 if err != nil { 900 t.Fatal(err) 901 } 902 903 obj = &HasCustomID{ID: CustomID(key.ID())} 904 err = bm.Get(obj) 905 if err != nil { 906 t.Fatal(err) 907 } 908 909 if v := obj.Name; v != "foobar" { 910 t.Errorf("unexpected: %v", v) 911 } 912 }