Support ID-based repository URIs, and canonicalize repository URIs
Summary: Ref T4245. Make `/diffusion/123/` work, but redirect the user to `/diffusion/XYZ/` if the repository has a callsign. (Right now, every repository has a callsign, so this always redirects.) Also redirect `/R123:abcdef` if the repository has a callsign. Also also, move the Pull garbage collector somewhere more sensible. Test Plan: - Added test coverage. - Visited `/diffusion/1/`, was redirected. - Visited `/diffusion/R1:abcdef`, was redirected. - Browsed Diffusion normally. Reviewers: chad Reviewed By: chad Maniphest Tasks: T4245 Differential Revision: https://secure.phabricator.com/D15301
This commit is contained in:
		@@ -689,7 +689,7 @@ phutil_register_library_map(array(
 | 
			
		||||
    'DiffusionPreCommitRefRepositoryHeraldField' => 'applications/diffusion/herald/DiffusionPreCommitRefRepositoryHeraldField.php',
 | 
			
		||||
    'DiffusionPreCommitRefRepositoryProjectsHeraldField' => 'applications/diffusion/herald/DiffusionPreCommitRefRepositoryProjectsHeraldField.php',
 | 
			
		||||
    'DiffusionPreCommitRefTypeHeraldField' => 'applications/diffusion/herald/DiffusionPreCommitRefTypeHeraldField.php',
 | 
			
		||||
    'DiffusionPullEventGarbageCollector' => 'applications/diffusion/DiffusionPullEventGarbageCollector.php',
 | 
			
		||||
    'DiffusionPullEventGarbageCollector' => 'applications/diffusion/garbagecollector/DiffusionPullEventGarbageCollector.php',
 | 
			
		||||
    'DiffusionPushCapability' => 'applications/diffusion/capability/DiffusionPushCapability.php',
 | 
			
		||||
    'DiffusionPushEventViewController' => 'applications/diffusion/controller/DiffusionPushEventViewController.php',
 | 
			
		||||
    'DiffusionPushLogController' => 'applications/diffusion/controller/DiffusionPushLogController.php',
 | 
			
		||||
 
 | 
			
		||||
@@ -64,7 +64,11 @@ final class PhabricatorDiffusionApplication extends PhabricatorApplication {
 | 
			
		||||
          '(?:query/(?P<queryKey>[^/]+)/)?' => 'DiffusionPushLogListController',
 | 
			
		||||
          'view/(?P<id>\d+)/' => 'DiffusionPushEventViewController',
 | 
			
		||||
        ),
 | 
			
		||||
        '(?P<repositoryCallsign>[A-Z]+)/' => array(
 | 
			
		||||
        '(?:'.
 | 
			
		||||
          '(?P<repositoryCallsign>[A-Z]+)'.
 | 
			
		||||
          '|'.
 | 
			
		||||
          '(?P<repositoryID>[1-9]\d*)'.
 | 
			
		||||
        ')/' => array(
 | 
			
		||||
          '' => 'DiffusionRepositoryController',
 | 
			
		||||
 | 
			
		||||
          'repository/(?P<dblob>.*)'    => 'DiffusionRepositoryController',
 | 
			
		||||
@@ -115,8 +119,9 @@ final class PhabricatorDiffusionApplication extends PhabricatorApplication {
 | 
			
		||||
        // catch-all for serving repositories over HTTP. We must accept
 | 
			
		||||
        // requests without the trailing "/" because SVN commands don't
 | 
			
		||||
        // necessarily include it.
 | 
			
		||||
        '(?P<repositoryCallsign>[A-Z]+)(?:/.*)?' =>
 | 
			
		||||
          'DiffusionRepositoryDefaultController',
 | 
			
		||||
        '(?:(?P<repositoryCallsign>[A-Z]+)|(?P<repositoryID>[1-9]\d*))'.
 | 
			
		||||
          '(?:/.*)?'
 | 
			
		||||
          => 'DiffusionRepositoryDefaultController',
 | 
			
		||||
 | 
			
		||||
        'inline/' => array(
 | 
			
		||||
          'edit/(?P<phid>[^/]+)/' => 'DiffusionInlineCommentController',
 | 
			
		||||
 
 | 
			
		||||
@@ -64,6 +64,20 @@ abstract class DiffusionController extends PhabricatorController {
 | 
			
		||||
      return new Aphront404Response();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // If the client is making a request like "/diffusion/1/...", but the
 | 
			
		||||
    // repository has a different canonical path like "/diffusion/XYZ/...",
 | 
			
		||||
    // redirect them to the canonical path.
 | 
			
		||||
 | 
			
		||||
    $request_path = $request->getPath();
 | 
			
		||||
    $repository = $drequest->getRepository();
 | 
			
		||||
 | 
			
		||||
    $canonical_path = $repository->getCanonicalPath($request_path);
 | 
			
		||||
    if ($canonical_path !== null) {
 | 
			
		||||
      if ($canonical_path != $request_path) {
 | 
			
		||||
        return id(new AphrontRedirectResponse())->setURI($canonical_path);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $this->diffusionRequest = $drequest;
 | 
			
		||||
 | 
			
		||||
    return null;
 | 
			
		||||
 
 | 
			
		||||
@@ -687,6 +687,55 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO
 | 
			
		||||
    return "/r{$callsign}{$identifier}";
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public function getCanonicalPath($request_path) {
 | 
			
		||||
    $standard_pattern =
 | 
			
		||||
      '(^'.
 | 
			
		||||
        '(?P<prefix>/diffusion/)'.
 | 
			
		||||
        '(?P<identifier>[^/]+)'.
 | 
			
		||||
        '(?P<suffix>(?:/.*)?)'.
 | 
			
		||||
      '\z)';
 | 
			
		||||
 | 
			
		||||
    $matches = null;
 | 
			
		||||
    if (preg_match($standard_pattern, $request_path, $matches)) {
 | 
			
		||||
      $prefix = $matches['prefix'];
 | 
			
		||||
 | 
			
		||||
      $callsign = $this->getCallsign();
 | 
			
		||||
      if ($callsign) {
 | 
			
		||||
        $identifier = $callsign;
 | 
			
		||||
      } else {
 | 
			
		||||
        $identifier = $this->getID();
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      $suffix = $matches['suffix'];
 | 
			
		||||
      if (!strlen($suffix)) {
 | 
			
		||||
        $suffix = '/';
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      return $prefix.$identifier.$suffix;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $commit_pattern =
 | 
			
		||||
      '(^'.
 | 
			
		||||
        '(?P<prefix>/)'.
 | 
			
		||||
        '(?P<monogram>'.
 | 
			
		||||
          '(?:'.
 | 
			
		||||
            'r(?P<repositoryCallsign>[A-Z]+)'.
 | 
			
		||||
            '|'.
 | 
			
		||||
            'R(?P<repositoryID>[1-9]\d*):'.
 | 
			
		||||
          ')'.
 | 
			
		||||
          '(?P<commit>[a-f0-9]+)'.
 | 
			
		||||
        ')'.
 | 
			
		||||
      '\z)';
 | 
			
		||||
 | 
			
		||||
    $matches = null;
 | 
			
		||||
    if (preg_match($commit_pattern, $request_path, $matches)) {
 | 
			
		||||
      $commit = $matches['commit'];
 | 
			
		||||
      return $this->getCommitURI($commit);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return null;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public function generateURI(array $params) {
 | 
			
		||||
    $req_branch = false;
 | 
			
		||||
    $req_commit = false;
 | 
			
		||||
 
 | 
			
		||||
@@ -9,6 +9,47 @@ final class PhabricatorRepositoryURITestCase
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public function testRepositoryURICanonicalization() {
 | 
			
		||||
    $repo = id(new PhabricatorRepository())
 | 
			
		||||
      ->makeEphemeral()
 | 
			
		||||
      ->setID(123);
 | 
			
		||||
 | 
			
		||||
    $tests = array(
 | 
			
		||||
      '/diffusion/123' => '/diffusion/123/',
 | 
			
		||||
      '/diffusion/123/' => '/diffusion/123/',
 | 
			
		||||
      '/diffusion/123/browse/master/' => '/diffusion/123/browse/master/',
 | 
			
		||||
      '/kangaroo/' => null,
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    foreach ($tests as $input => $expect) {
 | 
			
		||||
      $this->assertEqual(
 | 
			
		||||
        $expect,
 | 
			
		||||
        $repo->getCanonicalPath($input),
 | 
			
		||||
        pht('Canonical Path (ID, No Callsign): %s', $input));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $repo->setCallsign('XYZ');
 | 
			
		||||
 | 
			
		||||
    $tests = array(
 | 
			
		||||
      '/diffusion/123' => '/diffusion/XYZ/',
 | 
			
		||||
      '/diffusion/123/' => '/diffusion/XYZ/',
 | 
			
		||||
      '/diffusion/123/browse/master/' => '/diffusion/XYZ/browse/master/',
 | 
			
		||||
      '/diffusion/XYZ' => '/diffusion/XYZ/',
 | 
			
		||||
      '/diffusion/XYZ/' => '/diffusion/XYZ/',
 | 
			
		||||
      '/diffusion/XYZ/browse/master/' => '/diffusion/XYZ/browse/master/',
 | 
			
		||||
      '/diffusion/ABC/' => '/diffusion/XYZ/',
 | 
			
		||||
      '/kangaroo/' => null,
 | 
			
		||||
      '/R1:abcdef' => '/rXYZabcdef',
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    foreach ($tests as $input => $expect) {
 | 
			
		||||
      $this->assertEqual(
 | 
			
		||||
        $expect,
 | 
			
		||||
        $repo->getCanonicalPath($input),
 | 
			
		||||
        pht('Canonical Path (ID, Callsign): %s', $input));
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public function testURIGeneration() {
 | 
			
		||||
    $svn = PhabricatorRepositoryType::REPOSITORY_TYPE_SVN;
 | 
			
		||||
    $git = PhabricatorRepositoryType::REPOSITORY_TYPE_GIT;
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user