Exemplo n.º 1
0
    def commit(self, request, uuid):
        """
        Commit the Draft and create a new BundleVersion that points to it.

        In the future, we may want to separate these two steps so that we can
        create multiple BundleVersions at once in a single transaction, however
        given our modeling conventions of a course being in a single Bundle,
        that's not something that we need to implement immediately.

        We currently return a summary of the things that were created, however
        we may need to rethink this interface if the commit process is going to
        take so long as to require async processing.

        TODO: Test with large Bundles.
        """
        draft_repo = DraftRepo(SnapshotRepo())
        staged_draft = draft_repo.get(uuid)

        # Is this the appropriate response when trying to commit a Draft with
        # no changes?
        if not staged_draft.files_to_overwrite and not staged_draft.links_to_overwrite:
            raise serializers.ValidationError(
                "Draft has no changes to commit.")

        new_snapshot, _updated_draft = draft_repo.commit(staged_draft)
        new_bv = BundleVersion.create_new_version(new_snapshot.bundle_uuid,
                                                  new_snapshot.hash_digest)

        # This is a placeholder response. May need to revisit after trying
        # some large commits.
        result = {
            'bundle_version':
            reverse(
                'api:v1:bundleversion-detail',
                args=[new_snapshot.bundle_uuid, new_bv.version_num],
                request=request,
            ),
            'updated_draft':
            reverse(
                'api:v1:draft-detail',
                args=[uuid],
                request=request,
            )
        }
        return Response(result, status=status.HTTP_201_CREATED)
Exemplo n.º 2
0
    def _parse_links(self, links):
        """
        Parse link information supplied by the user.

        We expect links to come to us in a format that looks like:

        "links": {
            "algebra_problem_bank": {
                "bundle_uuid": "408d549c-2ebf-4bae-9350-d72109a54163",
                "version": 1
            },
            "link_to_delete": None
        }

        Once we have this information, we need to verify that the linked Bundles
        actually exist, and then return a dict of link names to direct
        Dependencies.
        """
        names_to_dependencies = {}
        for name, bv_info in links.items():
            # If bv_info is None, we want to delete this Link (set to None)
            if name and bv_info is None:
                names_to_dependencies[name] = None
                continue

            # Check that our fields exist.
            if 'bundle_uuid' not in bv_info:
                raise ValidationError(
                    "Link {} has no 'bundle_uuid' specified.".format(name)
                )
            if 'version' not in bv_info:
                raise ValidationError(
                    "Link {} has no 'version' specified.".format(name)
                )

            # Check that our field values make sense (proper types).
            if not isinstance(name, str):
                raise ValidationError(
                    "{} is not a valid Link name.".format(name)
                )
            version = bv_info['version']
            # Python's bool is a subclass of int
            if (not isinstance(version, int)) or isinstance(version, bool):
                raise ValidationError(
                    "Link {}: {} must be an integer.".format(name, version)
                )
            try:
                bundle_uuid_str = bv_info['bundle_uuid']
                bundle_uuid = uuid.UUID(bundle_uuid_str)
            except ValueError:
                raise ValidationError(
                    "Link {}: {} is not a valid UUID.".format(name, bundle_uuid_str)
                )

            # At this point it's syntactically correct, but it might be pointing
            # to a BundleVersion that doesn't really exist.
            bundle_version = BundleVersion.get_bundle_version(
                bundle_uuid=bundle_uuid,
                version_num=version,
            )
            if not bundle_version:
                raise ValidationError(
                    (
                        "BundleVersion ({}, {}) referenced in Link {} does not exist."
                        .format(bundle_uuid, version, name)
                    )
                )

            # If everything checks out, create a Dependency. We can't make a
            # Link yet because we don't know the indirect Dependencies (i.e.
            # this Dependency's dependencies).
            names_to_dependencies[name] = Dependency(
                bundle_uuid=bundle_uuid,
                version=version,
                snapshot_digest=bundle_version.snapshot_digest_bytes,
            )

        return names_to_dependencies