Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
Menu
Open sidebar
陈曦
sub2api
Commits
abf5de69
Commit
abf5de69
authored
Feb 12, 2026
by
yangjianbo
Browse files
Merge branch 'main' into test
parents
7582dc53
174d7c77
Changes
63
Hide whitespace changes
Inline
Side-by-side
backend/ent/errorpassthroughrule.go
View file @
abf5de69
...
...
@@ -44,6 +44,8 @@ type ErrorPassthroughRule struct {
PassthroughBody
bool
`json:"passthrough_body,omitempty"`
// CustomMessage holds the value of the "custom_message" field.
CustomMessage
*
string
`json:"custom_message,omitempty"`
// SkipMonitoring holds the value of the "skip_monitoring" field.
SkipMonitoring
bool
`json:"skip_monitoring,omitempty"`
// Description holds the value of the "description" field.
Description
*
string
`json:"description,omitempty"`
selectValues
sql
.
SelectValues
...
...
@@ -56,7 +58,7 @@ func (*ErrorPassthroughRule) scanValues(columns []string) ([]any, error) {
switch
columns
[
i
]
{
case
errorpassthroughrule
.
FieldErrorCodes
,
errorpassthroughrule
.
FieldKeywords
,
errorpassthroughrule
.
FieldPlatforms
:
values
[
i
]
=
new
([]
byte
)
case
errorpassthroughrule
.
FieldEnabled
,
errorpassthroughrule
.
FieldPassthroughCode
,
errorpassthroughrule
.
FieldPassthroughBody
:
case
errorpassthroughrule
.
FieldEnabled
,
errorpassthroughrule
.
FieldPassthroughCode
,
errorpassthroughrule
.
FieldPassthroughBody
,
errorpassthroughrule
.
FieldSkipMonitoring
:
values
[
i
]
=
new
(
sql
.
NullBool
)
case
errorpassthroughrule
.
FieldID
,
errorpassthroughrule
.
FieldPriority
,
errorpassthroughrule
.
FieldResponseCode
:
values
[
i
]
=
new
(
sql
.
NullInt64
)
...
...
@@ -171,6 +173,12 @@ func (_m *ErrorPassthroughRule) assignValues(columns []string, values []any) err
_m
.
CustomMessage
=
new
(
string
)
*
_m
.
CustomMessage
=
value
.
String
}
case
errorpassthroughrule
.
FieldSkipMonitoring
:
if
value
,
ok
:=
values
[
i
]
.
(
*
sql
.
NullBool
);
!
ok
{
return
fmt
.
Errorf
(
"unexpected type %T for field skip_monitoring"
,
values
[
i
])
}
else
if
value
.
Valid
{
_m
.
SkipMonitoring
=
value
.
Bool
}
case
errorpassthroughrule
.
FieldDescription
:
if
value
,
ok
:=
values
[
i
]
.
(
*
sql
.
NullString
);
!
ok
{
return
fmt
.
Errorf
(
"unexpected type %T for field description"
,
values
[
i
])
...
...
@@ -257,6 +265,9 @@ func (_m *ErrorPassthroughRule) String() string {
builder
.
WriteString
(
*
v
)
}
builder
.
WriteString
(
", "
)
builder
.
WriteString
(
"skip_monitoring="
)
builder
.
WriteString
(
fmt
.
Sprintf
(
"%v"
,
_m
.
SkipMonitoring
))
builder
.
WriteString
(
", "
)
if
v
:=
_m
.
Description
;
v
!=
nil
{
builder
.
WriteString
(
"description="
)
builder
.
WriteString
(
*
v
)
...
...
backend/ent/errorpassthroughrule/errorpassthroughrule.go
View file @
abf5de69
...
...
@@ -39,6 +39,8 @@ const (
FieldPassthroughBody
=
"passthrough_body"
// FieldCustomMessage holds the string denoting the custom_message field in the database.
FieldCustomMessage
=
"custom_message"
// FieldSkipMonitoring holds the string denoting the skip_monitoring field in the database.
FieldSkipMonitoring
=
"skip_monitoring"
// FieldDescription holds the string denoting the description field in the database.
FieldDescription
=
"description"
// Table holds the table name of the errorpassthroughrule in the database.
...
...
@@ -61,6 +63,7 @@ var Columns = []string{
FieldResponseCode
,
FieldPassthroughBody
,
FieldCustomMessage
,
FieldSkipMonitoring
,
FieldDescription
,
}
...
...
@@ -95,6 +98,8 @@ var (
DefaultPassthroughCode
bool
// DefaultPassthroughBody holds the default value on creation for the "passthrough_body" field.
DefaultPassthroughBody
bool
// DefaultSkipMonitoring holds the default value on creation for the "skip_monitoring" field.
DefaultSkipMonitoring
bool
)
// OrderOption defines the ordering options for the ErrorPassthroughRule queries.
...
...
@@ -155,6 +160,11 @@ func ByCustomMessage(opts ...sql.OrderTermOption) OrderOption {
return
sql
.
OrderByField
(
FieldCustomMessage
,
opts
...
)
.
ToFunc
()
}
// BySkipMonitoring orders the results by the skip_monitoring field.
func
BySkipMonitoring
(
opts
...
sql
.
OrderTermOption
)
OrderOption
{
return
sql
.
OrderByField
(
FieldSkipMonitoring
,
opts
...
)
.
ToFunc
()
}
// ByDescription orders the results by the description field.
func
ByDescription
(
opts
...
sql
.
OrderTermOption
)
OrderOption
{
return
sql
.
OrderByField
(
FieldDescription
,
opts
...
)
.
ToFunc
()
...
...
backend/ent/errorpassthroughrule/where.go
View file @
abf5de69
...
...
@@ -104,6 +104,11 @@ func CustomMessage(v string) predicate.ErrorPassthroughRule {
return
predicate
.
ErrorPassthroughRule
(
sql
.
FieldEQ
(
FieldCustomMessage
,
v
))
}
// SkipMonitoring applies equality check predicate on the "skip_monitoring" field. It's identical to SkipMonitoringEQ.
func
SkipMonitoring
(
v
bool
)
predicate
.
ErrorPassthroughRule
{
return
predicate
.
ErrorPassthroughRule
(
sql
.
FieldEQ
(
FieldSkipMonitoring
,
v
))
}
// Description applies equality check predicate on the "description" field. It's identical to DescriptionEQ.
func
Description
(
v
string
)
predicate
.
ErrorPassthroughRule
{
return
predicate
.
ErrorPassthroughRule
(
sql
.
FieldEQ
(
FieldDescription
,
v
))
...
...
@@ -544,6 +549,16 @@ func CustomMessageContainsFold(v string) predicate.ErrorPassthroughRule {
return
predicate
.
ErrorPassthroughRule
(
sql
.
FieldContainsFold
(
FieldCustomMessage
,
v
))
}
// SkipMonitoringEQ applies the EQ predicate on the "skip_monitoring" field.
func
SkipMonitoringEQ
(
v
bool
)
predicate
.
ErrorPassthroughRule
{
return
predicate
.
ErrorPassthroughRule
(
sql
.
FieldEQ
(
FieldSkipMonitoring
,
v
))
}
// SkipMonitoringNEQ applies the NEQ predicate on the "skip_monitoring" field.
func
SkipMonitoringNEQ
(
v
bool
)
predicate
.
ErrorPassthroughRule
{
return
predicate
.
ErrorPassthroughRule
(
sql
.
FieldNEQ
(
FieldSkipMonitoring
,
v
))
}
// DescriptionEQ applies the EQ predicate on the "description" field.
func
DescriptionEQ
(
v
string
)
predicate
.
ErrorPassthroughRule
{
return
predicate
.
ErrorPassthroughRule
(
sql
.
FieldEQ
(
FieldDescription
,
v
))
...
...
backend/ent/errorpassthroughrule_create.go
View file @
abf5de69
...
...
@@ -172,6 +172,20 @@ func (_c *ErrorPassthroughRuleCreate) SetNillableCustomMessage(v *string) *Error
return
_c
}
// SetSkipMonitoring sets the "skip_monitoring" field.
func
(
_c
*
ErrorPassthroughRuleCreate
)
SetSkipMonitoring
(
v
bool
)
*
ErrorPassthroughRuleCreate
{
_c
.
mutation
.
SetSkipMonitoring
(
v
)
return
_c
}
// SetNillableSkipMonitoring sets the "skip_monitoring" field if the given value is not nil.
func
(
_c
*
ErrorPassthroughRuleCreate
)
SetNillableSkipMonitoring
(
v
*
bool
)
*
ErrorPassthroughRuleCreate
{
if
v
!=
nil
{
_c
.
SetSkipMonitoring
(
*
v
)
}
return
_c
}
// SetDescription sets the "description" field.
func
(
_c
*
ErrorPassthroughRuleCreate
)
SetDescription
(
v
string
)
*
ErrorPassthroughRuleCreate
{
_c
.
mutation
.
SetDescription
(
v
)
...
...
@@ -249,6 +263,10 @@ func (_c *ErrorPassthroughRuleCreate) defaults() {
v
:=
errorpassthroughrule
.
DefaultPassthroughBody
_c
.
mutation
.
SetPassthroughBody
(
v
)
}
if
_
,
ok
:=
_c
.
mutation
.
SkipMonitoring
();
!
ok
{
v
:=
errorpassthroughrule
.
DefaultSkipMonitoring
_c
.
mutation
.
SetSkipMonitoring
(
v
)
}
}
// check runs all checks and user-defined validators on the builder.
...
...
@@ -287,6 +305,9 @@ func (_c *ErrorPassthroughRuleCreate) check() error {
if
_
,
ok
:=
_c
.
mutation
.
PassthroughBody
();
!
ok
{
return
&
ValidationError
{
Name
:
"passthrough_body"
,
err
:
errors
.
New
(
`ent: missing required field "ErrorPassthroughRule.passthrough_body"`
)}
}
if
_
,
ok
:=
_c
.
mutation
.
SkipMonitoring
();
!
ok
{
return
&
ValidationError
{
Name
:
"skip_monitoring"
,
err
:
errors
.
New
(
`ent: missing required field "ErrorPassthroughRule.skip_monitoring"`
)}
}
return
nil
}
...
...
@@ -366,6 +387,10 @@ func (_c *ErrorPassthroughRuleCreate) createSpec() (*ErrorPassthroughRule, *sqlg
_spec
.
SetField
(
errorpassthroughrule
.
FieldCustomMessage
,
field
.
TypeString
,
value
)
_node
.
CustomMessage
=
&
value
}
if
value
,
ok
:=
_c
.
mutation
.
SkipMonitoring
();
ok
{
_spec
.
SetField
(
errorpassthroughrule
.
FieldSkipMonitoring
,
field
.
TypeBool
,
value
)
_node
.
SkipMonitoring
=
value
}
if
value
,
ok
:=
_c
.
mutation
.
Description
();
ok
{
_spec
.
SetField
(
errorpassthroughrule
.
FieldDescription
,
field
.
TypeString
,
value
)
_node
.
Description
=
&
value
...
...
@@ -608,6 +633,18 @@ func (u *ErrorPassthroughRuleUpsert) ClearCustomMessage() *ErrorPassthroughRuleU
return
u
}
// SetSkipMonitoring sets the "skip_monitoring" field.
func
(
u
*
ErrorPassthroughRuleUpsert
)
SetSkipMonitoring
(
v
bool
)
*
ErrorPassthroughRuleUpsert
{
u
.
Set
(
errorpassthroughrule
.
FieldSkipMonitoring
,
v
)
return
u
}
// UpdateSkipMonitoring sets the "skip_monitoring" field to the value that was provided on create.
func
(
u
*
ErrorPassthroughRuleUpsert
)
UpdateSkipMonitoring
()
*
ErrorPassthroughRuleUpsert
{
u
.
SetExcluded
(
errorpassthroughrule
.
FieldSkipMonitoring
)
return
u
}
// SetDescription sets the "description" field.
func
(
u
*
ErrorPassthroughRuleUpsert
)
SetDescription
(
v
string
)
*
ErrorPassthroughRuleUpsert
{
u
.
Set
(
errorpassthroughrule
.
FieldDescription
,
v
)
...
...
@@ -888,6 +925,20 @@ func (u *ErrorPassthroughRuleUpsertOne) ClearCustomMessage() *ErrorPassthroughRu
})
}
// SetSkipMonitoring sets the "skip_monitoring" field.
func
(
u
*
ErrorPassthroughRuleUpsertOne
)
SetSkipMonitoring
(
v
bool
)
*
ErrorPassthroughRuleUpsertOne
{
return
u
.
Update
(
func
(
s
*
ErrorPassthroughRuleUpsert
)
{
s
.
SetSkipMonitoring
(
v
)
})
}
// UpdateSkipMonitoring sets the "skip_monitoring" field to the value that was provided on create.
func
(
u
*
ErrorPassthroughRuleUpsertOne
)
UpdateSkipMonitoring
()
*
ErrorPassthroughRuleUpsertOne
{
return
u
.
Update
(
func
(
s
*
ErrorPassthroughRuleUpsert
)
{
s
.
UpdateSkipMonitoring
()
})
}
// SetDescription sets the "description" field.
func
(
u
*
ErrorPassthroughRuleUpsertOne
)
SetDescription
(
v
string
)
*
ErrorPassthroughRuleUpsertOne
{
return
u
.
Update
(
func
(
s
*
ErrorPassthroughRuleUpsert
)
{
...
...
@@ -1337,6 +1388,20 @@ func (u *ErrorPassthroughRuleUpsertBulk) ClearCustomMessage() *ErrorPassthroughR
})
}
// SetSkipMonitoring sets the "skip_monitoring" field.
func
(
u
*
ErrorPassthroughRuleUpsertBulk
)
SetSkipMonitoring
(
v
bool
)
*
ErrorPassthroughRuleUpsertBulk
{
return
u
.
Update
(
func
(
s
*
ErrorPassthroughRuleUpsert
)
{
s
.
SetSkipMonitoring
(
v
)
})
}
// UpdateSkipMonitoring sets the "skip_monitoring" field to the value that was provided on create.
func
(
u
*
ErrorPassthroughRuleUpsertBulk
)
UpdateSkipMonitoring
()
*
ErrorPassthroughRuleUpsertBulk
{
return
u
.
Update
(
func
(
s
*
ErrorPassthroughRuleUpsert
)
{
s
.
UpdateSkipMonitoring
()
})
}
// SetDescription sets the "description" field.
func
(
u
*
ErrorPassthroughRuleUpsertBulk
)
SetDescription
(
v
string
)
*
ErrorPassthroughRuleUpsertBulk
{
return
u
.
Update
(
func
(
s
*
ErrorPassthroughRuleUpsert
)
{
...
...
backend/ent/errorpassthroughrule_update.go
View file @
abf5de69
...
...
@@ -227,6 +227,20 @@ func (_u *ErrorPassthroughRuleUpdate) ClearCustomMessage() *ErrorPassthroughRule
return
_u
}
// SetSkipMonitoring sets the "skip_monitoring" field.
func
(
_u
*
ErrorPassthroughRuleUpdate
)
SetSkipMonitoring
(
v
bool
)
*
ErrorPassthroughRuleUpdate
{
_u
.
mutation
.
SetSkipMonitoring
(
v
)
return
_u
}
// SetNillableSkipMonitoring sets the "skip_monitoring" field if the given value is not nil.
func
(
_u
*
ErrorPassthroughRuleUpdate
)
SetNillableSkipMonitoring
(
v
*
bool
)
*
ErrorPassthroughRuleUpdate
{
if
v
!=
nil
{
_u
.
SetSkipMonitoring
(
*
v
)
}
return
_u
}
// SetDescription sets the "description" field.
func
(
_u
*
ErrorPassthroughRuleUpdate
)
SetDescription
(
v
string
)
*
ErrorPassthroughRuleUpdate
{
_u
.
mutation
.
SetDescription
(
v
)
...
...
@@ -387,6 +401,9 @@ func (_u *ErrorPassthroughRuleUpdate) sqlSave(ctx context.Context) (_node int, e
if
_u
.
mutation
.
CustomMessageCleared
()
{
_spec
.
ClearField
(
errorpassthroughrule
.
FieldCustomMessage
,
field
.
TypeString
)
}
if
value
,
ok
:=
_u
.
mutation
.
SkipMonitoring
();
ok
{
_spec
.
SetField
(
errorpassthroughrule
.
FieldSkipMonitoring
,
field
.
TypeBool
,
value
)
}
if
value
,
ok
:=
_u
.
mutation
.
Description
();
ok
{
_spec
.
SetField
(
errorpassthroughrule
.
FieldDescription
,
field
.
TypeString
,
value
)
}
...
...
@@ -611,6 +628,20 @@ func (_u *ErrorPassthroughRuleUpdateOne) ClearCustomMessage() *ErrorPassthroughR
return
_u
}
// SetSkipMonitoring sets the "skip_monitoring" field.
func
(
_u
*
ErrorPassthroughRuleUpdateOne
)
SetSkipMonitoring
(
v
bool
)
*
ErrorPassthroughRuleUpdateOne
{
_u
.
mutation
.
SetSkipMonitoring
(
v
)
return
_u
}
// SetNillableSkipMonitoring sets the "skip_monitoring" field if the given value is not nil.
func
(
_u
*
ErrorPassthroughRuleUpdateOne
)
SetNillableSkipMonitoring
(
v
*
bool
)
*
ErrorPassthroughRuleUpdateOne
{
if
v
!=
nil
{
_u
.
SetSkipMonitoring
(
*
v
)
}
return
_u
}
// SetDescription sets the "description" field.
func
(
_u
*
ErrorPassthroughRuleUpdateOne
)
SetDescription
(
v
string
)
*
ErrorPassthroughRuleUpdateOne
{
_u
.
mutation
.
SetDescription
(
v
)
...
...
@@ -801,6 +832,9 @@ func (_u *ErrorPassthroughRuleUpdateOne) sqlSave(ctx context.Context) (_node *Er
if
_u
.
mutation
.
CustomMessageCleared
()
{
_spec
.
ClearField
(
errorpassthroughrule
.
FieldCustomMessage
,
field
.
TypeString
)
}
if
value
,
ok
:=
_u
.
mutation
.
SkipMonitoring
();
ok
{
_spec
.
SetField
(
errorpassthroughrule
.
FieldSkipMonitoring
,
field
.
TypeBool
,
value
)
}
if
value
,
ok
:=
_u
.
mutation
.
Description
();
ok
{
_spec
.
SetField
(
errorpassthroughrule
.
FieldDescription
,
field
.
TypeString
,
value
)
}
...
...
backend/ent/migrate/schema.go
View file @
abf5de69
...
...
@@ -325,6 +325,7 @@ var (
{
Name
:
"response_code"
,
Type
:
field
.
TypeInt
,
Nullable
:
true
},
{
Name
:
"passthrough_body"
,
Type
:
field
.
TypeBool
,
Default
:
true
},
{
Name
:
"custom_message"
,
Type
:
field
.
TypeString
,
Nullable
:
true
,
Size
:
2147483647
},
{
Name
:
"skip_monitoring"
,
Type
:
field
.
TypeBool
,
Default
:
false
},
{
Name
:
"description"
,
Type
:
field
.
TypeString
,
Nullable
:
true
,
Size
:
2147483647
},
}
// ErrorPassthroughRulesTable holds the schema information for the "error_passthrough_rules" table.
...
...
backend/ent/mutation.go
View file @
abf5de69
...
...
@@ -5778,6 +5778,7 @@ type ErrorPassthroughRuleMutation struct {
addresponse_code *int
passthrough_body *bool
custom_message *string
skip_monitoring *bool
description *string
clearedFields map[string]struct{}
done bool
...
...
@@ -6505,6 +6506,42 @@ func (m *ErrorPassthroughRuleMutation) ResetCustomMessage() {
delete(m.clearedFields, errorpassthroughrule.FieldCustomMessage)
}
// SetSkipMonitoring sets the "skip_monitoring" field.
func (m *ErrorPassthroughRuleMutation) SetSkipMonitoring(b bool) {
m.skip_monitoring = &b
}
// SkipMonitoring returns the value of the "skip_monitoring" field in the mutation.
func (m *ErrorPassthroughRuleMutation) SkipMonitoring() (r bool, exists bool) {
v := m.skip_monitoring
if v == nil {
return
}
return *v, true
}
// OldSkipMonitoring returns the old "skip_monitoring" field's value of the ErrorPassthroughRule entity.
// If the ErrorPassthroughRule object wasn't provided to the builder, the object is fetched from the database.
// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
func (m *ErrorPassthroughRuleMutation) OldSkipMonitoring(ctx context.Context) (v bool, err error) {
if !m.op.Is(OpUpdateOne) {
return v, errors.New("OldSkipMonitoring is only allowed on UpdateOne operations")
}
if m.id == nil || m.oldValue == nil {
return v, errors.New("OldSkipMonitoring requires an ID field in the mutation")
}
oldValue, err := m.oldValue(ctx)
if err != nil {
return v, fmt.Errorf("querying old value for OldSkipMonitoring: %w", err)
}
return oldValue.SkipMonitoring, nil
}
// ResetSkipMonitoring resets all changes to the "skip_monitoring" field.
func (m *ErrorPassthroughRuleMutation) ResetSkipMonitoring() {
m.skip_monitoring = nil
}
// SetDescription sets the "description" field.
func (m *ErrorPassthroughRuleMutation) SetDescription(s string) {
m.description = &s
...
...
@@ -6588,7 +6625,7 @@ func (m *ErrorPassthroughRuleMutation) Type() string {
// order to get all numeric fields that were incremented/decremented, call
// AddedFields().
func (m *ErrorPassthroughRuleMutation) Fields() []string {
fields := make([]string, 0, 1
4
)
fields := make([]string, 0, 1
5
)
if m.created_at != nil {
fields = append(fields, errorpassthroughrule.FieldCreatedAt)
}
...
...
@@ -6628,6 +6665,9 @@ func (m *ErrorPassthroughRuleMutation) Fields() []string {
if m.custom_message != nil {
fields = append(fields, errorpassthroughrule.FieldCustomMessage)
}
if m.skip_monitoring != nil {
fields = append(fields, errorpassthroughrule.FieldSkipMonitoring)
}
if m.description != nil {
fields = append(fields, errorpassthroughrule.FieldDescription)
}
...
...
@@ -6665,6 +6705,8 @@ func (m *ErrorPassthroughRuleMutation) Field(name string) (ent.Value, bool) {
return m.PassthroughBody()
case errorpassthroughrule.FieldCustomMessage:
return m.CustomMessage()
case errorpassthroughrule.FieldSkipMonitoring:
return m.SkipMonitoring()
case errorpassthroughrule.FieldDescription:
return m.Description()
}
...
...
@@ -6702,6 +6744,8 @@ func (m *ErrorPassthroughRuleMutation) OldField(ctx context.Context, name string
return m.OldPassthroughBody(ctx)
case errorpassthroughrule.FieldCustomMessage:
return m.OldCustomMessage(ctx)
case errorpassthroughrule.FieldSkipMonitoring:
return m.OldSkipMonitoring(ctx)
case errorpassthroughrule.FieldDescription:
return m.OldDescription(ctx)
}
...
...
@@ -6804,6 +6848,13 @@ func (m *ErrorPassthroughRuleMutation) SetField(name string, value ent.Value) er
}
m.SetCustomMessage(v)
return nil
case errorpassthroughrule.FieldSkipMonitoring:
v, ok := value.(bool)
if !ok {
return fmt.Errorf("unexpected type %T for field %s", value, name)
}
m.SetSkipMonitoring(v)
return nil
case errorpassthroughrule.FieldDescription:
v, ok := value.(string)
if !ok {
...
...
@@ -6965,6 +7016,9 @@ func (m *ErrorPassthroughRuleMutation) ResetField(name string) error {
case errorpassthroughrule.FieldCustomMessage:
m.ResetCustomMessage()
return nil
case errorpassthroughrule.FieldSkipMonitoring:
m.ResetSkipMonitoring()
return nil
case errorpassthroughrule.FieldDescription:
m.ResetDescription()
return nil
...
...
backend/ent/runtime/runtime.go
View file @
abf5de69
...
...
@@ -327,6 +327,10 @@ func init() {
errorpassthroughruleDescPassthroughBody
:=
errorpassthroughruleFields
[
9
]
.
Descriptor
()
// errorpassthroughrule.DefaultPassthroughBody holds the default value on creation for the passthrough_body field.
errorpassthroughrule
.
DefaultPassthroughBody
=
errorpassthroughruleDescPassthroughBody
.
Default
.
(
bool
)
// errorpassthroughruleDescSkipMonitoring is the schema descriptor for skip_monitoring field.
errorpassthroughruleDescSkipMonitoring
:=
errorpassthroughruleFields
[
11
]
.
Descriptor
()
// errorpassthroughrule.DefaultSkipMonitoring holds the default value on creation for the skip_monitoring field.
errorpassthroughrule
.
DefaultSkipMonitoring
=
errorpassthroughruleDescSkipMonitoring
.
Default
.
(
bool
)
groupMixin
:=
schema
.
Group
{}
.
Mixin
()
groupMixinHooks1
:=
groupMixin
[
1
]
.
Hooks
()
group
.
Hooks
[
0
]
=
groupMixinHooks1
[
0
]
...
...
backend/ent/schema/error_passthrough_rule.go
View file @
abf5de69
...
...
@@ -105,6 +105,12 @@ func (ErrorPassthroughRule) Fields() []ent.Field {
Optional
()
.
Nillable
(),
// skip_monitoring: 是否跳过运维监控记录
// true: 匹配此规则的错误不会被记录到 ops_error_logs
// false: 正常记录到运维监控(默认行为)
field
.
Bool
(
"skip_monitoring"
)
.
Default
(
false
),
// description: 规则描述,用于说明规则的用途
field
.
Text
(
"description"
)
.
Optional
()
.
...
...
backend/internal/handler/admin/antigravity_oauth_handler.go
View file @
abf5de69
...
...
@@ -65,3 +65,27 @@ func (h *AntigravityOAuthHandler) ExchangeCode(c *gin.Context) {
response
.
Success
(
c
,
tokenInfo
)
}
// AntigravityRefreshTokenRequest represents the request for validating Antigravity refresh token
type
AntigravityRefreshTokenRequest
struct
{
RefreshToken
string
`json:"refresh_token" binding:"required"`
ProxyID
*
int64
`json:"proxy_id"`
}
// RefreshToken validates an Antigravity refresh token and returns full token info
// POST /api/v1/admin/antigravity/oauth/refresh-token
func
(
h
*
AntigravityOAuthHandler
)
RefreshToken
(
c
*
gin
.
Context
)
{
var
req
AntigravityRefreshTokenRequest
if
err
:=
c
.
ShouldBindJSON
(
&
req
);
err
!=
nil
{
response
.
BadRequest
(
c
,
"请求无效: "
+
err
.
Error
())
return
}
tokenInfo
,
err
:=
h
.
antigravityOAuthService
.
ValidateRefreshToken
(
c
.
Request
.
Context
(),
req
.
RefreshToken
,
req
.
ProxyID
)
if
err
!=
nil
{
response
.
ErrorFrom
(
c
,
err
)
return
}
response
.
Success
(
c
,
tokenInfo
)
}
backend/internal/handler/admin/error_passthrough_handler.go
View file @
abf5de69
...
...
@@ -32,6 +32,7 @@ type CreateErrorPassthroughRuleRequest struct {
ResponseCode
*
int
`json:"response_code"`
PassthroughBody
*
bool
`json:"passthrough_body"`
CustomMessage
*
string
`json:"custom_message"`
SkipMonitoring
*
bool
`json:"skip_monitoring"`
Description
*
string
`json:"description"`
}
...
...
@@ -48,6 +49,7 @@ type UpdateErrorPassthroughRuleRequest struct {
ResponseCode
*
int
`json:"response_code"`
PassthroughBody
*
bool
`json:"passthrough_body"`
CustomMessage
*
string
`json:"custom_message"`
SkipMonitoring
*
bool
`json:"skip_monitoring"`
Description
*
string
`json:"description"`
}
...
...
@@ -122,6 +124,9 @@ func (h *ErrorPassthroughHandler) Create(c *gin.Context) {
}
else
{
rule
.
PassthroughBody
=
true
}
if
req
.
SkipMonitoring
!=
nil
{
rule
.
SkipMonitoring
=
*
req
.
SkipMonitoring
}
rule
.
ResponseCode
=
req
.
ResponseCode
rule
.
CustomMessage
=
req
.
CustomMessage
rule
.
Description
=
req
.
Description
...
...
@@ -190,6 +195,7 @@ func (h *ErrorPassthroughHandler) Update(c *gin.Context) {
ResponseCode
:
existing
.
ResponseCode
,
PassthroughBody
:
existing
.
PassthroughBody
,
CustomMessage
:
existing
.
CustomMessage
,
SkipMonitoring
:
existing
.
SkipMonitoring
,
Description
:
existing
.
Description
,
}
...
...
@@ -230,6 +236,9 @@ func (h *ErrorPassthroughHandler) Update(c *gin.Context) {
if
req
.
Description
!=
nil
{
rule
.
Description
=
req
.
Description
}
if
req
.
SkipMonitoring
!=
nil
{
rule
.
SkipMonitoring
=
*
req
.
SkipMonitoring
}
// 确保切片不为 nil
if
rule
.
ErrorCodes
==
nil
{
...
...
backend/internal/handler/admin/redeem_handler.go
View file @
abf5de69
...
...
@@ -202,7 +202,7 @@ func (h *RedeemHandler) Export(c *gin.Context) {
writer
:=
csv
.
NewWriter
(
&
buf
)
// Write header
if
err
:=
writer
.
Write
([]
string
{
"id"
,
"code"
,
"type"
,
"value"
,
"status"
,
"used_by"
,
"used_at"
,
"created_at"
});
err
!=
nil
{
if
err
:=
writer
.
Write
([]
string
{
"id"
,
"code"
,
"type"
,
"value"
,
"status"
,
"used_by"
,
"used_by_email"
,
"used_at"
,
"created_at"
});
err
!=
nil
{
response
.
InternalError
(
c
,
"Failed to export redeem codes: "
+
err
.
Error
())
return
}
...
...
@@ -213,6 +213,10 @@ func (h *RedeemHandler) Export(c *gin.Context) {
if
code
.
UsedBy
!=
nil
{
usedBy
=
fmt
.
Sprintf
(
"%d"
,
*
code
.
UsedBy
)
}
usedByEmail
:=
""
if
code
.
User
!=
nil
{
usedByEmail
=
code
.
User
.
Email
}
usedAt
:=
""
if
code
.
UsedAt
!=
nil
{
usedAt
=
code
.
UsedAt
.
Format
(
"2006-01-02 15:04:05"
)
...
...
@@ -224,6 +228,7 @@ func (h *RedeemHandler) Export(c *gin.Context) {
fmt
.
Sprintf
(
"%.2f"
,
code
.
Value
),
code
.
Status
,
usedBy
,
usedByEmail
,
usedAt
,
code
.
CreatedAt
.
Format
(
"2006-01-02 15:04:05"
),
});
err
!=
nil
{
...
...
backend/internal/handler/gateway_handler.go
View file @
abf5de69
...
...
@@ -7,6 +7,7 @@ import (
"errors"
"fmt"
"io"
"log"
"net/http"
"strings"
"time"
...
...
@@ -247,6 +248,7 @@ func (h *GatewayHandler) Messages(c *gin.Context) {
maxAccountSwitches
:=
h
.
maxAccountSwitchesGemini
switchCount
:=
0
failedAccountIDs
:=
make
(
map
[
int64
]
struct
{})
sameAccountRetryCount
:=
make
(
map
[
int64
]
int
)
// 同账号重试计数
var
lastFailoverErr
*
service
.
UpstreamFailoverError
var
forceCacheBilling
bool
// 粘性会话切换时的缓存计费标记
...
...
@@ -376,11 +378,28 @@ func (h *GatewayHandler) Messages(c *gin.Context) {
if
err
!=
nil
{
var
failoverErr
*
service
.
UpstreamFailoverError
if
errors
.
As
(
err
,
&
failoverErr
)
{
failedAccountIDs
[
account
.
ID
]
=
struct
{}{}
lastFailoverErr
=
failoverErr
if
needForceCacheBilling
(
hasBoundSession
,
failoverErr
)
{
forceCacheBilling
=
true
}
// 同账号重试:对 RetryableOnSameAccount 的临时性错误,先在同一账号上重试
if
failoverErr
.
RetryableOnSameAccount
&&
sameAccountRetryCount
[
account
.
ID
]
<
maxSameAccountRetries
{
sameAccountRetryCount
[
account
.
ID
]
++
log
.
Printf
(
"Account %d: retryable error %d, same-account retry %d/%d"
,
account
.
ID
,
failoverErr
.
StatusCode
,
sameAccountRetryCount
[
account
.
ID
],
maxSameAccountRetries
)
if
!
sleepSameAccountRetryDelay
(
c
.
Request
.
Context
())
{
return
}
continue
}
// 同账号重试用尽,执行临时封禁并切换账号
if
failoverErr
.
RetryableOnSameAccount
{
h
.
gatewayService
.
TempUnscheduleRetryableError
(
c
.
Request
.
Context
(),
account
.
ID
,
failoverErr
)
}
failedAccountIDs
[
account
.
ID
]
=
struct
{}{}
if
switchCount
>=
maxAccountSwitches
{
h
.
handleFailoverExhausted
(
c
,
failoverErr
,
service
.
PlatformGemini
,
streamStarted
)
return
...
...
@@ -456,6 +475,7 @@ func (h *GatewayHandler) Messages(c *gin.Context) {
maxAccountSwitches
:=
h
.
maxAccountSwitches
switchCount
:=
0
failedAccountIDs
:=
make
(
map
[
int64
]
struct
{})
sameAccountRetryCount
:=
make
(
map
[
int64
]
int
)
// 同账号重试计数
var
lastFailoverErr
*
service
.
UpstreamFailoverError
retryWithFallback
:=
false
var
forceCacheBilling
bool
// 粘性会话切换时的缓存计费标记
...
...
@@ -623,11 +643,28 @@ func (h *GatewayHandler) Messages(c *gin.Context) {
}
var
failoverErr
*
service
.
UpstreamFailoverError
if
errors
.
As
(
err
,
&
failoverErr
)
{
failedAccountIDs
[
account
.
ID
]
=
struct
{}{}
lastFailoverErr
=
failoverErr
if
needForceCacheBilling
(
hasBoundSession
,
failoverErr
)
{
forceCacheBilling
=
true
}
// 同账号重试:对 RetryableOnSameAccount 的临时性错误,先在同一账号上重试
if
failoverErr
.
RetryableOnSameAccount
&&
sameAccountRetryCount
[
account
.
ID
]
<
maxSameAccountRetries
{
sameAccountRetryCount
[
account
.
ID
]
++
log
.
Printf
(
"Account %d: retryable error %d, same-account retry %d/%d"
,
account
.
ID
,
failoverErr
.
StatusCode
,
sameAccountRetryCount
[
account
.
ID
],
maxSameAccountRetries
)
if
!
sleepSameAccountRetryDelay
(
c
.
Request
.
Context
())
{
return
}
continue
}
// 同账号重试用尽,执行临时封禁并切换账号
if
failoverErr
.
RetryableOnSameAccount
{
h
.
gatewayService
.
TempUnscheduleRetryableError
(
c
.
Request
.
Context
(),
account
.
ID
,
failoverErr
)
}
failedAccountIDs
[
account
.
ID
]
=
struct
{}{}
if
switchCount
>=
maxAccountSwitches
{
h
.
handleFailoverExhausted
(
c
,
failoverErr
,
account
.
Platform
,
streamStarted
)
return
...
...
@@ -935,6 +972,23 @@ func needForceCacheBilling(hasBoundSession bool, failoverErr *service.UpstreamFa
return
hasBoundSession
||
(
failoverErr
!=
nil
&&
failoverErr
.
ForceCacheBilling
)
}
const
(
// maxSameAccountRetries 同账号重试次数上限(针对 RetryableOnSameAccount 错误)
maxSameAccountRetries
=
2
// sameAccountRetryDelay 同账号重试间隔
sameAccountRetryDelay
=
500
*
time
.
Millisecond
)
// sleepSameAccountRetryDelay 同账号重试固定延时,返回 false 表示 context 已取消。
func
sleepSameAccountRetryDelay
(
ctx
context
.
Context
)
bool
{
select
{
case
<-
ctx
.
Done
()
:
return
false
case
<-
time
.
After
(
sameAccountRetryDelay
)
:
return
true
}
}
// sleepFailoverDelay 账号切换线性递增延时:第1次0s、第2次1s、第3次2s…
// 返回 false 表示 context 已取消。
func
sleepFailoverDelay
(
ctx
context
.
Context
,
switchCount
int
)
bool
{
...
...
@@ -994,6 +1048,10 @@ func (h *GatewayHandler) handleFailoverExhausted(c *gin.Context, failoverErr *se
msg
=
*
rule
.
CustomMessage
}
if
rule
.
SkipMonitoring
{
c
.
Set
(
service
.
OpsSkipPassthroughKey
,
true
)
}
h
.
handleStreamingAwareError
(
c
,
respCode
,
"upstream_error"
,
msg
,
streamStarted
)
return
}
...
...
backend/internal/handler/gemini_v1beta_handler.go
View file @
abf5de69
...
...
@@ -598,6 +598,10 @@ func (h *GatewayHandler) handleGeminiFailoverExhausted(c *gin.Context, failoverE
msg
=
*
rule
.
CustomMessage
}
if
rule
.
SkipMonitoring
{
c
.
Set
(
service
.
OpsSkipPassthroughKey
,
true
)
}
googleError
(
c
,
respCode
,
msg
)
return
}
...
...
backend/internal/handler/openai_gateway_handler.go
View file @
abf5de69
...
...
@@ -453,6 +453,10 @@ func (h *OpenAIGatewayHandler) handleFailoverExhausted(c *gin.Context, failoverE
msg
=
*
rule
.
CustomMessage
}
if
rule
.
SkipMonitoring
{
c
.
Set
(
service
.
OpsSkipPassthroughKey
,
true
)
}
h
.
handleStreamingAwareError
(
c
,
respCode
,
"upstream_error"
,
msg
,
streamStarted
)
return
}
...
...
backend/internal/handler/ops_error_logger.go
View file @
abf5de69
...
...
@@ -553,6 +553,13 @@ func OpsErrorLoggerMiddleware(ops *service.OpsService) gin.HandlerFunc {
// Store request headers/body only when an upstream error occurred to keep overhead minimal.
entry
.
RequestHeadersJSON
=
extractOpsRetryRequestHeaders
(
c
)
// Skip logging if a passthrough rule with skip_monitoring=true matched.
if
v
,
ok
:=
c
.
Get
(
service
.
OpsSkipPassthroughKey
);
ok
{
if
skip
,
_
:=
v
.
(
bool
);
skip
{
return
}
}
enqueueOpsErrorLog
(
ops
,
entry
,
requestBody
)
return
}
...
...
@@ -560,6 +567,13 @@ func OpsErrorLoggerMiddleware(ops *service.OpsService) gin.HandlerFunc {
body
:=
w
.
buf
.
Bytes
()
parsed
:=
parseOpsErrorResponse
(
body
)
// Skip logging if a passthrough rule with skip_monitoring=true matched.
if
v
,
ok
:=
c
.
Get
(
service
.
OpsSkipPassthroughKey
);
ok
{
if
skip
,
_
:=
v
.
(
bool
);
skip
{
return
}
}
// Skip logging if the error should be filtered based on settings
if
shouldSkipOpsErrorLog
(
c
.
Request
.
Context
(),
ops
,
parsed
.
Message
,
string
(
body
),
c
.
Request
.
URL
.
Path
)
{
return
...
...
backend/internal/model/error_passthrough_rule.go
View file @
abf5de69
...
...
@@ -18,6 +18,7 @@ type ErrorPassthroughRule struct {
ResponseCode
*
int
`json:"response_code"`
// 自定义状态码(passthrough_code=false 时使用)
PassthroughBody
bool
`json:"passthrough_body"`
// 是否透传原始错误信息
CustomMessage
*
string
`json:"custom_message"`
// 自定义错误信息(passthrough_body=false 时使用)
SkipMonitoring
bool
`json:"skip_monitoring"`
// 是否跳过运维监控记录
Description
*
string
`json:"description"`
// 规则描述
CreatedAt
time
.
Time
`json:"created_at"`
UpdatedAt
time
.
Time
`json:"updated_at"`
...
...
backend/internal/pkg/antigravity/claude_types.go
View file @
abf5de69
...
...
@@ -27,7 +27,7 @@ type ClaudeMessage struct {
// ThinkingConfig Thinking 配置
type
ThinkingConfig
struct
{
Type
string
`json:"type"`
// "enabled"
or
"disabled"
Type
string
`json:"type"`
// "enabled"
/ "adaptive" /
"disabled"
BudgetTokens
int
`json:"budget_tokens,omitempty"`
// thinking budget
}
...
...
backend/internal/pkg/antigravity/client.go
View file @
abf5de69
...
...
@@ -115,6 +115,23 @@ type LoadCodeAssistResponse struct {
IneligibleTiers
[]
*
IneligibleTier
`json:"ineligibleTiers,omitempty"`
}
// OnboardUserRequest onboardUser 请求
type
OnboardUserRequest
struct
{
TierID
string
`json:"tierId"`
Metadata
struct
{
IDEType
string
`json:"ideType"`
Platform
string
`json:"platform,omitempty"`
PluginType
string
`json:"pluginType,omitempty"`
}
`json:"metadata"`
}
// OnboardUserResponse onboardUser 响应
type
OnboardUserResponse
struct
{
Name
string
`json:"name,omitempty"`
Done
bool
`json:"done"`
Response
map
[
string
]
any
`json:"response,omitempty"`
}
// GetTier 获取账户类型
// 优先返回 paidTier(付费订阅级别),否则返回 currentTier
func
(
r
*
LoadCodeAssistResponse
)
GetTier
()
string
{
...
...
@@ -371,6 +388,117 @@ func (c *Client) LoadCodeAssist(ctx context.Context, accessToken string) (*LoadC
return
nil
,
nil
,
lastErr
}
// OnboardUser 触发账号 onboarding,并返回 project_id
// 说明:
// 1) 部分账号 loadCodeAssist 不会立即返回 cloudaicompanionProject;
// 2) 这时需要调用 onboardUser 完成初始化,之后才能拿到 project_id。
func
(
c
*
Client
)
OnboardUser
(
ctx
context
.
Context
,
accessToken
,
tierID
string
)
(
string
,
error
)
{
tierID
=
strings
.
TrimSpace
(
tierID
)
if
tierID
==
""
{
return
""
,
fmt
.
Errorf
(
"tier_id 为空"
)
}
reqBody
:=
OnboardUserRequest
{
TierID
:
tierID
}
reqBody
.
Metadata
.
IDEType
=
"ANTIGRAVITY"
reqBody
.
Metadata
.
Platform
=
"PLATFORM_UNSPECIFIED"
reqBody
.
Metadata
.
PluginType
=
"GEMINI"
bodyBytes
,
err
:=
json
.
Marshal
(
reqBody
)
if
err
!=
nil
{
return
""
,
fmt
.
Errorf
(
"序列化请求失败: %w"
,
err
)
}
availableURLs
:=
BaseURLs
var
lastErr
error
for
urlIdx
,
baseURL
:=
range
availableURLs
{
apiURL
:=
baseURL
+
"/v1internal:onboardUser"
for
attempt
:=
1
;
attempt
<=
5
;
attempt
++
{
req
,
err
:=
http
.
NewRequestWithContext
(
ctx
,
http
.
MethodPost
,
apiURL
,
bytes
.
NewReader
(
bodyBytes
))
if
err
!=
nil
{
lastErr
=
fmt
.
Errorf
(
"创建请求失败: %w"
,
err
)
break
}
req
.
Header
.
Set
(
"Authorization"
,
"Bearer "
+
accessToken
)
req
.
Header
.
Set
(
"Content-Type"
,
"application/json"
)
req
.
Header
.
Set
(
"User-Agent"
,
UserAgent
)
resp
,
err
:=
c
.
httpClient
.
Do
(
req
)
if
err
!=
nil
{
lastErr
=
fmt
.
Errorf
(
"onboardUser 请求失败: %w"
,
err
)
if
shouldFallbackToNextURL
(
err
,
0
)
&&
urlIdx
<
len
(
availableURLs
)
-
1
{
log
.
Printf
(
"[antigravity] onboardUser URL fallback: %s -> %s"
,
baseURL
,
availableURLs
[
urlIdx
+
1
])
break
}
return
""
,
lastErr
}
respBodyBytes
,
err
:=
io
.
ReadAll
(
resp
.
Body
)
_
=
resp
.
Body
.
Close
()
if
err
!=
nil
{
return
""
,
fmt
.
Errorf
(
"读取响应失败: %w"
,
err
)
}
if
shouldFallbackToNextURL
(
nil
,
resp
.
StatusCode
)
&&
urlIdx
<
len
(
availableURLs
)
-
1
{
log
.
Printf
(
"[antigravity] onboardUser URL fallback (HTTP %d): %s -> %s"
,
resp
.
StatusCode
,
baseURL
,
availableURLs
[
urlIdx
+
1
])
break
}
if
resp
.
StatusCode
!=
http
.
StatusOK
{
lastErr
=
fmt
.
Errorf
(
"onboardUser 失败 (HTTP %d): %s"
,
resp
.
StatusCode
,
string
(
respBodyBytes
))
return
""
,
lastErr
}
var
onboardResp
OnboardUserResponse
if
err
:=
json
.
Unmarshal
(
respBodyBytes
,
&
onboardResp
);
err
!=
nil
{
lastErr
=
fmt
.
Errorf
(
"onboardUser 响应解析失败: %w"
,
err
)
return
""
,
lastErr
}
if
onboardResp
.
Done
{
if
projectID
:=
extractProjectIDFromOnboardResponse
(
onboardResp
.
Response
);
projectID
!=
""
{
DefaultURLAvailability
.
MarkSuccess
(
baseURL
)
return
projectID
,
nil
}
lastErr
=
fmt
.
Errorf
(
"onboardUser 完成但未返回 project_id"
)
return
""
,
lastErr
}
// done=false 时等待后重试(与 CLIProxyAPI 行为一致)
select
{
case
<-
time
.
After
(
2
*
time
.
Second
)
:
case
<-
ctx
.
Done
()
:
return
""
,
ctx
.
Err
()
}
}
}
if
lastErr
!=
nil
{
return
""
,
lastErr
}
return
""
,
fmt
.
Errorf
(
"onboardUser 未返回 project_id"
)
}
func
extractProjectIDFromOnboardResponse
(
resp
map
[
string
]
any
)
string
{
if
len
(
resp
)
==
0
{
return
""
}
if
v
,
ok
:=
resp
[
"cloudaicompanionProject"
];
ok
{
switch
project
:=
v
.
(
type
)
{
case
string
:
return
strings
.
TrimSpace
(
project
)
case
map
[
string
]
any
:
if
id
,
ok
:=
project
[
"id"
]
.
(
string
);
ok
{
return
strings
.
TrimSpace
(
id
)
}
}
}
return
""
}
// ModelQuotaInfo 模型配额信息
type
ModelQuotaInfo
struct
{
RemainingFraction
float64
`json:"remainingFraction"`
...
...
backend/internal/pkg/antigravity/client_test.go
View file @
abf5de69
...
...
@@ -1655,3 +1655,74 @@ func TestClient_FetchAvailableModels_404Fallback_RealCall(t *testing.T) {
t
.
Error
(
"应返回 fallback server 的模型 m1"
)
}
}
func
TestExtractProjectIDFromOnboardResponse
(
t
*
testing
.
T
)
{
t
.
Parallel
()
tests
:=
[]
struct
{
name
string
resp
map
[
string
]
any
want
string
}{
{
name
:
"nil response"
,
resp
:
nil
,
want
:
""
,
},
{
name
:
"empty response"
,
resp
:
map
[
string
]
any
{},
want
:
""
,
},
{
name
:
"project as string"
,
resp
:
map
[
string
]
any
{
"cloudaicompanionProject"
:
"my-project-123"
,
},
want
:
"my-project-123"
,
},
{
name
:
"project as string with spaces"
,
resp
:
map
[
string
]
any
{
"cloudaicompanionProject"
:
" my-project-123 "
,
},
want
:
"my-project-123"
,
},
{
name
:
"project as map with id"
,
resp
:
map
[
string
]
any
{
"cloudaicompanionProject"
:
map
[
string
]
any
{
"id"
:
"proj-from-map"
,
},
},
want
:
"proj-from-map"
,
},
{
name
:
"project as map without id"
,
resp
:
map
[
string
]
any
{
"cloudaicompanionProject"
:
map
[
string
]
any
{
"name"
:
"some-name"
,
},
},
want
:
""
,
},
{
name
:
"missing cloudaicompanionProject key"
,
resp
:
map
[
string
]
any
{
"otherField"
:
"value"
,
},
want
:
""
,
},
}
for
_
,
tc
:=
range
tests
{
t
.
Run
(
tc
.
name
,
func
(
t
*
testing
.
T
)
{
t
.
Parallel
()
got
:=
extractProjectIDFromOnboardResponse
(
tc
.
resp
)
if
got
!=
tc
.
want
{
t
.
Fatalf
(
"extractProjectIDFromOnboardResponse() = %q, want %q"
,
got
,
tc
.
want
)
}
})
}
}
Prev
1
2
3
4
Next
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment