pho 0.0.1 → 0.1

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile CHANGED
@@ -5,12 +5,12 @@ require 'rake/testtask'
5
5
  require 'rake/clean'
6
6
 
7
7
  NAME = "pho"
8
- VER = "0.0.1"
8
+ VER = "0.1"
9
9
 
10
10
  RDOC_OPTS = ['--quiet', '--title', 'Pho (Talis Platform Client) Reference', '--main', 'README']
11
11
 
12
12
  PKG_FILES = %w( README Rakefile ) +
13
- Dir.glob("{bin,doc,tests,lib}/**/*")
13
+ Dir.glob("{bin,doc,tests,examples,lib}/**/*")
14
14
 
15
15
  CLEAN.include ['*.gem', 'pkg']
16
16
  SPEC =
@@ -0,0 +1,30 @@
1
+ require 'rubygems'
2
+ require 'pho'
3
+
4
+ # Create the store object
5
+ store = Pho::Store.new("http://api.talis.com/stores/ldodds-dev1", "ldodds", "XXXXXXX")
6
+
7
+ # Using StringIO object here, but could more usually provide a File object
8
+ data = StringIO.new("Some data to store")
9
+
10
+ puts "Uploading item..."
11
+ resp = store.upload_item(data, "text/plain", "/items/test.txt")
12
+
13
+ puts "Status code after storage is: #{resp.status}"
14
+
15
+ puts "Retrieving data..."
16
+ resp = store.get_item("http://api.talis.com/stores/ldodds-dev1/items/test.txt")
17
+
18
+ puts "Status code after retrieval is #{resp.status}"
19
+ # Should output "Some data to store"
20
+ puts "Retrieved data is: #{resp.content}"
21
+
22
+ puts "Deleting item..."
23
+ resp = store.delete_item("/items/test.txt")
24
+
25
+ puts "Status code after deletion is #{resp.status}"
26
+
27
+ puts "Retrieving data..."
28
+ resp = store.get_item("http://api.talis.com/stores/ldodds-dev1/items/test.txt")
29
+
30
+ puts "Status code after deletion is: #{resp.status}"
@@ -0,0 +1,84 @@
1
+ require 'rubygems'
2
+ require 'json'
3
+ require 'pho'
4
+
5
+ # Use the demonstration store containing NASA space flight data
6
+ store = Pho::Store.new("http://api.talis.com/stores/space")
7
+
8
+ #Retrieve simple RDF description for this resource (Apollo 11 Launch) as RDF/XML
9
+ puts "Describe Apollo 11 Launch"
10
+ response = store.describe("http://purl.org/net/schemas/space/launch/1969-059")
11
+
12
+ # Dump to console
13
+ puts response.content
14
+
15
+ # SPARQL Query
16
+ SPARQL = <<-EOL
17
+ PREFIX space: <http://purl.org/net/schemas/space/>
18
+ PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
19
+
20
+
21
+ DESCRIBE ?spacecraft WHERE {
22
+
23
+ ?launch space:launched "1969-07-16"^^xsd:date.
24
+
25
+ ?spacecraft space:launch ?launch.
26
+
27
+ }
28
+
29
+ EOL
30
+ puts "Describe spacecraft launched on 16th July 1969"
31
+ response = store.sparql_describe(SPARQL)
32
+ puts response.content
33
+
34
+ SPARQL_CONSTRUCT = <<-EOL
35
+
36
+ PREFIX space: <http://purl.org/net/schemas/space/>
37
+ PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
38
+ PREFIX foaf: <http://xmlns.com/foaf/0.1/>
39
+
40
+ CONSTRUCT {
41
+ ?spacecraft foaf:name ?name;
42
+ space:agency ?agency;
43
+ space:mass ?mass.
44
+ }
45
+ WHERE {
46
+ ?launch space:launched "1969-07-16"^^xsd:date.
47
+
48
+ ?spacecraft space:launch ?launch;
49
+ foaf:name ?name;
50
+ space:agency ?agency;
51
+ space:mass ?mass.
52
+ }
53
+
54
+ EOL
55
+
56
+ puts "Get name, agency and mass for spacecraft launched on 16th July 1969"
57
+ response = store.sparql_construct(SPARQL_CONSTRUCT)
58
+ puts response.content
59
+
60
+ SPARQL_SELECT = <<-EOL
61
+ PREFIX space: <http://purl.org/net/schemas/space/>
62
+ PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
63
+ PREFIX foaf: <http://xmlns.com/foaf/0.1/>
64
+
65
+ SELECT ?name
66
+ WHERE {
67
+
68
+ ?launch space:launched "1969-07-16"^^xsd:date.
69
+
70
+ ?spacecraft space:launch ?launch;
71
+ foaf:name ?name.
72
+ }
73
+
74
+ EOL
75
+
76
+ puts "Get name of spacecraft launched on 16th July 1969, as JSON"
77
+ response = store.sparql_construct(SPARQL_SELECT, "application/sparql-results+json")
78
+ json = JSON.parse( response.content )
79
+
80
+ json["results"]["bindings"].each do |b|
81
+
82
+ puts b["name"]["value"]
83
+
84
+ end
@@ -0,0 +1,9 @@
1
+ require 'rubygems'
2
+ require 'pho'
3
+
4
+ # Create the store object
5
+ store = Pho::Store.new("http://api.talis.com/stores/space")
6
+ # Retrieve the store status as a simple hash
7
+ status = store.status
8
+ # Dump the hash to the console
9
+ puts status.inspect
data/lib/pho.rb CHANGED
@@ -4,6 +4,7 @@ require 'json'
4
4
 
5
5
  require 'pho/etags'
6
6
  require 'pho/store'
7
+ require 'pho/snapshot'
7
8
  require 'pho/rdf_collection'
8
9
 
9
10
  module Pho
@@ -16,6 +17,9 @@ module Pho
16
17
  JOB_RESET = "http://schemas.talis.com/2006/bigfoot/configuration#ResetDataJob".freeze
17
18
  JOB_SNAPSHOT = "http://schemas.talis.com/2006/bigfoot/configuration#SnapshotJob".freeze
18
19
  JOB_REINDEX = "http://schemas.talis.com/2006/bigfoot/configuration#ReindexJob".freeze
20
+ JOB_RESTORE = "http://schemas.talis.com/2006/bigfoot/configuration#RestoreJob".freeze
19
21
 
20
22
 
23
+ NAMESPACE_CONFIG = "http://schemas.talis.com/2006/bigfoot/configuration#"
24
+
21
25
  end
@@ -0,0 +1,17 @@
1
+ module Pho
2
+
3
+ # Captures information about a Platform Job
4
+ class Job
5
+
6
+
7
+ # Parse out an array of Job objects from a response returned from the Platform
8
+ def Job.parse_jobs(resp)
9
+ if resp.status != 200
10
+ throw
11
+ end
12
+ return nil
13
+ end
14
+
15
+ end
16
+
17
+ end
@@ -28,6 +28,7 @@ module Pho
28
28
  files_to_store.each do |filename|
29
29
  file = File.new(filename)
30
30
  response = @store.store_file(file)
31
+ #TODO error checking
31
32
  end
32
33
  end
33
34
 
@@ -0,0 +1,95 @@
1
+ module Pho
2
+
3
+ require 'digest/md5'
4
+
5
+ #In the Talis Platform a "snapshot" is an backup of the contents of a Store
6
+ #
7
+ #A snapshot consists of a tar file that contains all data (contentbox and metabox)
8
+ #in the store. The snapshot also contains the store configuration but this is encrypted
9
+ #and not intended for reuse.
10
+ #
11
+ #A snapshot can be generated (or scheduled) using the Store.snapshot method.
12
+ #
13
+ class Snapshot
14
+
15
+ #The URL from which the snapshot can be retrieved
16
+ attr_reader :url
17
+
18
+ #The URL from which the MD5 for the snapshot can be retrieved
19
+ attr_reader :md5_url
20
+
21
+ #Size of the snapshot
22
+ attr_reader :size
23
+
24
+ #Units for Size, e.g. KB
25
+ attr_reader :units
26
+
27
+ #Class method to parse the HTML response from API, e.g. as produced by the Store.get_snapshots method
28
+ #and create a new Snapshot object. At the moment the Platform only supports single snapshot.
29
+ #
30
+ #If the response was an error, then an exception will be thrown
31
+ #
32
+ # resp:: HTTP response generated by the Store.get_snapshots method
33
+ def Snapshot.parse(resp)
34
+ if (resp.status > 200)
35
+ raise "Response was not successful. Status code was: #{resp.status}"
36
+ end
37
+ content = resp.content
38
+
39
+ snapshot_url = content.match("<a href=\"(http://api.talis.com/stores/\.+/snapshots/\.+tar)\">\.+</a>")[1]
40
+
41
+ metadata = Regexp.new("<a href=\"(http://api.talis.com/stores/\.+/snapshots/\.+tar\.md5)\">\.+</a>\n\s+\-\s+([0-9]+) ([a-zA-Z]+)", "m")
42
+ snapshot_md5_url = metadata.match(content)[1]
43
+ snapshot_size = metadata.match(content)[2]
44
+ snapshot_units = metadata.match(content)[3]
45
+
46
+ return Snapshot.new(snapshot_url, snapshot_md5_url, snapshot_size, snapshot_units)
47
+
48
+ end
49
+
50
+ def initialize(url, md5_url, size, units)
51
+ @url = url
52
+ @md5_url = md5_url
53
+ @size = size
54
+ @units = units
55
+ end
56
+
57
+
58
+ #Read the published MD5 value
59
+ def read_md5(client=HttpClient.new())
60
+ return client.get_content(@md5_url)
61
+ end
62
+
63
+ # Download this snapshot to the specified directory. Will automatically calculate an MD5 checksum for
64
+ # the download and compare it against the published value. If they don't match then a RuntimeError will
65
+ # be raised
66
+ #
67
+ # dir:: directory in which snapshot will be stored
68
+ # client:: specify a preconfigured client, e.g. to configure proxy servers, etc
69
+ def download(dir, client=HTTPClient.new())
70
+
71
+ published_md5 = read_md5(client)
72
+
73
+ digest = Digest::MD5.new()
74
+
75
+ filename = @url.split("/").last
76
+ file = File.open( File.join(dir, filename), "w" ) do |file|
77
+
78
+ #FIXME: this is not efficient as the snapshot may be very large and this
79
+ #will just read all of the data into memory
80
+ content = client.get_content(@url)
81
+ puts content
82
+ file.print(content)
83
+ digest << content
84
+
85
+ end
86
+
87
+ calc_md5 = digest.hexdigest
88
+ if (calc_md5 != published_md5)
89
+ raise "Calculated digest of #{calc_md5} but does not match published md5 #{published_md5}"
90
+ end
91
+ end
92
+
93
+ end
94
+
95
+ end
@@ -4,12 +4,19 @@ module Pho
4
4
  #
5
5
  # Changesets
6
6
  # Multisparql
7
- #
7
+ #
8
+ # Listing jobs
9
+ # Retrieving single job
10
+ # FP Map
11
+ # Query Profile
12
+ # List snapshots
13
+ # Get snapshot
14
+ #
15
+ #
8
16
  # Conditional deletions
9
17
  # If-Modified-Since support
10
18
  # Robustness in uri fetching
11
- #
12
- # RDOC
19
+
13
20
 
14
21
  # The Store class acts as a lightweight client interface to the Talis Platform API
15
22
  # (http://n2.talis.com/wiki/Platform_API). The class provides methods for interacting
@@ -25,7 +32,8 @@ module Pho
25
32
  # store.reset
26
33
  #
27
34
  # == Examples
28
- #
35
+ #
36
+ # See the examples directory in the distribution
29
37
  class Store
30
38
 
31
39
  #Retrieve the HTTPClient instance being used by this object
@@ -330,7 +338,7 @@ module Pho
330
338
  #############
331
339
  # JOBS
332
340
  #############
333
-
341
+
334
342
  # Construct an RDF/XML document containing a job request for submitting to the Platform.
335
343
  #
336
344
  # t:: a Time object, specifying the time at which the request should be carried out
@@ -362,6 +370,27 @@ module Pho
362
370
  return submit_job(JOB_SNAPSHOT, "Snapshot my store", t)
363
371
  end
364
372
 
373
+ #Restore this store from a previously generated Snapshot
374
+ #The Platform can restore from any snapshot that is web-accessible
375
+ #
376
+ #snapshot_url:: the URL of the snapshot
377
+ def restore(snapshot_url, t=Time.now)
378
+ time = t.strftime("%Y-%m-%dT%H:%M:%SZ")
379
+ data = "<rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\" "
380
+ data << " xmlns:rdfs=\"http://www.w3.org/2000/01/rdf-schema#\" "
381
+ data << " xmlns:bf=\"http://schemas.talis.com/2006/bigfoot/configuration#\"> "
382
+ data << " <bf:JobRequest>"
383
+ data << " <rdfs:label>Restore my store</rdfs:label>"
384
+ data << " <bf:jobType rdf:resource=\"#{Pho::JOB_RESTORE}\"/>"
385
+ data << " <bf:snapshotUri rdf:resource=\"#{snapshot_url}\"/>"
386
+ data << " <bf:startTime>#{time}</bf:startTime>"
387
+ data << " </bf:JobRequest>"
388
+ data << "</rdf:RDF>"
389
+ u = build_uri("/jobs")
390
+ response = @client.post(u, data, RDF_XML )
391
+ return response
392
+ end
393
+
365
394
  def submit_job(joburi, label, t=Time.now)
