@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

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

Move project slug normalization inside project Query

Summary:
Ref T10010. We currently require `withSlugs()` to have properly formatted slugs, but this leads to similar code in several places.

Instead: accept any slug, normalize slugs in the query, return a map so callers can figure out what happened if they want.

This tends to do the right thing by default, while keeping enough information around to do more complex things if necessary. A similar approach for querying commits has worked well in Diffusion.

Test Plan: Added and executed unit tests.

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T10010

Differential Revision: https://secure.phabricator.com/D14888

+178 -12
+84
src/applications/project/__tests__/PhabricatorProjectCoreTestCase.php
··· 493 493 $this->assertFalse((bool)$this->refreshProject($child, $user2)); 494 494 } 495 495 496 + public function testSlugMaps() { 497 + // When querying by slugs, slugs should be normalized and the mapping 498 + // should be reported correctly. 499 + $user = $this->createUser(); 500 + $user->save(); 501 + 502 + $name = 'queryslugproject'; 503 + $name2 = 'QUERYslugPROJECT'; 504 + $slug = 'queryslugextra'; 505 + $slug2 = 'QuErYSlUgExTrA'; 506 + 507 + $project = PhabricatorProject::initializeNewProject($user); 508 + 509 + $xactions = array(); 510 + 511 + $xactions[] = id(new PhabricatorProjectTransaction()) 512 + ->setTransactionType(PhabricatorProjectTransaction::TYPE_NAME) 513 + ->setNewValue($name); 514 + 515 + $xactions[] = id(new PhabricatorProjectTransaction()) 516 + ->setTransactionType(PhabricatorProjectTransaction::TYPE_SLUGS) 517 + ->setNewValue(array($slug)); 518 + 519 + $this->applyTransactions($project, $user, $xactions); 520 + 521 + $project_query = id(new PhabricatorProjectQuery()) 522 + ->setViewer($user) 523 + ->withSlugs(array($name)); 524 + $project_query->execute(); 525 + $map = $project_query->getSlugMap(); 526 + 527 + $this->assertEqual( 528 + array( 529 + $name => $project->getPHID(), 530 + ), 531 + ipull($map, 'projectPHID')); 532 + 533 + $project_query = id(new PhabricatorProjectQuery()) 534 + ->setViewer($user) 535 + ->withSlugs(array($slug)); 536 + $project_query->execute(); 537 + $map = $project_query->getSlugMap(); 538 + 539 + $this->assertEqual( 540 + array( 541 + $slug => $project->getPHID(), 542 + ), 543 + ipull($map, 'projectPHID')); 544 + 545 + $project_query = id(new PhabricatorProjectQuery()) 546 + ->setViewer($user) 547 + ->withSlugs(array($name, $slug, $name2, $slug2)); 548 + $project_query->execute(); 549 + $map = $project_query->getSlugMap(); 550 + 551 + $expect = array( 552 + $name => $project->getPHID(), 553 + $slug => $project->getPHID(), 554 + $name2 => $project->getPHID(), 555 + $slug2 => $project->getPHID(), 556 + ); 557 + 558 + $actual = ipull($map, 'projectPHID'); 559 + 560 + ksort($expect); 561 + ksort($actual); 562 + 563 + $this->assertEqual($expect, $actual); 564 + 565 + $expect = array( 566 + $name => $name, 567 + $slug => $slug, 568 + $name2 => $name, 569 + $slug2 => $slug, 570 + ); 571 + 572 + $actual = ipull($map, 'slug'); 573 + 574 + ksort($expect); 575 + ksort($actual); 576 + 577 + $this->assertEqual($expect, $actual); 578 + } 579 + 496 580 private function attemptProjectEdit( 497 581 PhabricatorProject $proj, 498 582 PhabricatorUser $user,
+94 -12
src/applications/project/query/PhabricatorProjectQuery.php
··· 7 7 private $phids; 8 8 private $memberPHIDs; 9 9 private $slugs; 10 + private $slugNormals; 11 + private $slugMap; 10 12 private $names; 11 13 private $nameTokens; 12 14 private $icons; ··· 157 159 ); 158 160 } 159 161 162 + public function getSlugMap() { 163 + if ($this->slugMap === null) { 164 + throw new PhutilInvalidStateException('execute'); 165 + } 166 + return $this->slugMap; 167 + } 168 + 169 + protected function willExecute() { 170 + $this->slugMap = array(); 171 + $this->slugNormals = array(); 172 + if ($this->slugs) { 173 + foreach ($this->slugs as $slug) { 174 + $normal = PhabricatorSlug::normalizeProjectSlug($slug); 175 + $this->slugNormals[$slug] = $normal; 176 + } 177 + } 178 + } 179 + 160 180 protected function loadPage() { 161 181 return $this->loadStandardPage($this->newResultObject()); 162 182 } ··· 287 307 } 288 308 } 289 309 290 - if ($this->needSlugs) { 291 - $slugs = id(new PhabricatorProjectSlug()) 292 - ->loadAllWhere( 293 - 'projectPHID IN (%Ls)', 294 - mpull($projects, 'getPHID')); 295 - $slugs = mgroup($slugs, 'getProjectPHID'); 296 - foreach ($projects as $project) { 297 - $project_slugs = idx($slugs, $project->getPHID(), array()); 298 - $project->attachSlugs($project_slugs); 299 - } 300 - } 310 + $this->loadSlugs($projects); 301 311 302 312 return $projects; 303 313 } ··· 356 366 $where[] = qsprintf( 357 367 $conn, 358 368 'slug.slug IN (%Ls)', 359 - $this->slugs); 369 + $this->slugNormals); 360 370 } 361 371 362 372 if ($this->names !== null) { ··· 581 591 } 582 592 583 593 return $ancestors; 594 + } 595 + 596 + private function loadSlugs(array $projects) { 597 + // Build a map from primary slugs to projects. 598 + $primary_map = array(); 599 + foreach ($projects as $project) { 600 + $primary_slug = $project->getPrimarySlug(); 601 + if ($primary_slug === null) { 602 + continue; 603 + } 604 + 605 + $primary_map[$primary_slug] = $project; 606 + } 607 + 608 + // Link up all of the queried slugs which correspond to primary 609 + // slugs. If we can link up everything from this (no slugs were queried, 610 + // or only primary slugs were queried) we don't need to load anything 611 + // else. 612 + $unknown = $this->slugNormals; 613 + foreach ($unknown as $input => $normal) { 614 + if (!isset($primary_map[$normal])) { 615 + continue; 616 + } 617 + 618 + $this->slugMap[$input] = array( 619 + 'slug' => $normal, 620 + 'projectPHID' => $primary_map[$normal]->getPHID(), 621 + ); 622 + 623 + unset($unknown[$input]); 624 + } 625 + 626 + // If we need slugs, we have to load everything. 627 + // If we still have some queried slugs which we haven't mapped, we only 628 + // need to look for them. 629 + // If we've mapped everything, we don't have to do any work. 630 + $project_phids = mpull($projects, 'getPHID'); 631 + if ($this->needSlugs) { 632 + $slugs = id(new PhabricatorProjectSlug())->loadAllWhere( 633 + 'projectPHID IN (%Ls)', 634 + $project_phids); 635 + } else if ($unknown) { 636 + $slugs = id(new PhabricatorProjectSlug())->loadAllWhere( 637 + 'projectPHID IN (%Ls) AND slug IN (%Ls)', 638 + $project_phids, 639 + $unknown); 640 + } else { 641 + $slugs = array(); 642 + } 643 + 644 + // Link up any slugs we were not able to link up earlier. 645 + $extra_map = mpull($slugs, 'getProjectPHID', 'getSlug'); 646 + foreach ($unknown as $input => $normal) { 647 + if (!isset($extra_map[$normal])) { 648 + continue; 649 + } 650 + 651 + $this->slugMap[$input] = array( 652 + 'slug' => $normal, 653 + 'projectPHID' => $extra_map[$normal], 654 + ); 655 + 656 + unset($unknown[$input]); 657 + } 658 + 659 + if ($this->needSlugs) { 660 + $slug_groups = mgroup($slugs, 'getProjectPHID'); 661 + foreach ($projects as $project) { 662 + $project_slugs = idx($slug_groups, $project->getPHID(), array()); 663 + $project->attachSlugs($project_slugs); 664 + } 665 + } 584 666 } 585 667 586 668 }