def new(self, package_id, format="html", **kwargs):
        """Retrieve the form for uploading a package version.

        If the user isn't logged in, this presents a login screen. If they are
        logged in but don't have admin priviliges or don't own the package, this
        redirects to /packages/.

        This accepts arbitrary keyword arguments to support OAuth.
        """
        is_json = format == "json"
        user = handlers.get_current_user()

        package = handlers.request().maybe_package
        if not user:
            if is_json:
                handlers.http_error(403, "OAuth authentication failed.")
            else:
                raise cherrypy.HTTPRedirect(users.create_login_url(cherrypy.url()))
        elif package and user not in package.uploaders:
            message = "You aren't an uploader for package '%s'." % package.name
            if is_json:
                handlers.http_error(403, message)
            else:
                handlers.flash(message)
                raise cherrypy.HTTPRedirect("/packages/%s" % package.name)
        elif not handlers.is_current_user_admin():
            message = "Currently only admins may create packages."
            if is_json:
                handlers.http_error(403, message)
            else:
                handlers.flash(message)
                raise cherrypy.HTTPRedirect("/packages")
        elif PrivateKey.get() is None:
            if is_json:
                handlers.http_error(500, "No private key set.")
            else:
                raise cherrypy.HTTPRedirect("/admin#tab-private-key")

        id = str(uuid4())
        redirect_url = handlers.request().url(action="create", id=id)
        upload = cloud_storage.Upload(
            "tmp/" + id, acl="project-private", size_range=(0, Package.MAX_SIZE), success_redirect=redirect_url
        )

        # If the package hasn't been validated and moved out of tmp in five
        # minutes, delete it. This could happen if the user uploads the package
        # to cloud storage, but closes the browser before "create" is run.
        deferred.defer(self._remove_tmp_package, id, _countdown=5 * 60)

        if is_json:
            return upload.to_json()

        if package is not None:
            title = "Upload a new version of %s" % package.name
        else:
            title = "Upload a new package"

            return handlers.render(
                "packages/versions/new", form=upload.to_form(), package=package, layout={"title": title}
            )
    def test_user_creates_new_package(self):
        self.be_normal_oauth_user()
        self.post_package_version(name='new-package', version='0.0.1')

        package = Package.get_by_key_name('new-package')
        self.assertIsNotNone(package)
        self.assertEqual(package.name, 'new-package')
        self.assertEqual(package.uploaders, [handlers.get_current_user()])

        version = package.version_set.get()
        self.assertIsNotNone(version)
        self.assertEqual(version.version, SemanticVersion('0.0.1'))
        self.assertEqual(version.package.name, 'new-package')

        version = package.latest_version
        self.assertIsNotNone(version)
        self.assertEqual(version.version, SemanticVersion('0.0.1'))
        self.assertEqual(version.package.name, 'new-package')

        self.assertEqual(package.updated, version.created)
    def test_admin_creates_new_package_with_json(self):
        self.be_admin_oauth_user()
        self.post_package_version_with_json(name="new-package", version="0.0.1")

        package = Package.get_by_key_name("new-package")
        self.assertIsNotNone(package)
        self.assertEqual(package.name, "new-package")
        self.assertEqual(package.uploaders, [handlers.get_current_user()])

        version = package.version_set.get()
        self.assertIsNotNone(version)
        self.assertEqual(version.version, SemanticVersion("0.0.1"))
        self.assertEqual(version.package.name, "new-package")

        version = package.latest_version
        self.assertIsNotNone(version)
        self.assertEqual(version.version, SemanticVersion("0.0.1"))
        self.assertEqual(version.package.name, "new-package")

        self.assertEqual(package.updated, version.created)
    def test_api_user_creates_new_package(self):
        self.be_normal_oauth_user()
        self.post_package_version(name='new-package', version='0.0.1')

        package = Package.get_by_key_name('new-package')
        self.assertIsNotNone(package)
        self.assertEqual(package.name, 'new-package')
        self.assertEqual(package.uploaders, [handlers.get_current_user()])

        version = package.version_set.get()
        self.assertIsNotNone(version)
        self.assertEqual(version.version, SemanticVersion('0.0.1'))
        self.assertEqual(version.package.name, 'new-package')

        version = package.latest_version
        self.assertIsNotNone(version)
        self.assertEqual(version.version, SemanticVersion('0.0.1'))
        self.assertEqual(version.package.name, 'new-package')

        self.assertEqual(package.updated, version.created)
        self.assertEqual('This is a README.', version.readme.text)
    def create(self, package_id, id, format='html', **kwargs):
        """Create a new package version.

        This creates a single package version. It will also create all the
        package metadata if that doesn't already exist. The package archive is
        uploaded to cloud storage separately.

        If the user doesn't own the package or isn't logged in with admin
        privileges, this will return a 403. If the package already has a version
        with this number, or if the version is invalid, this will redirect to
        the new version form.

        Arguments:
          id: The id of the package in cloud storage.
        """

        try:
            is_json = format == 'json'

            route = handlers.request().route
            if 'id' in route: del route['id']

            package = handlers.request().maybe_package
            if package and handlers.get_current_user() not in package.uploaders:
                handlers.request().error(
                    403, "You aren't an uploader for package '%s'." %
                             package.name)
            elif not handlers.is_current_user_admin():
                handlers.request().error(
                    403, "Only admins may create packages.")

            try:
                with closing(cloud_storage.read('tmp/' + id)) as f:
                    version = PackageVersion.from_archive(
                        f, uploader=handlers.get_current_user())
            except (KeyError, files.ExistenceError):
                handlers.request().error(
                    403, "Package upload " + id + " does not exist.")

            if version.package.is_saved():
                if handlers.get_current_user() not in version.package.uploaders:
                    handlers.request().error(
                        403, "You aren't an uploader for package '%s'." %
                                 version.package.name)
                elif version.package.has_version(version.version):
                    message = 'Package "%s" already has version "%s".' % \
                        (version.package.name, version.version)
                    if is_json: handlers.json_error(400, message)

                    handlers.flash(message)
                    url = handlers.request().url(
                        action='new', package_id=version.package.name)
                    raise cherrypy.HTTPRedirect(url)

                if self._should_update_latest_version(version):
                    version.package.latest_version = version
            else:
                version.package.latest_version = version

            cloud_storage.modify_object(version.storage_path,
                                        acl='public-read',
                                        copy_source='tmp/' + id)

            with models.transaction():
                version.package.put()
                version.put()

            deferred.defer(self._compute_version_order, version.package.name)

            message = '%s %s uploaded successfully.' % \
                (version.package.name, version.version)
            if is_json:
                cherrypy.response.headers['Content-Type'] = 'application/json'
                return json.dumps({"success": {"message": message}})

            handlers.flash(message)
            raise cherrypy.HTTPRedirect('/packages/%s' % version.package.name)
        finally:
            cloud_storage.delete_object('tmp/' + id)