blob: 051ea3980abe4d4e189dbc1b64b1c9c80a3844f4 [file] [log] [blame]
Zack Williamse940c7a2019-08-21 14:25:39 -07001// Copyright 2011 Google Inc. All rights reserved.
2// Use of this source code is governed by the Apache 2.0
3// license that can be found in the LICENSE file.
4
5// Package internal provides support for package appengine.
6//
7// Programs should not use this package directly. Its API is not stable.
8// Use packages appengine and appengine/* instead.
9package internal
10
11import (
12 "fmt"
13
14 "github.com/golang/protobuf/proto"
15
16 remotepb "google.golang.org/appengine/internal/remote_api"
17)
18
19// errorCodeMaps is a map of service name to the error code map for the service.
20var errorCodeMaps = make(map[string]map[int32]string)
21
22// RegisterErrorCodeMap is called from API implementations to register their
23// error code map. This should only be called from init functions.
24func RegisterErrorCodeMap(service string, m map[int32]string) {
25 errorCodeMaps[service] = m
26}
27
28type timeoutCodeKey struct {
29 service string
30 code int32
31}
32
33// timeoutCodes is the set of service+code pairs that represent timeouts.
34var timeoutCodes = make(map[timeoutCodeKey]bool)
35
36func RegisterTimeoutErrorCode(service string, code int32) {
37 timeoutCodes[timeoutCodeKey{service, code}] = true
38}
39
40// APIError is the type returned by appengine.Context's Call method
41// when an API call fails in an API-specific way. This may be, for instance,
42// a taskqueue API call failing with TaskQueueServiceError::UNKNOWN_QUEUE.
43type APIError struct {
44 Service string
45 Detail string
46 Code int32 // API-specific error code
47}
48
49func (e *APIError) Error() string {
50 if e.Code == 0 {
51 if e.Detail == "" {
52 return "APIError <empty>"
53 }
54 return e.Detail
55 }
56 s := fmt.Sprintf("API error %d", e.Code)
57 if m, ok := errorCodeMaps[e.Service]; ok {
58 s += " (" + e.Service + ": " + m[e.Code] + ")"
59 } else {
60 // Shouldn't happen, but provide a bit more detail if it does.
61 s = e.Service + " " + s
62 }
63 if e.Detail != "" {
64 s += ": " + e.Detail
65 }
66 return s
67}
68
69func (e *APIError) IsTimeout() bool {
70 return timeoutCodes[timeoutCodeKey{e.Service, e.Code}]
71}
72
73// CallError is the type returned by appengine.Context's Call method when an
74// API call fails in a generic way, such as RpcError::CAPABILITY_DISABLED.
75type CallError struct {
76 Detail string
77 Code int32
78 // TODO: Remove this if we get a distinguishable error code.
79 Timeout bool
80}
81
82func (e *CallError) Error() string {
83 var msg string
84 switch remotepb.RpcError_ErrorCode(e.Code) {
85 case remotepb.RpcError_UNKNOWN:
86 return e.Detail
87 case remotepb.RpcError_OVER_QUOTA:
88 msg = "Over quota"
89 case remotepb.RpcError_CAPABILITY_DISABLED:
90 msg = "Capability disabled"
91 case remotepb.RpcError_CANCELLED:
92 msg = "Canceled"
93 default:
94 msg = fmt.Sprintf("Call error %d", e.Code)
95 }
96 s := msg + ": " + e.Detail
97 if e.Timeout {
98 s += " (timeout)"
99 }
100 return s
101}
102
103func (e *CallError) IsTimeout() bool {
104 return e.Timeout
105}
106
107// NamespaceMods is a map from API service to a function that will mutate an RPC request to attach a namespace.
108// The function should be prepared to be called on the same message more than once; it should only modify the
109// RPC request the first time.
110var NamespaceMods = make(map[string]func(m proto.Message, namespace string))