Michael Granger avatar Michael Granger committed 07a46ba

Implemented DELETE for the metadata API.

Comments (0)

Files changed (7)

 PUT /v1/«uuid»/metadata/«key» *
 
 # remove all user metadata for an asset
-DELETE /v1/«uuid»/metadata
+DELETE /v1/«uuid»/metadata *
 
 # delete an asset's specific metadata key
-DELETE /v1/«uuid»/metadata/«key»
+DELETE /v1/«uuid»/metadata/«key» *
 
 
 	}
 
 	# Metadata keys which aren't directly modifiable via the REST API
-	PROTECTED_METADATA_KEYS = %w[
+	OPERATIONAL_METADATA_KEYS = %w[
 		format
 		extent
 		created
+		uploadaddress
 	]
 
 
 			uri = base_uri.dup
 			uri.path += uuid
 
-			protected_values = self.metastore.fetch( uuid, *PROTECTED_METADATA_KEYS )
-			metadata = Hash[ [PROTECTED_METADATA_KEYS, protected_values].transpose ]
+			protected_values = self.metastore.fetch( uuid, *OPERATIONAL_METADATA_KEYS )
+			metadata = Hash[ [OPERATIONAL_METADATA_KEYS, protected_values].transpose ]
 			metadata['uri'] = uri.to_s
 
 			metadata
 		uuid = req.params[:uuid]
 		key  = req.params[:key]
 
-		finish_with( HTTP::FORBIDDEN, "Protected metadata." ) if PROTECTED_METADATA_KEYS.include?( key )
+		finish_with( HTTP::FORBIDDEN, "Protected metadata." ) if
+			OPERATIONAL_METADATA_KEYS.include?( key )
 		finish_with( HTTP::NOT_FOUND, "No such object." ) unless self.metastore.include?( uuid )
 		previous_value = self.metastore.fetch( uuid, key )
 
 	end
 
 
+	# DELETE /«uuid»/metadata
+	# Remove all (but operational) metadata associated with «uuid».
+	delete ':uuid/metadata' do |req|
+		uuid = req.params[:uuid]
+
+		finish_with( HTTP::NOT_FOUND, "No such object." ) unless self.metastore.include?( uuid )
+
+		self.metastore.remove_except( uuid, *OPERATIONAL_METADATA_KEYS )
+
+		res = req.response
+		res.status = HTTP::NO_CONTENT
+
+		return res
+	end
+
+
+	# DELETE /«uuid»/metadata
+	# Remove the metadata associated with «key» for the given «uuid».
+	delete ':uuid/metadata/:key' do |req|
+		uuid = req.params[:uuid]
+		key = req.params[:key]
+
+		finish_with( HTTP::NOT_FOUND, "No such object." ) unless self.metastore.include?( uuid )
+		finish_with( HTTP::FORBIDDEN, "Protected metadata." ) if
+			OPERATIONAL_METADATA_KEYS.include?( key )
+
+		self.metastore.remove( uuid, key )
+
+		res = req.response
+		res.status = HTTP::NO_CONTENT
+
+		return res
+	end
+
+
 	#########
 	protected
 	#########
 		new_metadata = req.params.fields.dup
 		new_metadata.delete( :uuid )
 
-		protected_keys = PROTECTED_METADATA_KEYS & new_metadata.keys
+		protected_keys = OPERATIONAL_METADATA_KEYS & new_metadata.keys
 
 		unless protected_keys.empty?
 			finish_with HTTP::FORBIDDEN,

lib/thingfish/metastore.rb

 	             :merge,
 	             :include?,
 	             :remove,
+	             :remove_except,
 				 :size
 
 	### Return a representation of the object as a String suitable for debugging.

lib/thingfish/metastore/memory.rb

 
 
 	### Remove all metadata associated with +oid+ from the Metastore.
-	def remove( oid )
+	def remove( oid, *keys )
 		oid = self.normalize_oid( oid )
-		@storage.delete( oid )
+		if keys.empty?
+			@storage.delete( oid )
+		else
+			keys = keys.map( &:to_s )
+			@storage[ oid ].delete_if {|key, _| keys.include?(key) }
+		end
+	end
+
+
+	### Remove all metadata associated with +oid+ except for the specified +keys+.
+	def remove_except( oid, *keys )
+		oid = self.normalize_oid( oid )
+		keys = keys.map( &:to_s )
+		@storage[ oid ].keep_if {|key,_| keys.include?(key) }
 	end
 
 

spec/thingfish/metastore/memory_spec.rb

 	end
 
 
+	it "can remove a single key/value pair from the metadata for a UUID" do
+		@store.save( TEST_UUID, TEST_METADATA.first )
+		@store.remove( TEST_UUID, :useragent )
+
+		expect( @store.fetch(TEST_UUID, :useragent) ).to be_nil
+	end
+
+
+	it "can truncate metadata not in a list of keys for a UUID" do
+		@store.save( TEST_UUID, TEST_METADATA.first )
+		@store.remove_except( TEST_UUID, :format, :extent )
+
+		metadata = @store.fetch( TEST_UUID )
+		expect( metadata ).to have( 2 ).members
+		expect( metadata.keys ).to include( 'format', 'extent' )
+	end
+
+
 	it "knows if it has data for a given OID" do
 		@store.save( TEST_UUID, TEST_METADATA.first )
 		expect( @store ).to include( TEST_UUID )

spec/thingfish/metastore_spec.rb

 			expect { store.remove(TEST_UUID) }.to raise_error( NotImplementedError, /remove/ )
 		end
 
+		it "raises an error if it doesn't implement #remove_except" do
+			expect { store.remove_except(TEST_UUID, :format) }.
+				to raise_error( NotImplementedError, /remove_except/ )
+		end
+
 		it "raises an error if it doesn't implement #size" do
 			expect { store.size }.to raise_error( NotImplementedError, /size/ )
 		end

spec/thingfish_spec.rb

 		end
 
 
-		it "returns FORBIDDEN when attempting to merge metadata with protected keys" do
+		it "returns FORBIDDEN when attempting to merge metadata with operational keys" do
 			uuid = handler.datastore.save( @png_io )
 			handler.metastore.save( uuid, {
 				'format' => 'image/png',
 		end
 
 
-		it "returns FORBIDDEN when attempting to replace a protected metadata value with a PUT" do
+		it "returns FORBIDDEN when attempting to replace a operational metadata value with a PUT" do
 			uuid = handler.datastore.save( @png_io )
 			handler.metastore.save( uuid, {
 				'format'  => 'image/png',
 			expect( result.body.string ).to match( /protected metadata/i )
 			expect( handler.metastore.fetch(uuid, 'format') ).to eq( 'image/png' )
 		end
+
+
+		it "can remove all non-default metadata with a DELETE" do
+			timestamp = Time.now.getgm
+			uuid = handler.datastore.save( @png_io )
+			handler.metastore.save( uuid, {
+				'format'        => 'image/png',
+				'extent'        => 288,
+				'comment'       => 'nill bill',
+				'useragent'     => 'Inky/2.0',
+				'uploadaddress' => '127.0.0.1',
+				'created'       => timestamp,
+			})
+
+			req = factory.delete( "/#{uuid}/metadata" )
+			result = handler.handle( req )
+
+			expect( result.status ).to eq( HTTP::NO_CONTENT )
+			expect( result.body.string ).to be_empty
+			expect( handler.metastore.fetch(uuid, 'format') ).to eq( 'image/png' )
+			expect( handler.metastore.fetch(uuid, 'extent') ).to eq( 288 )
+			expect( handler.metastore.fetch(uuid, 'uploadaddress') ).to eq( '127.0.0.1' )
+			expect( handler.metastore.fetch(uuid, 'created') ).to eq( timestamp )
+
+			expect( handler.metastore.fetch(uuid, 'comment') ).to be_nil
+			expect( handler.metastore.fetch(uuid, 'useragent') ).to be_nil
+		end
+
+
+		it "can remove a single metadata value with DELETE" do
+			uuid = handler.datastore.save( @png_io )
+			handler.metastore.save( uuid, {
+				'format'  => 'image/png',
+				'comment' => 'nill bill'
+			})
+
+			req = factory.delete( "/#{uuid}/metadata/comment" )
+			result = handler.handle( req )
+
+			expect( result.status ).to eq( HTTP::NO_CONTENT )
+			expect( result.body.string ).to be_empty
+			expect( handler.metastore.fetch(uuid, 'comment') ).to be_nil
+			expect( handler.metastore.fetch(uuid, 'format') ).to eq( 'image/png' )
+		end
+
+
+		it "returns FORBIDDEN when attempting to remove a operational metadata value with a DELETE" do
+			uuid = handler.datastore.save( @png_io )
+			handler.metastore.save( uuid, {
+				'format'  => 'image/png'
+			})
+
+			req = factory.delete( "/#{uuid}/metadata/format" )
+			result = handler.handle( req )
+
+			expect( result.status ).to eq( HTTP::FORBIDDEN )
+			expect( result.body.string ).to match( /protected metadata/i )
+			expect( handler.metastore.fetch(uuid, 'format') ).to eq( 'image/png' )
+		end
 	end
 end
 
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.