this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

labelmaker: progress on tests and error codes

+230 -31
+4 -1
labeling/admin.go
··· 6 6 "time" 7 7 8 8 comatproto "github.com/bluesky-social/indigo/api/atproto" 9 + appbsky "github.com/bluesky-social/indigo/api/bsky" 10 + lexutil "github.com/bluesky-social/indigo/lex/util" 9 11 "github.com/bluesky-social/indigo/models" 10 12 ) 11 13 ··· 31 33 IndexedAt: indexedAt, 32 34 Moderation: nil, 33 35 Repo: repoView, 34 - // TODO: Value 36 + // XXX: replace with actual record 37 + Value: lexutil.LexiconTypeDecoder{&appbsky.FeedPost{}}, 35 38 } 36 39 if uri != nil { 37 40 recordView.Uri = *uri
+191 -7
labeling/moderation_test.go
··· 15 15 comatproto "github.com/bluesky-social/indigo/api/atproto" 16 16 ) 17 17 18 - func TestLabelMakerXRPCReport(t *testing.T) { 18 + func TestLabelMakerXRPCReportRepo(t *testing.T) { 19 19 e := echo.New() 20 20 lm := testLabelMaker(t) 21 21 22 22 // create and read back a basic repo report 23 23 rt := "spam" 24 + reportedDid := "did:plc:123" 24 25 report := comatproto.ReportCreate_Input{ 25 26 //Reason 26 27 ReasonType: &rt, 27 28 Subject: &comatproto.ReportCreate_Input_Subject{ 28 29 RepoRepoRef: &comatproto.RepoRepoRef{ 29 - //com.atproto.repo.repoRef 30 - Did: "did:plc:123", 30 + Did: reportedDid, 31 31 }, 32 32 }, 33 33 } ··· 50 50 } 51 51 assert.Equal(t, report.ReasonType, out.ReasonType) 52 52 assert.Equal(t, report.Subject.RepoRepoRef, out.Subject.RepoRepoRef) 53 + reportId := out.Id 53 54 54 55 // read it back 55 56 params := make(url.Values) 56 - params.Set("id", strconv.Itoa(int(out.Id))) 57 + params.Set("id", strconv.Itoa(int(reportId))) 57 58 req = httptest.NewRequest(http.MethodGet, "/xrpc/com.atproto.admin.getModerationReport?"+params.Encode(), nil) 58 59 recorder = httptest.NewRecorder() 59 60 c = e.NewContext(req, recorder) 60 61 assert.NoError(t, lm.HandleComAtprotoAdminGetModerationReport(c)) 61 62 assert.Equal(t, 200, recorder.Code) 63 + var vd comatproto.AdminModerationReport_ViewDetail 64 + if err := json.Unmarshal([]byte(recorder.Body.String()), &vd); err != nil { 65 + t.Fatal(err) 66 + } 67 + assert.Equal(t, vd.Id, reportId, vd.Id) 68 + assert.Equal(t, vd.ReasonType, report.ReasonType) 69 + assert.Nil(t, vd.Reason) 70 + assert.Equal(t, vd.Subject.AdminRepo_View.Did, reportedDid) 71 + assert.Nil(t, vd.Subject.AdminRecord_View) 72 + // TODO: additional AdminRecord_View fields 62 73 63 - var view comatproto.AdminModerationReport_ViewDetail 64 - if err := json.Unmarshal([]byte(recorder.Body.String()), &view); err != nil { 74 + // read back via get multi 75 + req = httptest.NewRequest(http.MethodGet, "/xrpc/com.atproto.admin.getModerationReports", nil) 76 + recorder = httptest.NewRecorder() 77 + c = e.NewContext(req, recorder) 78 + assert.NoError(t, lm.HandleComAtprotoAdminGetModerationReports(c)) 79 + assert.Equal(t, 200, recorder.Code) 80 + var reportsOut comatproto.AdminGetModerationReports_Output 81 + if err := json.Unmarshal([]byte(recorder.Body.String()), &reportsOut); err != nil { 65 82 t.Fatal(err) 66 83 } 67 - assert.Equal(t, report.ReasonType, view.ReasonType) 84 + assert.Equal(t, len(reportsOut.Reports), 1) 85 + assert.Equal(t, reportsOut.Reports[0].Id, reportId) 86 + 87 + } 88 + 89 + func TestLabelMakerXRPCReportRepoBad(t *testing.T) { 90 + e := echo.New() 91 + lm := testLabelMaker(t) 92 + 93 + table := []struct { 94 + rType string 95 + rDid string 96 + statusCode int 97 + }{ 98 + {"spam", "did:plc:123", 200}, 99 + {"", "did:plc:123", 400}, 100 + {"spam", "", 400}, 101 + } 102 + 103 + for _, row := range table { 104 + 105 + report := comatproto.ReportCreate_Input{ 106 + //Reason 107 + ReasonType: &row.rType, 108 + Subject: &comatproto.ReportCreate_Input_Subject{ 109 + RepoRepoRef: &comatproto.RepoRepoRef{ 110 + Did: row.rDid, 111 + }, 112 + }, 113 + } 114 + reportJSON, err := json.Marshal(report) 115 + if err != nil { 116 + t.Fatal(err) 117 + } 118 + req := httptest.NewRequest(http.MethodPost, "/xrpc/com.atproto.report.create", strings.NewReader(string(reportJSON))) 119 + req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON) 120 + recorder := httptest.NewRecorder() 121 + c := e.NewContext(req, recorder) 122 + err = lm.HandleComAtprotoReportCreate(c) 123 + if err != nil { 124 + httpError, _ := err.(*echo.HTTPError) 125 + assert.Equal(t, row.statusCode, httpError.Code) 126 + } else { 127 + assert.Equal(t, row.statusCode, recorder.Code) 128 + } 129 + } 130 + } 131 + 132 + func TestLabelMakerXRPCReportRecord(t *testing.T) { 133 + e := echo.New() 134 + lm := testLabelMaker(t) 135 + // create a second report, on a record 136 + rt := "spam" 137 + reason := "I just don't like it!" 138 + uri := "at://did:plc:123/com.example.record/bcd234" 139 + cid := "bafyreie5cvv4h45feadgeuwhbcutmh6t2ceseocckahdoe6uat64zmz454" 140 + report := comatproto.ReportCreate_Input{ 141 + Reason: &reason, 142 + ReasonType: &rt, 143 + Subject: &comatproto.ReportCreate_Input_Subject{ 144 + RepoRecordRef: &comatproto.RepoRecordRef{ 145 + //com.atproto.repo.recordRef 146 + Uri: uri, 147 + Cid: &cid, 148 + }, 149 + }, 150 + } 151 + reportJSON, err := json.Marshal(report) 152 + if err != nil { 153 + t.Fatal(err) 154 + } 155 + req := httptest.NewRequest(http.MethodPost, "/xrpc/com.atproto.report.create", strings.NewReader(string(reportJSON))) 156 + req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON) 157 + recorder := httptest.NewRecorder() 158 + c := e.NewContext(req, recorder) 159 + 160 + assert.NoError(t, lm.HandleComAtprotoReportCreate(c)) 161 + // TODO: "Created" / 201 162 + assert.Equal(t, 200, recorder.Code) 163 + 164 + var out comatproto.ReportCreate_Output 165 + if err := json.Unmarshal([]byte(recorder.Body.String()), &out); err != nil { 166 + t.Fatal(err) 167 + } 168 + assert.Equal(t, report.ReasonType, out.ReasonType) 169 + assert.Equal(t, report.Subject.RepoRepoRef, out.Subject.RepoRepoRef) 170 + reportId := out.Id 171 + 172 + // read it back 173 + params := make(url.Values) 174 + params.Set("id", strconv.Itoa(int(reportId))) 175 + req = httptest.NewRequest(http.MethodGet, "/xrpc/com.atproto.admin.getModerationReport?"+params.Encode(), nil) 176 + recorder = httptest.NewRecorder() 177 + c = e.NewContext(req, recorder) 178 + assert.NoError(t, lm.HandleComAtprotoAdminGetModerationReport(c)) 179 + assert.Equal(t, 200, recorder.Code) 180 + var vd comatproto.AdminModerationReport_ViewDetail 181 + if err := json.Unmarshal([]byte(recorder.Body.String()), &vd); err != nil { 182 + t.Fatal(err) 183 + } 184 + assert.Equal(t, reportId, vd.Id) 185 + assert.Equal(t, *report.Reason, reason) 186 + assert.Equal(t, report.ReasonType, vd.ReasonType) 187 + } 188 + 189 + func TestLabelMakerXRPCReportRecordBad(t *testing.T) { 190 + e := echo.New() 191 + lm := testLabelMaker(t) 192 + 193 + uriStr := "at://did:plc:123/com.example.record/bcd234" 194 + cidStr := "bafyreie5cvv4h45feadgeuwhbcutmh6t2ceseocckahdoe6uat64zmz454" 195 + emptyStr := "" 196 + table := []struct { 197 + rType string 198 + rUri string 199 + rCid *string 200 + statusCode int 201 + }{ 202 + {"spam", uriStr, &cidStr, 200}, 203 + {"spam", uriStr, nil, 400}, 204 + {"", uriStr, &cidStr, 400}, 205 + {"spam", "", &cidStr, 400}, 206 + {"spam", uriStr, &emptyStr, 400}, 207 + } 208 + 209 + for _, row := range table { 210 + 211 + report := comatproto.ReportCreate_Input{ 212 + ReasonType: &row.rType, 213 + Subject: &comatproto.ReportCreate_Input_Subject{ 214 + RepoRecordRef: &comatproto.RepoRecordRef{ 215 + //com.atproto.repo.recordRef 216 + Uri: row.rUri, 217 + Cid: row.rCid, 218 + }, 219 + }, 220 + } 221 + reportJSON, err := json.Marshal(report) 222 + if err != nil { 223 + t.Fatal(err) 224 + } 225 + req := httptest.NewRequest(http.MethodPost, "/xrpc/com.atproto.report.create", strings.NewReader(string(reportJSON))) 226 + req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON) 227 + recorder := httptest.NewRecorder() 228 + c := e.NewContext(req, recorder) 229 + err = lm.HandleComAtprotoReportCreate(c) 230 + if err != nil { 231 + httpError, _ := err.(*echo.HTTPError) 232 + assert.Equal(t, row.statusCode, httpError.Code) 233 + } else { 234 + assert.Equal(t, row.statusCode, recorder.Code) 235 + } 236 + } 237 + } 238 + 239 + func TestLabelMakerXRPCReportAction(t *testing.T) { 240 + //e := echo.New() 241 + //lm := testLabelMaker(t) 242 + 243 + // XXX: create report 244 + // XXX: action report 245 + // XXX: get action 246 + // XXX: get actions (plural) 247 + // XXX: get report (should have action included) 248 + // XXX: reverse action 249 + // XXX: get action 250 + // XXX: get actions (plural) 251 + // XXX: get report (should not have action included) 68 252 }
+35 -23
labeling/xrpc_handlers.go
··· 89 89 90 90 func (s *Server) handleComAtprotoRepoGetRecord(ctx context.Context, c string, collection string, rkey string, user string) (*atproto.RepoGetRecord_Output, error) { 91 91 if user != s.user.Did { 92 - return nil, fmt.Errorf("unknown user: %s", user) 92 + return nil, echo.NewHTTPError(404, "unknown user: %s", user) 93 93 } 94 94 95 95 var maybeCid cid.Cid ··· 103 103 104 104 reccid, rec, err := s.repoman.GetRecord(ctx, s.user.UserId, collection, rkey, maybeCid) 105 105 if err != nil { 106 + // XXX: handle 404 106 107 return nil, fmt.Errorf("repoman GetRecord: %w", err) 107 108 } 108 109 ··· 133 134 } 134 135 135 136 srcQuery := s.db 136 - fmt.Printf("%v\n", sources) 137 137 for _, src := range sources { 138 138 if src == "*" { 139 139 continue ··· 217 217 if cursor != "" { 218 218 cursorID, err := strconv.Atoi(cursor) 219 219 if err != nil { 220 - // XXX: HTTP 400 error 221 - return nil, err 220 + return nil, echo.NewHTTPError(400, "invalid cursor param: %v", cursor) 222 221 } 223 222 q = q.Where("id < ?", cursorID) 224 223 } ··· 280 279 if cursor != "" { 281 280 cursorID, err := strconv.Atoi(cursor) 282 281 if err != nil { 283 - // XXX: HTTP 400 error 284 - return nil, err 282 + return nil, echo.NewHTTPError(400, "invalid cursor param: %v", cursor) 285 283 } 286 284 q = q.Where("id < ?", cursorID) 287 285 } ··· 332 330 333 331 func (s *Server) handleComAtprotoAdminResolveModerationReports(ctx context.Context, body *atproto.AdminResolveModerationReports_Input) (*atproto.AdminModerationAction_View, error) { 334 332 335 - // XXX: check that body fields are not nil/empty: CreatedBy, ReportIds 333 + if body.CreatedBy == "" { 334 + return nil, echo.NewHTTPError(400, "createdBy param must be non-empty") 335 + } 336 + if len(body.ReportIds) == 0 { 337 + return nil, echo.NewHTTPError(400, "at least one reportId required") 338 + } 336 339 337 340 var rows []models.ModerationReportResolution 338 341 for _, reportId := range body.ReportIds { ··· 367 370 368 371 func (s *Server) handleComAtprotoAdminReverseModerationAction(ctx context.Context, body *atproto.AdminReverseModerationAction_Input) (*atproto.AdminModerationAction_View, error) { 369 372 370 - // XXX: validate body CreatedBy, Reason 373 + if body.CreatedBy == "" { 374 + return nil, echo.NewHTTPError(400, "createBy param must be non-empty") 375 + } 376 + /* XXX: 377 + if body.Reason == "" { 378 + return nil, echo.NewHTTPError(400, "reason param was provided, but empty string") 379 + } 380 + */ 371 381 372 382 row := models.ModerationAction{ID: uint64(body.Id)} 373 383 result := s.db.First(&row) 374 384 if result.Error != nil { 375 385 // XXX: if not found, 404 386 + //return nil, echo.NewHTTPError(404, "moderation action not found: %d", body.Id) 376 387 return nil, result.Error 377 388 } 378 389 379 390 if row.ReversedAt != nil { 380 - // XXX: http 400 (already reversed) 381 - return nil, fmt.Errorf("action has already been reversed actionId=%d", body.Id) 391 + return nil, echo.NewHTTPError(400, "action has already been reversed actionId=%d", body.Id) 382 392 } 383 393 384 394 now := time.Now() ··· 414 424 Did: row.SubjectDid, 415 425 } 416 426 } else if body.Subject.RepoRecordRef != nil { 417 - if row.SubjectCid == nil { 418 - return nil, fmt.Errorf("this implementation requires a strong record ref (aka, with CID) in reports") 427 + if body.Subject.RepoRecordRef.Cid == nil { 428 + return nil, echo.NewHTTPError(400, "this implementation requires a strong record ref (aka, with CID) in reports") 419 429 } 420 430 row.SubjectType = "com.atproto.repo.recordRef" 421 431 // TODO: row.SubjectDid from URI? ··· 427 437 Cid: *row.SubjectCid, 428 438 } 429 439 } else { 430 - // XXX: 400 error (and similar instances) 431 - return nil, fmt.Errorf("report subject must be a repoRef or a recordRef") 440 + return nil, echo.NewHTTPError(400, "report subject must be a repoRef or a recordRef") 432 441 } 433 442 434 443 result := s.db.Create(&row) ··· 450 459 451 460 func (s *Server) handleComAtprotoReportCreate(ctx context.Context, body *atproto.ReportCreate_Input) (*atproto.ReportCreate_Output, error) { 452 461 453 - // TODO: shouldn't lexgen and the endpoint handlers help with these already? both are required fields 454 - if body.ReasonType == nil { 455 - // XXX: 400 error 456 - return nil, fmt.Errorf("ReasonType is required") 462 + if body.ReasonType == nil || *body.ReasonType == "" { 463 + return nil, echo.NewHTTPError(400, "reasonType is required") 457 464 } 458 465 if body.Subject == nil { 459 - // XXX: 400 error 460 - return nil, fmt.Errorf("Subject is required") 466 + return nil, echo.NewHTTPError(400, "Subject is required") 461 467 } 462 468 463 469 row := models.ModerationReport{ ··· 468 474 } 469 475 var outSubj atproto.ReportCreate_Output_Subject 470 476 if body.Subject.RepoRepoRef != nil { 477 + if body.Subject.RepoRepoRef.Did == "" { 478 + return nil, echo.NewHTTPError(400, "DID is required for repo reports") 479 + } 471 480 row.SubjectType = "com.atproto.repo.repoRef" 472 481 row.SubjectDid = body.Subject.RepoRepoRef.Did 473 482 outSubj.RepoRepoRef = &atproto.RepoRepoRef{ ··· 475 484 Did: row.SubjectDid, 476 485 } 477 486 } else if body.Subject.RepoRecordRef != nil { 478 - if row.SubjectCid == nil { 479 - return nil, fmt.Errorf("this implementation requires a strong record ref (aka, with CID) in reports") 487 + if body.Subject.RepoRecordRef.Uri == "" { 488 + return nil, echo.NewHTTPError(400, "URI required for record reports") 489 + } 490 + if body.Subject.RepoRecordRef.Cid == nil || *body.Subject.RepoRecordRef.Cid == "" { 491 + return nil, echo.NewHTTPError(400, "this implementation requires a strong record ref (aka, with CID) in reports") 480 492 } 481 493 row.SubjectType = "com.atproto.repo.recordRef" 482 494 // TODO: row.SubjectDid from URI? ··· 488 500 Cid: *row.SubjectCid, 489 501 } 490 502 } else { 491 - return nil, fmt.Errorf("report subject must be a repoRef or a recordRef") 503 + return nil, echo.NewHTTPError(400, "report subject must be a repoRef or a recordRef") 492 504 } 493 505 494 506 result := s.db.Create(&row)