2019-04-09 10:05:42 +00:00
package http
import (
"bytes"
"context"
2021-05-05 15:01:32 +00:00
"fmt"
2022-04-13 20:24:27 +00:00
"io"
2019-04-09 10:05:42 +00:00
"net/http"
"net/http/httptest"
"testing"
2021-05-05 15:01:32 +00:00
"time"
2019-04-09 10:05:42 +00:00
2020-04-03 17:39:20 +00:00
"github.com/influxdata/influxdb/v2"
pcontext "github.com/influxdata/influxdb/v2/context"
2021-09-13 19:12:35 +00:00
"github.com/influxdata/influxdb/v2/kit/platform"
"github.com/influxdata/influxdb/v2/kit/platform/errors"
2020-04-03 17:39:20 +00:00
kithttp "github.com/influxdata/influxdb/v2/kit/transport/http"
"github.com/influxdata/influxdb/v2/mock"
2021-09-13 19:12:35 +00:00
"github.com/influxdata/influxdb/v2/models"
2020-04-03 17:39:20 +00:00
influxtesting "github.com/influxdata/influxdb/v2/testing"
2019-12-04 23:10:23 +00:00
"go.uber.org/zap/zaptest"
2019-04-09 10:05:42 +00:00
)
refactor(kv): delete deprecated kv service code
This includes removal of a lot of kv.Service responsibilities. However,
it does not finish the re-wiring. It removes documents, telegrafs,
notification rules + endpoints, checks, orgs, users, buckets, passwords,
urms, labels and authorizations. There are some oustanding pieces that
are needed to get kv service compiling (dashboard service urm
dependency). Then all the call sites for kv service need updating and
the new implementations of telegraf and notification rules + endpoints
needed installing (along with any necessary migrations).
2020-10-20 13:25:36 +00:00
var user1ID = influxtesting . MustIDBase16 ( "020f755c3c082001" )
2019-04-09 10:05:42 +00:00
// NewMockDeleteBackend returns a DeleteBackend with mock services.
2019-12-04 23:10:23 +00:00
func NewMockDeleteBackend ( t * testing . T ) * DeleteBackend {
2019-04-09 10:05:42 +00:00
return & DeleteBackend {
2019-12-04 23:10:23 +00:00
log : zaptest . NewLogger ( t ) ,
2019-04-09 10:05:42 +00:00
DeleteService : mock . NewDeleteService ( ) ,
BucketService : mock . NewBucketService ( ) ,
OrganizationService : mock . NewOrganizationService ( ) ,
}
}
func TestDelete ( t * testing . T ) {
type fields struct {
DeleteService influxdb . DeleteService
OrganizationService influxdb . OrganizationService
BucketService influxdb . BucketService
}
type args struct {
queryParams map [ string ] [ ] string
body [ ] byte
authorizer influxdb . Authorizer
}
type wants struct {
statusCode int
contentType string
body string
}
tests := [ ] struct {
name string
fields fields
args args
wants wants
} {
{
name : "missing start time" ,
args : args {
queryParams : map [ string ] [ ] string { } ,
body : [ ] byte ( ` { } ` ) ,
authorizer : & influxdb . Authorization { UserID : user1ID } ,
} ,
fields : fields { } ,
wants : wants {
statusCode : http . StatusBadRequest ,
contentType : "application/json; charset=utf-8" ,
2019-10-17 15:59:24 +00:00
body : ` {
2019-04-09 10:05:42 +00:00
"code" : "invalid" ,
2022-10-14 19:09:32 +00:00
"message" : "error decoding json body: invalid RFC3339Nano for field start, please format your time with RFC3339Nano format, example: 2009-01-02T23:00:00Z"
2019-10-17 15:59:24 +00:00
} ` ,
2019-04-09 10:05:42 +00:00
} ,
} ,
{
name : "missing stop time" ,
args : args {
queryParams : map [ string ] [ ] string { } ,
body : [ ] byte ( ` { "start":"2009-01-01T23:00:00Z"} ` ) ,
authorizer : & influxdb . Authorization { UserID : user1ID } ,
} ,
fields : fields { } ,
wants : wants {
statusCode : http . StatusBadRequest ,
contentType : "application/json; charset=utf-8" ,
2019-10-17 15:59:24 +00:00
body : ` {
2019-04-09 10:05:42 +00:00
"code" : "invalid" ,
2022-10-14 19:09:32 +00:00
"message" : "error decoding json body: invalid RFC3339Nano for field stop, please format your time with RFC3339Nano format, example: 2009-01-01T23:00:00Z"
2019-10-17 15:59:24 +00:00
} ` ,
2019-04-09 10:05:42 +00:00
} ,
} ,
2021-05-05 15:01:32 +00:00
{
name : "start time too soon" ,
args : args {
queryParams : map [ string ] [ ] string { } ,
body : [ ] byte ( fmt . Sprintf ( ` { "start":"%s"} ` , time . Unix ( 0 , models . MinNanoTime - 1 ) . UTC ( ) . Format ( time . RFC3339Nano ) ) ) ,
authorizer : & influxdb . Authorization { UserID : user1ID } ,
} ,
fields : fields { } ,
wants : wants {
statusCode : http . StatusBadRequest ,
contentType : "application/json; charset=utf-8" ,
body : fmt . Sprintf ( ` {
"code" : "invalid" ,
2022-10-14 19:09:32 +00:00
"message" : "error decoding json body: %s"
2021-05-05 15:01:32 +00:00
} ` , msgStartTooSoon ) ,
} ,
} ,
{
name : "stop time too late" ,
args : args {
queryParams : map [ string ] [ ] string { } ,
body : [ ] byte ( fmt . Sprintf ( ` { "start":"2020-01-01T01:01:01Z", "stop":"%s"} ` , time . Unix ( 0 , models . MaxNanoTime + 1 ) . UTC ( ) . Format ( time . RFC3339Nano ) ) ) ,
authorizer : & influxdb . Authorization { UserID : user1ID } ,
} ,
fields : fields { } ,
wants : wants {
statusCode : http . StatusBadRequest ,
contentType : "application/json; charset=utf-8" ,
body : fmt . Sprintf ( ` {
"code" : "invalid" ,
2022-10-14 19:09:32 +00:00
"message" : "error decoding json body: %s"
2021-05-05 15:01:32 +00:00
} ` , msgStopTooLate ) ,
} ,
} ,
2019-04-09 10:05:42 +00:00
{
name : "missing org" ,
args : args {
queryParams : map [ string ] [ ] string { } ,
body : [ ] byte ( ` { "start":"2009-01-01T23:00:00Z","stop":"2009-11-10T01:00:00Z"} ` ) ,
authorizer : & influxdb . Authorization { UserID : user1ID } ,
} ,
fields : fields {
OrganizationService : & mock . OrganizationService {
FindOrganizationF : func ( ctx context . Context , f influxdb . OrganizationFilter ) ( * influxdb . Organization , error ) {
2021-03-30 18:10:02 +00:00
return nil , & errors . Error {
Code : errors . EInvalid ,
2019-04-09 10:05:42 +00:00
Msg : "Please provide either orgID or org" ,
}
} ,
} ,
} ,
wants : wants {
statusCode : http . StatusBadRequest ,
contentType : "application/json; charset=utf-8" ,
2019-10-17 15:59:24 +00:00
body : ` {
2019-04-09 10:05:42 +00:00
"code" : "invalid" ,
"message" : "Please provide either orgID or org"
2019-10-17 15:59:24 +00:00
} ` ,
2019-04-09 10:05:42 +00:00
} ,
} ,
{
name : "missing bucket" ,
args : args {
queryParams : map [ string ] [ ] string {
2021-01-29 16:50:57 +00:00
"org" : { "org1" } ,
2019-04-09 10:05:42 +00:00
} ,
body : [ ] byte ( ` { "start":"2009-01-01T23:00:00Z","stop":"2009-11-10T01:00:00Z"} ` ) ,
authorizer : & influxdb . Authorization { UserID : user1ID } ,
} ,
fields : fields {
BucketService : & mock . BucketService {
FindBucketFn : func ( ctx context . Context , f influxdb . BucketFilter ) ( * influxdb . Bucket , error ) {
2021-03-30 18:10:02 +00:00
return nil , & errors . Error {
Code : errors . EInvalid ,
2019-04-09 10:05:42 +00:00
Msg : "Please provide either bucketID or bucket" ,
}
} ,
} ,
OrganizationService : & mock . OrganizationService {
FindOrganizationF : func ( ctx context . Context , f influxdb . OrganizationFilter ) ( * influxdb . Organization , error ) {
return & influxdb . Organization {
2021-03-30 18:10:02 +00:00
ID : platform . ID ( 1 ) ,
2019-04-09 10:05:42 +00:00
} , nil
} ,
} ,
} ,
wants : wants {
statusCode : http . StatusBadRequest ,
contentType : "application/json; charset=utf-8" ,
2019-10-17 15:59:24 +00:00
body : ` {
2019-04-09 10:05:42 +00:00
"code" : "invalid" ,
"message" : "Please provide either bucketID or bucket"
2019-10-17 15:59:24 +00:00
} ` ,
2019-04-09 10:05:42 +00:00
} ,
} ,
{
name : "insufficient permissions delete" ,
args : args {
queryParams : map [ string ] [ ] string {
2021-01-29 16:50:57 +00:00
"org" : { "org1" } ,
"bucket" : { "buck1" } ,
2019-04-09 10:05:42 +00:00
} ,
body : [ ] byte ( ` { "start":"2009-01-01T23:00:00Z","stop":"2019-11-10T01:00:00Z"} ` ) ,
authorizer : & influxdb . Authorization { UserID : user1ID } ,
} ,
fields : fields {
BucketService : & mock . BucketService {
FindBucketFn : func ( ctx context . Context , f influxdb . BucketFilter ) ( * influxdb . Bucket , error ) {
return & influxdb . Bucket {
2021-03-30 18:10:02 +00:00
ID : platform . ID ( 2 ) ,
2019-04-09 10:05:42 +00:00
Name : "bucket1" ,
} , nil
} ,
} ,
OrganizationService : & mock . OrganizationService {
FindOrganizationF : func ( ctx context . Context , f influxdb . OrganizationFilter ) ( * influxdb . Organization , error ) {
return & influxdb . Organization {
2021-03-30 18:10:02 +00:00
ID : platform . ID ( 1 ) ,
2019-04-09 10:05:42 +00:00
} , nil
} ,
} ,
} ,
wants : wants {
statusCode : http . StatusForbidden ,
contentType : "application/json; charset=utf-8" ,
2019-10-17 15:59:24 +00:00
body : ` {
2019-04-09 10:05:42 +00:00
"code" : "forbidden" ,
"message" : "insufficient permissions to delete"
2019-10-17 15:59:24 +00:00
} ` ,
2019-04-09 10:05:42 +00:00
} ,
} ,
{
name : "no predicate delete" ,
args : args {
queryParams : map [ string ] [ ] string {
2021-01-29 16:50:57 +00:00
"org" : { "org1" } ,
"bucket" : { "buck1" } ,
2019-04-09 10:05:42 +00:00
} ,
body : [ ] byte ( ` { "start":"2009-01-01T23:00:00Z","stop":"2019-11-10T01:00:00Z"} ` ) ,
authorizer : & influxdb . Authorization {
UserID : user1ID ,
Status : influxdb . Active ,
Permissions : [ ] influxdb . Permission {
{
Action : influxdb . WriteAction ,
Resource : influxdb . Resource {
Type : influxdb . BucketsResourceType ,
2021-03-30 18:10:02 +00:00
ID : influxtesting . IDPtr ( platform . ID ( 2 ) ) ,
OrgID : influxtesting . IDPtr ( platform . ID ( 1 ) ) ,
2019-04-09 10:05:42 +00:00
} ,
} ,
} ,
} ,
} ,
fields : fields {
DeleteService : mock . NewDeleteService ( ) ,
BucketService : & mock . BucketService {
FindBucketFn : func ( ctx context . Context , f influxdb . BucketFilter ) ( * influxdb . Bucket , error ) {
return & influxdb . Bucket {
2021-03-30 18:10:02 +00:00
ID : platform . ID ( 2 ) ,
2019-04-09 10:05:42 +00:00
Name : "bucket1" ,
} , nil
} ,
} ,
OrganizationService : & mock . OrganizationService {
FindOrganizationF : func ( ctx context . Context , f influxdb . OrganizationFilter ) ( * influxdb . Organization , error ) {
return & influxdb . Organization {
2021-03-30 18:10:02 +00:00
ID : platform . ID ( 1 ) ,
2019-04-09 10:05:42 +00:00
Name : "org1" ,
} , nil
} ,
} ,
} ,
wants : wants {
2020-12-02 19:07:53 +00:00
statusCode : http . StatusNoContent ,
2019-10-17 15:59:24 +00:00
body : ` ` ,
2019-04-09 10:05:42 +00:00
} ,
} ,
{
2019-10-14 21:33:50 +00:00
name : "unsupported delete" ,
2019-04-09 10:05:42 +00:00
args : args {
queryParams : map [ string ] [ ] string {
2021-01-29 16:50:57 +00:00
"org" : { "org1" } ,
"bucket" : { "buck1" } ,
2019-04-09 10:05:42 +00:00
} ,
body : [ ] byte ( ` {
"start" : "2009-01-01T23:00:00Z" ,
2019-10-14 21:33:50 +00:00
"stop" : "2019-11-10T01:00:00Z" ,
"predicate" : "tag1=\"v1\" and (tag2=\"v2\" or tag3=\"v3\")"
} ` ) ,
authorizer : & influxdb . Authorization {
UserID : user1ID ,
Status : influxdb . Active ,
Permissions : [ ] influxdb . Permission {
2019-04-09 10:05:42 +00:00
{
2019-10-14 21:33:50 +00:00
Action : influxdb . WriteAction ,
Resource : influxdb . Resource {
Type : influxdb . BucketsResourceType ,
2021-03-30 18:10:02 +00:00
ID : influxtesting . IDPtr ( platform . ID ( 2 ) ) ,
OrgID : influxtesting . IDPtr ( platform . ID ( 1 ) ) ,
2019-10-14 21:33:50 +00:00
} ,
2019-04-09 10:05:42 +00:00
} ,
2019-10-14 21:33:50 +00:00
} ,
} ,
} ,
fields : fields {
DeleteService : mock . NewDeleteService ( ) ,
BucketService : & mock . BucketService {
FindBucketFn : func ( ctx context . Context , f influxdb . BucketFilter ) ( * influxdb . Bucket , error ) {
return & influxdb . Bucket {
2021-03-30 18:10:02 +00:00
ID : platform . ID ( 2 ) ,
2019-10-14 21:33:50 +00:00
Name : "bucket1" ,
} , nil
} ,
} ,
OrganizationService : & mock . OrganizationService {
FindOrganizationF : func ( ctx context . Context , f influxdb . OrganizationFilter ) ( * influxdb . Organization , error ) {
return & influxdb . Organization {
2021-03-30 18:10:02 +00:00
ID : platform . ID ( 1 ) ,
2019-10-14 21:33:50 +00:00
Name : "org1" ,
} , nil
} ,
} ,
} ,
wants : wants {
statusCode : http . StatusBadRequest ,
2019-10-17 15:59:24 +00:00
body : ` {
2019-10-14 21:33:50 +00:00
"code" : "invalid" ,
2022-10-14 19:09:32 +00:00
"message" : "error decoding json body: the logical operator OR is not supported yet at position 25"
} ` ,
} ,
} ,
{
name : "unsupported delete measurements" ,
args : args {
queryParams : map [ string ] [ ] string {
"org" : { "org1" } ,
"bucket" : { "buck1" } ,
} ,
body : [ ] byte ( ` {
"start" : "2009-01-01T23:00:00Z" ,
"stop" : "2019-11-10T01:00:00Z" ,
"predicate" : "_measurement=\"cpu\" or _measurement=\"mem\""
} ` ) ,
authorizer : & influxdb . Authorization {
UserID : user1ID ,
Status : influxdb . Active ,
Permissions : [ ] influxdb . Permission {
{
Action : influxdb . WriteAction ,
Resource : influxdb . Resource {
Type : influxdb . BucketsResourceType ,
ID : influxtesting . IDPtr ( platform . ID ( 2 ) ) ,
OrgID : influxtesting . IDPtr ( platform . ID ( 1 ) ) ,
} ,
} ,
} ,
} ,
} ,
fields : fields {
DeleteService : mock . NewDeleteService ( ) ,
BucketService : & mock . BucketService {
FindBucketFn : func ( ctx context . Context , f influxdb . BucketFilter ) ( * influxdb . Bucket , error ) {
return & influxdb . Bucket {
ID : platform . ID ( 2 ) ,
Name : "bucket1" ,
} , nil
} ,
} ,
OrganizationService : & mock . OrganizationService {
FindOrganizationF : func ( ctx context . Context , f influxdb . OrganizationFilter ) ( * influxdb . Organization , error ) {
return & influxdb . Organization {
ID : platform . ID ( 1 ) ,
Name : "org1" ,
} , nil
} ,
} ,
} ,
wants : wants {
statusCode : http . StatusBadRequest ,
body : ` {
"code" : "invalid" ,
"message" : "error decoding json body: the logical operator OR is not supported yet at position 19"
2019-10-17 15:59:24 +00:00
} ` ,
2019-10-14 21:33:50 +00:00
} ,
} ,
{
name : "complex delete" ,
args : args {
queryParams : map [ string ] [ ] string {
2021-01-29 16:50:57 +00:00
"org" : { "org1" } ,
"bucket" : { "buck1" } ,
2019-10-14 21:33:50 +00:00
} ,
body : [ ] byte ( ` {
"start" : "2009-01-01T23:00:00Z" ,
"stop" : "2019-11-10T01:00:00Z" ,
2022-10-14 19:09:32 +00:00
"predicate" : "_measurement=\"testing\" and tag1=\"v1\" and (tag2=\"v2\" and tag3=\"v3\")"
2019-04-09 10:05:42 +00:00
} ` ) ,
authorizer : & influxdb . Authorization {
UserID : user1ID ,
Status : influxdb . Active ,
Permissions : [ ] influxdb . Permission {
{
Action : influxdb . WriteAction ,
Resource : influxdb . Resource {
Type : influxdb . BucketsResourceType ,
2021-03-30 18:10:02 +00:00
ID : influxtesting . IDPtr ( platform . ID ( 2 ) ) ,
OrgID : influxtesting . IDPtr ( platform . ID ( 1 ) ) ,
2019-04-09 10:05:42 +00:00
} ,
} ,
} ,
} ,
} ,
fields : fields {
DeleteService : mock . NewDeleteService ( ) ,
BucketService : & mock . BucketService {
FindBucketFn : func ( ctx context . Context , f influxdb . BucketFilter ) ( * influxdb . Bucket , error ) {
return & influxdb . Bucket {
2021-03-30 18:10:02 +00:00
ID : platform . ID ( 2 ) ,
2019-04-09 10:05:42 +00:00
Name : "bucket1" ,
} , nil
} ,
} ,
OrganizationService : & mock . OrganizationService {
FindOrganizationF : func ( ctx context . Context , f influxdb . OrganizationFilter ) ( * influxdb . Organization , error ) {
return & influxdb . Organization {
2021-03-30 18:10:02 +00:00
ID : platform . ID ( 1 ) ,
2019-04-09 10:05:42 +00:00
Name : "org1" ,
} , nil
} ,
} ,
} ,
wants : wants {
2020-12-02 19:07:53 +00:00
statusCode : http . StatusNoContent ,
2019-10-17 15:59:24 +00:00
body : ` ` ,
2019-04-09 10:05:42 +00:00
} ,
} ,
}
for _ , tt := range tests {
t . Run ( tt . name , func ( t * testing . T ) {
2019-12-04 23:10:23 +00:00
deleteBackend := NewMockDeleteBackend ( t )
2021-09-13 19:12:35 +00:00
deleteBackend . HTTPErrorHandler = kithttp . NewErrorHandler ( zaptest . NewLogger ( t ) )
2019-04-09 10:05:42 +00:00
deleteBackend . DeleteService = tt . fields . DeleteService
deleteBackend . OrganizationService = tt . fields . OrganizationService
deleteBackend . BucketService = tt . fields . BucketService
2019-12-04 23:10:23 +00:00
h := NewDeleteHandler ( zaptest . NewLogger ( t ) , deleteBackend )
2019-04-09 10:05:42 +00:00
r := httptest . NewRequest ( "POST" , "http://any.tld" , bytes . NewReader ( tt . args . body ) )
qp := r . URL . Query ( )
for k , vs := range tt . args . queryParams {
for _ , v := range vs {
qp . Add ( k , v )
}
}
r = r . WithContext ( pcontext . SetAuthorizer ( r . Context ( ) , tt . args . authorizer ) )
r . URL . RawQuery = qp . Encode ( )
w := httptest . NewRecorder ( )
h . handleDelete ( w , r )
res := w . Result ( )
content := res . Header . Get ( "Content-Type" )
2022-04-13 20:24:27 +00:00
body , _ := io . ReadAll ( res . Body )
2019-04-09 10:05:42 +00:00
if res . StatusCode != tt . wants . statusCode {
t . Errorf ( "%q. handleDelete() = %v, want %v" , tt . name , res . StatusCode , tt . wants . statusCode )
}
if tt . wants . contentType != "" && content != tt . wants . contentType {
t . Errorf ( "%q. handleDelete() = %v, want %v" , tt . name , content , tt . wants . contentType )
}
if tt . wants . body != "" {
if eq , diff , err := jsonEqual ( string ( body ) , tt . wants . body ) ; err != nil {
2020-11-11 18:54:21 +00:00
t . Errorf ( "%q, handleDelete(). error unmarshalling json %v" , tt . name , err )
2019-04-09 10:05:42 +00:00
} else if ! eq {
t . Errorf ( "%q. handleDelete() = ***%s***" , tt . name , diff )
}
}
} )
}
}