366
395
  u = build_uri("/jobs")
367
396
  data = build_job_request(t, joburi, label)
@@ -383,7 +412,17 @@ module Pho
383
412
  state["accessMode"] = json[u.to_s]["http:\/\/schemas.talis.com\/2006\/bigfoot\/configuration#accessMode"][0]["value"]
384
413
  return state
385
414
  end
386
-
415
+
416
+ # Retrieve the list of snapshots for this store
417
+ #
418
+ # Currently the response will contain an HTML document. Use Snapshot.parse to turn this into
419
+ # a Snapshot object
420
+ def get_snapshots()
421
+ u = build_uri("/snapshots")
422
+ response = @client.get(u, nil, nil)
423
+ return response
424
+ end
425
+
387
426
  end
388
427
 
389
428
  end
@@ -55,5 +55,11 @@ class JobControlTest < Test::Unit::TestCase
55
55
  store = Pho::Store.new("http://api.talis.com/stores/testing", "user", "pass", mc)
56
56
  job_req = store.snapshot()
57
57
  end
58
-
58
+
59
+ def test_restore
60
+ mc = mock()
61
+ set_expectations("http://api.talis.com/stores/testing", mc, Pho::JOB_RESTORE, "Restore my store" )
62
+ store = Pho::Store.new("http://api.talis.com/stores/testing", "user", "pass", mc)
63
+ job_req = store.restore("http://www.example.com.tar")
64
+ end
59
65
  end
@@ -0,0 +1,77 @@
1
+ $:.unshift File.join(File.dirname(__FILE__), "..", "lib")
2
+ require 'pho'
3
+ require 'test/unit'
4
+ require 'mocha'
5
+ require 'tmpdir'
6
+
7
+ class SnapshotsTest < Test::Unit::TestCase
8
+
9
+ def setup()
10
+ @snapshot = <<-EOL
11
+ <?xml version="1.0" encoding="UTF-8"?><html xmlns:bf="http://schemas.talis.com/2006/bigfoot/configuration#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/1999/xhtml" xmlns:dc="http://purl.org/dc/elements/1.1/"><head><meta http-equiv="content-type" content="text/html; charset=utf-8"/><title>Snapshots</title><style type="text/css">
12
+
13
+ </style></head><body><div id="wrap"><div id="content"><div class="gutter"><h1>Snapshots for this store</h1><ul class="xoxo"><li><span class="field-name"><a href="http://api.talis.com/stores/test-store/snapshots/20090222113727.tar">http://api.talis.com/stores/test-store/snapshots/20090222113727.tar</a>
14
+ - <a href="http://api.talis.com/stores/test-store/snapshots/20090222113727.tar.md5">MD5</a>
15
+ - 30 KB
16
+ - 11:37 22-February-2009</span></li></ul></div><ul id="footer"><li id="learn"><a href="http://n2.talis.com/wiki/Platform_API">learn more</a></li></ul></div></div></body></html>
17
+ EOL
18
+
19
+ end
20
+
21
+ def teardown()
22
+
23
+ f = File.join(Dir.tmpdir, "2009022213727.tar")
24
+ if File.exists?(f)
25
+ # File.delete(f)
26
+ end
27
+
28
+ end
29
+
30
+ def test_get_snapshots()
31
+ mc = mock()
32
+ mc.expects(:set_auth)
33
+ mc.expects(:get).with("http://api.talis.com/stores/testing/snapshots", nil, nil)
34
+
35
+ store = Pho::Store.new("http://api.talis.com/stores/testing", "user", "pass", mc)
36
+ response = store.get_snapshots()
37
+ end
38
+
39
+ def test_parse_snapshots()
40
+
41
+ mc = mock()
42
+ mc.expects(:status).returns(200)
43
+ mc.expects(:content).returns(@snapshot)
44
+
45
+ snapshot = Pho::Snapshot.parse(mc)
46
+
47
+ assert_equal(true, snapshot != nil)
48
+ assert_equal("http://api.talis.com/stores/test-store/snapshots/20090222113727.tar", snapshot.url)
49
+ assert_equal("http://api.talis.com/stores/test-store/snapshots/20090222113727.tar.md5", snapshot.md5_url)
50
+ assert_equal("30", snapshot.size)
51
+ assert_equal("KB", snapshot.units)
52
+ end
53
+
54
+ def test_parse_snapshots_raises_exception()
55
+
56
+ mc = mock()
57
+ mc.expects(:status).at_least_once.returns(500)
58
+
59
+ assert_raise RuntimeError do
60
+ Pho::Snapshot.parse(mc)
61
  end
