Current section
Files
Jump to
Current section
Files
lib/opencadc_vospace_client.ex
defmodule OpencadcVospaceClient do
@moduledoc """
VOSpace client to upload a stream of data. Useful for transferring from one location to a VOSpace.
"""
@doc """
Upload a file to a VOSpace
`sourceStream`: The source of data to PUT to VOSpace.
`destinationURI`: VOS URI to put to.
`cookieCredential`: Cookie value for auth.
"""
def upload(sourceStream, destinationURI, cookieCredential) do
OpencadcRegistryClient.lookupServiceURL("ivo://cadc.nrc.ca/vospace", "ivo://ivoa.net/std/VOSpace/v2.0#nodes", "vs:ParamHTTP") <> destinationURI
|> HTTPoison.put!(
toXML(destinationURI),
[{"Content-Type", "text/xml"}, {"Cookie", "CADC_SSO=\"" <> cookieCredential <> "\""}],
[{:follow_redirect, true}]
)
|> write(sourceStream, destinationURI, cookieCredential)
end
defp write(%HTTPoison.Response{status_code: 200}, sourceStream, destinationURI, cookieCredential) do
transferServiceURL = OpencadcRegistryClient.lookupServiceURL("ivo://cadc.nrc.ca/vospace", "ivo://ivoa.net/std/VOSpace/v2.0#transfers", "vs:ParamHTTP")
OpencadcRegistryClient.lookupServiceURL("ivo://cadc.nrc.ca/vospace", "ivo://ivoa.net/std/VOSpace#sync-2.1", "vs:ParamHTTP")
|> HTTPoison.post!(
toXML(destinationURI, transferServiceURL),
[{"Content-Type", "text/xml"}, {"Cookie", "CADC_SSO=\"" <> cookieCredential <> "\""}]
)
|> transfer(sourceStream, destinationURI, cookieCredential)
end
defp write(%HTTPoison.Response{status_code: 401}, _, destinationURI, _) do
raise ArgumentError, message: "Write permission denied on " <> destinationURI
end
defp write(%HTTPoison.Response{status_code: 404}, _, destinationURI, _) do
raise ArgumentError, message: "No such target " <> destinationURI
end
defp transfer(%HTTPoison.Response{status_code: 303} = response, sourceStream, _, cookieCredential) do
response.headers
|> get_header("Location")
|> HTTPoison.get!(
[{"Cookie", "CADC_SSO=\"" <> cookieCredential <> "\""}]
)
|> upload_to(sourceStream, cookieCredential)
end
defp transfer(response, _, destinationURI, _) do
raise ArgumentError, message: "Unable to transfer to " <> destinationURI <> "\n" <> response.body
end
defp upload_to(%HTTPoison.Response{status_code: 303} = response, sourceStream, cookieCredential) do
response.headers
|> get_header("Location")
|> HTTPoison.get!(
[{"Cookie", "CADC_SSO=\"" <> cookieCredential <> "\""}]
)
|> upload_to(sourceStream, cookieCredential)
end
defp upload_to(%HTTPoison.Response{status_code: 200} = response, sourceStream, cookieCredential) do
Parser.all(Parser.from_string(response.body), "//vos:transfer/vos:protocol[@uri=\"ivo://ivoa.net/vospace/core#httpput\"]/vos:endpoint")
|> extractTransferEndpointURL
|> HTTPoison.put!({:stream, sourceStream}, [{"Cookie", "CADC_SSO=\"" <> cookieCredential <> "\""}])
end
defp upload_to(response, _, _) do
raise ArgumentError, message: response.body
end
defp extractTransferEndpointURL([]) do
raise ArgumentError, message: "No service endpoints found to transfer to."
end
defp extractTransferEndpointURL(endpoints) do
endpoints |> Enum.at(0) |> Parser.text
end
defp toXML(destinationURI) do
"<vos:node uri=\"" <> destinationURI <> "\" busy=\"false\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"vos:DataNode\" xmlns:vos=\"http://www.ivoa.net/xml/VOSpace/v2.1\"><vos:properties/></vos:node>"
end
defp toXML(destinationURI, transferURL) do
"<vos:transfer xmlns:vos=\"http://www.ivoa.net/xml/VOSpace/v2.1\"><vos:target>" <> destinationURI <> "</vos:target><vos:direction>pushToVoSpace</vos:direction><vos:view uri=\"ivo://ivoa.net/vospace/core#defaultview\"/><vos:protocol uri=\"ivo://ivoa.net/vospace/core#httpput\"><vos:endpoint>" <> transferURL <> "</vos:endpoint></vos:protocol><vos:keepBytes>true</vos:keepBytes></vos:transfer>"
end
defp get_header(headers, key) do
headers
|> Enum.filter(fn({k, _}) -> k == key end)
|> hd
|> elem(1)
end
end