-
Notifications
You must be signed in to change notification settings - Fork 2
PFDR-252 v2 deposit scenario #88
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
e716d81
5e4a25e
c08a156
1c1adb6
a2cb7d1
50c415e
c19e7b7
7a2dc5a
45d71bc
8eb271f
64d1114
61bbef1
8fc0c6f
ca76538
535b3b8
8830316
9d49cb4
a45e127
b21fad2
723a7b7
6e4d312
58b0479
386faea
28b645e
9cd6d46
91712af
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| class DepositStatusType < ActiveRecord::Type::Value | ||
| def cast(value) | ||
| super(value.to_s) | ||
| end | ||
|
|
||
| def deserialize(value) | ||
| Chipmunk::DepositStatus.new(value) | ||
| end | ||
|
|
||
| def serialize(deposit_status) | ||
| deposit_status.to_s | ||
| end | ||
|
|
||
| end |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| module V2 | ||
| class ArtifactsController < ResourceController | ||
|
|
||
| collection_policy ArtifactsPolicy | ||
|
|
||
| def self.of_any_type | ||
| AnyArtifact.new | ||
| end | ||
|
|
||
| def show | ||
| @artifact = Artifact.find(params[:id]) | ||
| render json: @artifact, status: 200 | ||
| end | ||
|
|
||
| def create | ||
| collection_policy.new(current_user).authorize! :new? | ||
| # We don't explicitly check for :save? permissions | ||
|
|
||
| if (duplicate = Artifact.find_by(id: params[:id])) | ||
| resource_policy.new(current_user, duplicate).authorize! :show? | ||
| head 303, location: v2_artifact_path(duplicate) | ||
| else | ||
| @artifact = new_artifact(params) | ||
| if @artifact.valid? | ||
| @artifact.save! | ||
| render json: @artifact, status: 201, location: v2_artifact_path(@artifact) | ||
| else | ||
| render json: @artifact.errors, status: :unprocessable_entity | ||
| end | ||
| end | ||
| end | ||
|
|
||
| private | ||
|
|
||
| def new_artifact(params) | ||
| Artifact.new( | ||
| id: params[:id], | ||
| user: current_user, | ||
| storage_format: params[:storage_format], | ||
| content_type: params[:content_type] | ||
| ) | ||
| end | ||
|
|
||
| end | ||
| end | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| module V2 | ||
| class DepositsController < ResourceController | ||
|
|
||
| def create | ||
| # TODO policy check | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Blocker |
||
| @deposit = Deposit.create!( | ||
| artifact: Artifact.find(params[:artifact_id]), | ||
| user: current_user, | ||
| status: :started | ||
| ) | ||
| # TODO: don't pick a specific template | ||
| render "v2/deposits/show", status: 201, location: v2_deposit_path(@deposit) | ||
| end | ||
|
|
||
| def ready | ||
| # TODO policy check | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Blocker |
||
| @deposit = Deposit.find(params[:id]) | ||
| case @deposit.status | ||
| when Deposit.statuses[:started] | ||
| @deposit.update!(status: Deposit.statuses[:ingesting]) | ||
| FinishDepositJob.perform_later(@deposit) | ||
| render json: @deposit, status: 200 | ||
| when Deposit.statuses[:ingesting] | ||
| render json: @deposit, status: 200 | ||
| else # :completed, :failed, :cancelled | ||
| head 422 # TODO think about this response | ||
| end | ||
| end | ||
|
|
||
| end | ||
| end | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| module V2 | ||
| class RevisionsController < ResourceController | ||
|
|
||
| end | ||
| end |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| class FinishDepositJob < ApplicationJob | ||
| def perform(deposit) | ||
| Chipmunk::FinishDeposit.new(deposit).run | ||
| end | ||
| end |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,53 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| class Artifact < ApplicationRecord | ||
|
|
||
| class AnyArtifact | ||
| def to_resources | ||
| Artifact.content_types.map {|t| Checkpoint::Resource::AllOfType.new(t) } | ||
| end | ||
|
|
||
| def resource_type | ||
| "Artifact" | ||
| end | ||
|
|
||
| def resource_id | ||
| Checkpoint::Resource::ALL | ||
| end | ||
| end | ||
|
|
||
| def self.resource_types | ||
| content_types | ||
| end | ||
|
|
||
| def self.content_types | ||
| Rails.application.config.validation["bagger_profile"].keys + | ||
| Rails.application.config.validation["external"].keys | ||
| end | ||
|
|
||
| def self.of_any_type | ||
| AnyArtifact.new | ||
| end | ||
|
|
||
| alias_method :identifier, :id | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's talk about this identifier business... remind me of the intent behind an alias?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wanted to be able to treat the artifact the same as our v1 objects, so there was some mismatch between id, uuid. |
||
|
|
||
| # Each artifact belongs to a single user | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd like to follow convention and have all of the relationships and validations at the very top of the class. |
||
| belongs_to :user | ||
| # Deposits are collections of zero or more revisions | ||
| has_many :revisions | ||
| # Revisions are added to artifacts via deposits | ||
| has_many :deposits | ||
|
|
||
| validates :id, presence: true, | ||
| format: { with: Services.uuid_format, | ||
| message: "must be a valid v4 uuid." } | ||
|
|
||
| validates :user, presence: true | ||
| validates :storage_format, presence: true # TODO this is a controlled vocabulary | ||
| validates :content_type, presence: true # TODO this is a controlled vocabulary | ||
|
|
||
| def stored? | ||
| revisions.any? | ||
| end | ||
|
|
||
| end | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| class Deposit < ApplicationRecord | ||
|
|
||
| # Each deposit is created by a single user | ||
| belongs_to :user | ||
| # A deposit is an attempt to append a revision to a single artifact | ||
| belongs_to :artifact | ||
|
|
||
| # TODO Could not get the attributes api to work with a custom type | ||
| enum status: { | ||
| started: "started", | ||
| canceled: "cancelled", | ||
| ingesting: "ingesting", | ||
| failed: "failed", | ||
| completed: "completed" | ||
| } | ||
|
|
||
| validates :user, presence: true | ||
| validates :artifact, presence: true | ||
| validates :status, presence: true # TODO this is a controlled vocabulary | ||
|
|
||
| def identifier | ||
| id.to_s | ||
| end | ||
|
|
||
| def username | ||
| user.username | ||
| end | ||
|
|
||
| def content_type | ||
| artifact.content_type | ||
| end | ||
|
|
||
| def storage_format | ||
| artifact.storage_format | ||
| end | ||
|
|
||
| def complete! | ||
| update!(status: "completed") | ||
| end | ||
|
|
||
| def fail!(errors) | ||
| update!(status: "failed", error: errors.join("\n")) | ||
| end | ||
|
|
||
| def upload_link | ||
| Services.incoming_storage.upload_link(self) | ||
| end | ||
|
|
||
| end |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| class Revision < ApplicationRecord | ||
| belongs_to :artifact | ||
| belongs_to :deposit | ||
| end |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| class ArtifactsPolicy < CollectionPolicy | ||
| def index? | ||
| can?(:show, Artifact.of_any_type) | ||
| end | ||
|
|
||
| def new? | ||
| can?(:save, Artifact.of_any_type) | ||
| end | ||
|
|
||
| def base_scope | ||
| Artifact.all | ||
| end | ||
|
|
||
| def resolve | ||
| # Via the role map resolver, a user has access to: | ||
| # * All artifacts, if the user is an administrator | ||
| # * Artifacts of content types for which the user is a content manager | ||
| # * Artifacts of content types for which the user is authorized viewer | ||
| # * Any specific artifacts for which the user is granted access | ||
| ViewableResources.for(user, scope) | ||
| end | ||
| end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm unclear whether this is descriptive or declarative. Is this a problem report or a design decision?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's been a while, but the
savecheck and thenewcheck seemed be identical sets.