62
+
63
+ end
64
+
65
+ def test_download()
66
+
67
+ mc = mock()
68
+ mc.expects(:get_content).with("http://api.talis.com/stores/test-store/snapshots/20090222113727.tar.md5").returns("4880c0340c65d142838ea33ace9b850a")
69
+ mc.expects(:get_content).with("http://api.talis.com/stores/test-store/snapshots/20090222113727.tar").returns("12345abcdef")
70
+
71
+ snapshot = Pho::Snapshot.new("http://api.talis.com/stores/test-store/snapshots/20090222113727.tar", "http://api.talis.com/stores/test-store/snapshots/20090222113727.tar.md5", "1", "KB")
72
+
73
+ snapshot.download(Dir.tmpdir, mc)
74
+
75
+ assert_equal(true, File.exists?( File.join(Dir.tmpdir, "20090222113727.tar" ) ) )
76
+ end
77
+
78
+ end
@@ -5,6 +5,7 @@ require 'tc_etags.rb'
5
5
  require 'tc_contentbox.rb'
6
6
  require 'tc_metabox.rb'
7
7
  require 'tc_jobcontrol.rb'
8
+ require 'tc_snapshots.rb'
8
9
  require 'tc_search.rb'
9
10
  require 'tc_sparql.rb'
10
11
  require 'tc_rdf_collection.rb'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pho
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: "0.1"
5
5
  platform: ruby
6
6
  authors:
7
7
  - Leigh Dodds
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-02-21 00:00:00 +00:00
12
+ date: 2009-02-23 00:00:00 +00:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -60,14 +60,19 @@ files:
60
60
  - tests/tc_rdf_collection.rb
61
61
  - tests/ts_pho.rb
62
62
  - tests/tc_jobcontrol.rb
63
+ - tests/tc_snapshots.rb
63
64
  - tests/tc_search.rb
64
65
  - tests/tc_etags.rb
65
- - lib/test.rb
66
+ - examples/status.rb
67
+ - examples/sparql.rb
68
+ - examples/contentbox.rb
66
69
  - lib/pho.rb
67
70
  - lib/pho
68
71
  - lib/pho/etags.rb
69
72
  - lib/pho/rdf_collection.rb
70
73
  - lib/pho/store.rb
74
+ - lib/pho/job.rb
75
+ - lib/pho/snapshot.rb
71
76
  has_rdoc: true
72
77
  homepage: http://pho.rubyforge.net
73
78
  post_install_message:
@@ -1,29 +0,0 @@
1
- require 'pho'
2
-
3
- store = Pho::Store.new("http://api.talis.local/stores/space", "ldodds", "gwpjmv7z")
4
-
5
- state = store.status()
6
- puts state.inspect
7
-
8
- resp = store.store_file("/home/ldodds/data/space/nssdc/n3/1969-059A.rdf")
9
- puts resp
10
- puts resp.status
11
-
12
- resp = store.store_file(File.new("/home/ldodds/data/space/nssdc/n3/1969-059B.rdf") )
13
- puts resp.status
14
-
15
- resp = store.store_url("http://api.talis.local/stores/space/meta?about=http://purl.org/net/schemas/space/spacecraft/1969-059A")
16
- puts resp.status
17
-
18
- resp = store.store_url( URI.parse("http://api.talis.local/stores/space/meta?about=http://purl.org/net/schemas/space/spacecraft/1969-059A") )
19
- puts resp.status
20
-
21
- resp = store.describe("http://purl.org/net/schemas/space/spacecraft/1969-059A")
22
- puts resp
23
-
24
- resp = store.describe("http://purl.org/net/schemas/space/spacecraft/1969-059A", "application/json")
25
- puts resp
26
-
27
- store.snapshot
28
- store.reindex
29
- store.reset