You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

98 lines
3.3 KiB

  1. // Copyright 2018 Google LLC
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package metadata
  15. import (
  16. "context"
  17. "encoding/json"
  18. "errors"
  19. "fmt"
  20. "time"
  21. )
  22. // Metadata holds Google Cloud Functions metadata.
  23. type Metadata struct {
  24. // EventID is a unique ID for the event. For example: "70172329041928".
  25. EventID string `json:"eventId"`
  26. // Timestamp is the date/time this event was created.
  27. Timestamp time.Time `json:"timestamp"`
  28. // EventType is the type of the event. For example: "google.pubsub.topic.publish".
  29. EventType string `json:"eventType"`
  30. // Resource is the resource that triggered the event.
  31. Resource *Resource `json:"resource"`
  32. }
  33. // Resource holds Google Cloud Functions resource metadata.
  34. // Resource values are dependent on the event type they're from.
  35. type Resource struct {
  36. // Service is the service that triggered the event.
  37. Service string `json:"service"`
  38. // Name is the name associated with the event.
  39. Name string `json:"name"`
  40. // Type is the type of event.
  41. Type string `json:"type"`
  42. }
  43. type contextKey string
  44. // GCFContextKey satisfies an interface to be able to use contextKey to read
  45. // metadata from a Cloud Functions context.Context.
  46. //
  47. // Be careful making changes to this function. See FromContext.
  48. func (k contextKey) GCFContextKey() string {
  49. return string(k)
  50. }
  51. const metadataContextKey = contextKey("metadata")
  52. // FromContext extracts the Metadata from the Context, if present.
  53. func FromContext(ctx context.Context) (*Metadata, error) {
  54. if ctx == nil {
  55. return nil, errors.New("nil ctx")
  56. }
  57. // The original JSON is inserted by the Cloud Functions worker. So, the
  58. // format must not change, or the message may fail to unmarshal. We use
  59. // JSON as a common format between the worker and this package to ensure
  60. // this package can be updated independently from the worker. The contextKey
  61. // type and the metadataContextKey value use an interface to avoid using
  62. // a built-in type as a context key (which is easy to have collisions with).
  63. // If we need another value to be stored in the context, we can use a new
  64. // key or interface and avoid needing to change this one. Similarly, if we
  65. // need to change the format of the message, we should add an additional key
  66. // to keep backward compatibility.
  67. b, ok := ctx.Value(metadataContextKey).(json.RawMessage)
  68. if !ok {
  69. return nil, errors.New("unable to find metadata")
  70. }
  71. meta := &Metadata{}
  72. if err := json.Unmarshal(b, meta); err != nil {
  73. return nil, fmt.Errorf("json.Unmarshal: %v", err)
  74. }
  75. return meta, nil
  76. }
  77. // NewContext returns a new Context carrying m. If m is nil, NewContext returns
  78. // ctx. NewContext is only used for writing tests which rely on Metadata.
  79. func NewContext(ctx context.Context, m *Metadata) context.Context {
  80. if m == nil {
  81. return ctx
  82. }
  83. b, err := json.Marshal(m)
  84. if err != nil {
  85. return ctx
  86. }
  87. return context.WithValue(ctx, metadataContextKey, json.RawMessage(b))
  88. }