Sorting some files.

This commit is contained in:
Philippe PITTOLI 2020-01-02 09:21:11 +01:00
parent d6aa9f35fb
commit f87fd35a64
14 changed files with 740 additions and 299 deletions

View File

@ -16,11 +16,13 @@ dependencies:
git: https://github.com/Lukc/authd
targets:
common-tests:
main: src/tests/common-tests.cr
json-tests:
main: src/json_tests.cr
filestorage:
main: src/main.cr
filestorageclient:
main: src/client.cr
main: src/tests/json_tests.cr
server:
main: src/server/main.cr
client:
main: src/client/main.cr
license: MIT

View File

@ -2,7 +2,11 @@ require "option_parser"
require "ipc"
require "json"
require "./common.cr"
require "base64"
require "../common/filestorage.cr"
alias FM = FileStorage::Message
# TODO
# For now, this example only upload files.
@ -13,7 +17,7 @@ service_name = "filestorage"
files_and_directories_to_transfer = Array(String).new
# This is the requests we will send to the server
requets = Array(Requets).new
requests = Array(FM::Request).new
OptionParser.parse do |parser|
@ -29,6 +33,7 @@ OptionParser.parse do |parser|
parser.on "-h", "--help", "Show this help" do
puts parser
puts "program [OPTIONS] <files-to-upload>"
exit 0
end
end
@ -39,7 +44,8 @@ end
# For now, we only want to upload files, so we create an UploadRequest
#
files_info = Array(FileInfo).new
files_info = Hash(String, FileStorage::FileInfo).new
puts "files and directories to transfer"
files_and_directories_to_transfer.each do |f|
@ -48,7 +54,7 @@ files_and_directories_to_transfer.each do |f|
puts "Directories not supported, for now"
elsif File.file?(f) && File.readable? f
File.open(f) do |file|
files_info << FileInfo.new file
files_info[file.path] = FileStorage::FileInfo.new file
end
else
if ! File.exists? f
@ -61,9 +67,11 @@ files_and_directories_to_transfer.each do |f|
end
end
pp! files_info
files_info.values.each do |file_info|
requests << FM::UploadRequest.new file_info
end
requests << UploadRequest.new files_info
pp! requests
#
# Connection to the service
@ -75,11 +83,9 @@ client = IPC::Client.new service_name
# Sending the authentication message, including files info
#
token = Token.new 1002, "karchnu"
authentication_message = AuthenticationMessage.new token, files_info
client.send(1.to_u8, authentication_message.to_json)
token = FileStorage::Token.new 1002, "karchnu"
authentication_message = FM::Authentication.new token, requests
client.send FileStorage::MessageType::Authentication.to_u8, authentication_message.to_json
#
# Receiving a response
@ -89,29 +95,43 @@ m = client.read
# puts "message received: #{m.to_s}"
# puts "message received payload: #{String.new m.payload}"
response = Response.from_json(String.new m.payload)
response = FM::Response.from_json(String.new m.payload)
if response.mid == authentication_message.mid
puts "This is a response for the authentication message"
else
puts "Message IDs from authentication message and its response differ"
raise "Message IDs from authentication message and its response differ"
end
#
# file transfer
#
puts "transfer"
files_and_directories_to_transfer.each do |f|
puts "- #{f}"
def file_transfer(client : IPC::Client, file : File, file_info : FileStorage::FileInfo)
buffer_size = 1_000
if File.directory? f
# TODO
elsif File.file?(f) && File.readable? f
File.open(f) do |file|
# TODO
# file
end
buffer = Bytes.new buffer_size
counter = 1
size = 0
while (size = file.read(buffer)) > 0
# transfer message = file_info, chunk count, data (will be base64'd)
transfer_message = FM::Transfer.new file_info, counter, buffer[0 ... size]
client.send FileStorage::MessageType::Transfer.to_u8, transfer_message.to_json
counter += 1
buffer = Bytes.new buffer_size
end
end
puts "transfer"
files_info.keys.each do |file_path|
puts "- #{file_path}"
File.open(file_path) do |file|
file_transfer client, file, files_info[file_path]
end
end

View File

@ -1,108 +0,0 @@
require "uuid"
enum MessageType
Error
AuthenticationMessage
Response
Transfer
end
# For now, upload and download are sequentials.
# In a future version, we will be able to send
# arbitrary parts of each file.
class Token
JSON.mapping({
uid: Int32,
login: String
})
def initialize(@uid, @login)
end
end
# Who knows, maybe someday we will be on UDP, too.
#class SHA256
# JSON.mapping({
# chunk: Slice(UInt8)
# })
#end
# A file has a name, a size and tags.
class FileInfo
JSON.mapping({
name: String,
size: UInt64,
# list of SHA256, if we are on UDP
# chunks: Array(SHA256),
tags: Array(String)?
})
# debugging constructor
def initialize(@name, @size, @tags = nil)
# If on UDP
# @chunks = Array(SHA256).new
# arbitrary values here
end
def initialize(file : File, @tags = nil)
@name = File.basename file.path
@size = file.size
end
end
class Request
end
class UploadRequest < Request
property files_to_upload : Array(FileInfo)
def initialize(@files_to_upload)
end
end
# WIP
class DownloadRequest < Request
property names : Array(String)?,
property tags : Array(String)?
def initialize(@names = nil, @tags = nil)
end
end
class AuthenticationMessage
JSON.mapping({
mid: String,
token: Token,
requests: Array(Requests)
})
def initialize(@token, @files, @tags = nil)
@mid = UUID.random.to_s
end
end
class Response
JSON.mapping({
mid: String,
response: String,
reason: String?
})
def initialize(@mid, @response, @reason = nil)
end
end
class TransferMessage
JSON.mapping({
mid: String,
chunk: String,
data: Slice(UInt8)
})
def initialize(@chunk, @data)
@mid = UUID.random.to_s
end
end

219
src/common/filestorage.cr Normal file
View File

@ -0,0 +1,219 @@
require "uuid"
require "openssl"
require "json"
require "base64"
module FileStorage
extend self
# 1 MB read buffer, on-disk
def file_reading_buffer_size
1_000_000
end
# 1 KB message data buffer, on-network
def message_buffer_size
1_000
end
class Exception < ::Exception
end
enum MessageType
Error
Authentication
UploadRequest
DownloadRequest
Response
Responses
Transfer
end
class Chunk
JSON.mapping({
# chunk's number
n: Int32,
# number of chunks
on: Int32,
# digest of the current chunk
digest: String
})
def initialize(@n, @on, data)
@digest = FileStorage.data_digest data.to_slice
end
end
# For now, upload and download are sequentials.
# In a future version, we will be able to send
# arbitrary parts of each file.
class Token
JSON.mapping({
uid: Int32,
login: String
})
def initialize(@uid, @login)
end
end
# Who knows, maybe someday we will be on UDP, too.
#class SHA256
# JSON.mapping({
# chunk: Slice(UInt8)
# })
#end
# A file has a name, a size and tags.
class FileInfo
JSON.mapping({
name: String,
size: UInt64,
nb_chunks: Int32,
# SHA256 file digest
digest: String,
# list of SHA256, if we are on UDP
# chunks: Array(SHA256),
tags: Array(String)?
})
def initialize(file : File, @tags = nil)
@name = File.basename file.path
@size = file.size
@digest = FileStorage.file_digest file
@nb_chunks = (@size / FileStorage.message_buffer_size).ceil.to_i
end
end
class Message
alias Request = UploadRequest | DownloadRequest
class UploadRequest
JSON.mapping({
# autogenerated
mid: String,
file: FileInfo
})
def initialize(@file)
@mid = UUID.random.to_s
end
end
# WIP
class DownloadRequest
JSON.mapping({
# autogenerated
mid: String,
# SHA256 digest of the file, used as ID
uuid: String?,
name: String?,
tags: Array(String)?
})
def initialize(@uuid = nil, @name = nil, @tags = nil)
@mid = UUID.random.to_s
end
end
class Authentication
JSON.mapping({
# autogenerated
mid: String,
token: Token,
requests: Array(Request)
})
def initialize(@token, @requests = Array(Request).new)
@mid = UUID.random.to_s
end
end
class Response
JSON.mapping({
mid: String,
response: String,
reason: String?
})
def initialize(@mid, @response, @reason = nil)
end
end
class Error
JSON.mapping({
mid: String,
# a response for each request
response: String,
reason: String?
})
def initialize(@mid, @response, @reason = nil)
end
end
class Responses
JSON.mapping({
mid: String,
# a response for each request
responses: Array(Response),
response: String,
reason: String?
})
def initialize(@mid, @response, @responses, @reason = nil)
end
end
class Transfer
JSON.mapping({
# autogenerated
mid: String,
# SHA256 digest of the entire file
filedigest: String,
# For now, just the counter in a string
chunk: Chunk,
# base64 slice
data: String,
})
def initialize(file_info : FileInfo, count, bindata)
# count: chunk number
@filedigest = file_info.digest
@data = Base64.encode bindata
@chunk = FileStorage::Chunk.new count, file_info.nb_chunks, @data
@mid = UUID.random.to_s
end
end
end
# private function
def data_digest(data : Bytes)
iodata = IO::Memory.new data, false
buffer = Bytes.new FileStorage.file_reading_buffer_size
io = OpenSSL::DigestIO.new(iodata, "SHA256")
while io.read(buffer) > 0; end
io.digest.hexstring
end
# private function
def file_digest(file : File)
# 1M read buffer
buffer = Bytes.new(1_000_000)
io = OpenSSL::DigestIO.new(file, "SHA256")
while io.read(buffer) > 0 ; end
io.digest.hexstring
end
end

View File

@ -1,17 +0,0 @@
require "json"
require "./common.cr"
files_info = Array(FileInfo).new
files_info << FileInfo.new "file.txt", 4123.to_u64, %w(important truc machin)
token = Token.new 1002, "karchnu"
authentication_message = AuthenticationMessage.new token, files_info
# TODO, TEST, DEBUG, XXX, FIXME
pp! authentication_message.to_json
am_from_json = AuthenticationMessage.from_json authentication_message.to_json
pp! am_from_json

View File

@ -1,146 +0,0 @@
require "option_parser"
require "ipc"
require "json"
require "./colors"
# require "dodb"
require "./common.cr"
storage_directory = "./storage"
service_name = "filestorage"
OptionParser.parse do |parser|
parser.on "-d storage-directory",
"--storage-directory storage-directory",
"The directory where to put uploaded files." do |opt|
storage_directory = opt
end
parser.on "-s service-name", "--service-name service-name", "Service name." do |name|
service_name = name
end
parser.on "-h", "--help", "Show this help" do
puts parser
exit 0
end
end
# keep track of connected users and their requests
# TODO: requests should be handled concurrently
class User
property uid : Int32
property token : Token
property requests : Array(Request)
def initialize(@token)
@uid = token.uid
end
end
# list of connected users
# fd => uid
connected_users = Hash(Int32, Int32).new
users_status = Hash(Int32, User).new
service = IPC::SwitchingService.new service_name
def receiving_files(user : User, event : IPC::Event::Message)
end
# Could be the reception of a file or a file request
def request_handling(user : User, event : IPC::Event::Message)
puts "request handling"
#
# Here we get requests from the message received
#
end
service.loop do |event|
case event
when IPC::Event::Timer
puts "#{CORANGE}IPC::Event::Timer#{CRESET}"
when IPC::Event::Connection
puts "#{CBLUE}IPC::Event::Connection: #{event.connection.fd}#{CRESET}"
when IPC::Event::Disconnection
puts "#{CBLUE}IPC::Event::Disconnection: #{event.connection.fd}#{CRESET}"
connected_users.select! do |fd, uid|
fd != event.connection.fd
end
when IPC::Event::ExtraSocket
puts "#{CRED}IPC::Event::ExtraSocket: should not happen in this service#{CRESET}"
when IPC::Event::Switch
puts "#{CRED}IPC::Event::Switch: should not happen in this service#{CRESET}"
# IPC::Event::Message has to be the last entry
# because ExtraSocket and Switch inherit from Message class
when IPC::Event::Message
puts "#{CBLUE}IPC::Event::Message#{CRESET}: #{event.connection.fd}"
# 1. test if the client is already authenticated
if userid = connected_users[event.connection.fd]?
puts "User is connected: #{user.token.login}"
request_handling users_status[userid], event
else
puts "User is not currently connected"
# The first message sent to the server has to be the AuthenticationMessage.
# Users sent their token (JWT) to authenticate themselves.
# The token contains the user id, its login and a few other parameters.
# (see the authd documentation).
authentication_message =
AuthenticationMessage.from_json(
String.new event.message.payload
)
# Is the user already recorded in users_status?
if users_status[authentication_message.token.uid]?
puts "We already knew the user #{authentication_message.token.uid}"
pp! users_status[authentication_message.token.uid]
else
# AuthenticationMessage includes requests.
new_user =
User.new authentication_message.token,
authentication_message.requests
connected_users[event.connection.fd] = new_user.uid
# record the new user in users_status
users_status[new_user.uid] = new_user
puts "New user is: #{new_user.token.login}"
end
# The user is now connected.
user = users_status[authentication_message.token.uid]
# We verify the user's rights to upload files.
# TODO RIGHTS
# if user wants to upload but not allowed to: Response
# if user wants to get a file but not allowed to: Response
# The user is authorized to upload files.
# TODO: quotas
# Quotas are not defined yet.
# Sending a response.
# The response is "Ok" when the message is well received and authorized.
response = Response.new authentication_message.mid, "Ok"
event.connection.send MessageType::Response.to_u8, response.to_json
end
else
raise "Event type not supported."
end
end

17
src/server/cli.cr Normal file
View File

@ -0,0 +1,17 @@
OptionParser.parse do |parser|
parser.on "-d storage-directory",
"--storage-directory storage-directory",
"The directory where to put uploaded files." do |opt|
Context.storage_directory = opt
end
parser.on "-s service-name", "--service-name service-name", "Service name." do |name|
Context.service_name = name
end
parser.on "-h", "--help", "Show this help" do
puts parser
exit 0
end
end

26
src/server/context.cr Normal file
View File

@ -0,0 +1,26 @@
# keep track of connected users and their requests
# TODO: requests should be handled concurrently
class User
property uid : Int32
property token : FileStorage::Token
property requests : Array(FileStorage::Message::Request)?
def initialize(@token, @requests = nil)
@uid = token.uid
end
end
class Context
class_property service_name = "filestorage"
class_property storage_directory = "./storage"
# list of connected users (fd => uid)
class_property connected_users = Hash(Int32, Int32).new
# users_status: keep track of the users' status even if they are
# disconnected, allowing the application to handle connection problems
class_property users_status = Hash(Int32, User).new
class_property service : IPC::Service? = nil
end

132
src/server/handlers.cr Normal file
View File

@ -0,0 +1,132 @@
require "dodb"
require "base64"
# reception of a file chunk
def hdl_transfer(message : FileStorage::Message::Transfer,
user : User,
event : IPC::Event::Message) : FileStorage::Message::Response
puts "receiving a file"
transfer_message = FileStorage::Message::Transfer.from_json(
String.new event.message.payload
)
pp! transfer_message
puts "chunk: #{transfer_message.chunk}"
puts "data: #{Base64.decode transfer_message.data}"
FileStorage::Message::Response.new message.mid, "Ok"
end
# TODO
# the client sent an upload request
def hdl_upload(request : FileStorage::Message::UploadRequest,
user : User,
event : IPC::Event::Message) : FileStorage::Message::Response
puts "hdl upload: mid=#{request.mid}"
pp! request
FileStorage::Message::Response.new request.mid, "Upload OK"
end
# TODO
# the client sent a download request
def hdl_download(request : FileStorage::Message::DownloadRequest,
user : User,
event : IPC::Event::Message) : FileStorage::Message::Response
puts "hdl download: mid=#{request.mid}"
pp! request
FileStorage::Message::Response.new request.mid, "Download OK"
end
# Entry point for request management
# Each request should have a response.
# Then, responses are sent in a single message.
def hdl_requests(requests : Array(FileStorage::Message::Request),
user : User,
event : IPC::Event::Message) : Array(FileStorage::Message::Response)
puts "hdl request"
responses = Array(FileStorage::Message::Response).new
requests.each do |request|
case request
when FileStorage::Message::DownloadRequest
responses << hdl_download request, user, event
when FileStorage::Message::UploadRequest
responses << hdl_upload request, user, event
else
raise "request not understood"
end
puts
end
responses
end
# store the client in connected_users and users_status
# if already in users_status:
# check if the requests are the same
# if not: add them to the user structure in users_status
def hdl_authentication(event : IPC::Event::Message)
authentication_message =
FileStorage::Message::Authentication.from_json(
String.new event.message.payload
)
userid = authentication_message.token.uid
puts "user authentication: #{userid}"
# Is the user already recorded in users_status?
if Context.users_status[userid]?
puts "We already knew this user"
Context.connected_users[event.connection.fd] = userid
# TODO
pp! Context.connected_users
pp! Context.users_status[userid]
else
# AuthenticationMessage includes requests.
new_user =
User.new authentication_message.token,
authentication_message.requests
Context.connected_users[event.connection.fd] = userid
# record the new user in users_status
Context.users_status[userid] = new_user
puts "New user is: #{new_user.token.login}"
end
# The user is now connected.
user = Context.users_status[userid]
# We verify the user's rights to upload files.
# TODO RIGHTS
# if user wants to upload but not allowed to: Response
# if user wants to get a file but not allowed to: Response
# The user is authorized to upload files.
# TODO: quotas
# Quotas are not defined yet.
responses = hdl_requests authentication_message.requests,
Context.users_status[userid],
event
# Sending a response, containing a response for each request.
# The response is "Ok" when the message is well received and authorized.
response = FileStorage::Message::Responses.new authentication_message.mid, "Ok", responses
event.connection.send FileStorage::MessageType::Responses.to_u8, response.to_json
end

142
src/server/main-loop.cr Normal file
View File

@ -0,0 +1,142 @@
Context.service = IPC::Service.new Context.service_name
Context.service.not_nil!.loop do |event|
case event
when IPC::Event::Timer
puts "#{CORANGE}IPC::Event::Timer#{CRESET}"
when IPC::Event::Connection
puts "#{CBLUE}IPC::Event::Connection: #{event.connection.fd}#{CRESET}"
when IPC::Event::Disconnection
puts "#{CBLUE}IPC::Event::Disconnection: #{event.connection.fd}#{CRESET}"
Context.connected_users.select! do |fd, uid|
fd != event.connection.fd
end
when IPC::Event::ExtraSocket
puts "#{CRED}IPC::Event::ExtraSocket: should not happen in this service#{CRESET}"
when IPC::Event::Switch
puts "#{CRED}IPC::Event::Switch: should not happen in this service#{CRESET}"
# IPC::Event::Message has to be the last entry
# because ExtraSocket and Switch inherit from Message class
when IPC::Event::Message
puts "#{CBLUE}IPC::Event::Message#{CRESET}: #{event.connection.fd}"
# The first message sent to the server has to be the AuthenticationMessage.
# Users sent their token (JWT) to authenticate themselves.
# The token contains the user id, its login and a few other parameters.
# (see the authd documentation).
# TODO: for now, the token is replaced by a hardcoded one, for debugging
mtype = FileStorage::MessageType.new event.message.type.to_i32
# First, the user has to be authenticated unless we are receiving its first message
userid = Context.connected_users[event.connection.fd]?
if ! userid
case mtype
when .authentication?
else
mid = "message id not found"
m = String.new event.message.payload
case mtype
when .authentication?
when .upload_request?
puts "Upload request"
request = FileStorage::Message::UploadRequest.from_json(m)
mid = request.mid
when .download_request?
puts "Download request"
request = FileStorage::Message::DownloadRequest.from_json(m)
mid = request.mid
when .response?
puts "Response message"
request = FileStorage::Message::Response.from_json(m)
mid = request.mid
raise "not implemented yet"
when .responses?
puts "Responses message"
request = FileStorage::Message::Responses.from_json(m)
mid = request.mid
raise "not implemented yet"
when .error?
puts "Error message"
request = FileStorage::Message::Error.from_json(m)
mid = request.mid
raise "not implemented yet"
when .transfer?
request = FileStorage::Message::Transfer.from_json(m)
mid = request.mid
else
raise "Event type not supported, message from a non connected user."
end
response = FileStorage::Message::Response.new mid, "Not OK", "Action on non connected user"
event.connection.send FileStorage::MessageType::Response.to_u8, response.to_json
next
end
end
case mtype
when .authentication?
puts "Receiving an authentication message"
# 1. test if the client is already authenticated
if userid
user = Context.users_status[userid]
raise "Authentication message while the user was already connected: this should not happen"
else
puts "User is not currently connected"
hdl_authentication event
end
when .upload_request?
puts "Upload request"
request = FileStorage::Message::UploadRequest.from_json(
String.new event.message.payload
)
response = hdl_upload request, Context.users_status[userid], event
event.connection.send FileStorage::MessageType::Response.to_u8, response.to_json
raise "not implemented yet"
when .download_request?
puts "Download request"
request = FileStorage::Message::DownloadRequest.from_json(
String.new event.message.payload
)
response = hdl_download request, Context.users_status[userid], event
event.connection.send FileStorage::MessageType::Response.to_u8, response.to_json
raise "not implemented yet"
when .response?
puts "Response message"
raise "not implemented yet"
when .responses?
puts "Responses message"
raise "not implemented yet"
when .error?
puts "Error message"
raise "not implemented yet"
when .transfer?
# throw an error if the user isn't recorded
unless user = Context.users_status[userid]?
raise "The user isn't recorded in the users_status structure"
end
transfer = FileStorage::Message::Transfer.from_json(
String.new event.message.payload
)
response = hdl_transfer transfer, Context.users_status[userid], event
event.connection.send FileStorage::MessageType::Response.to_u8, response.to_json
hdl_transfer transfer, user, event
end
else
raise "Event type not supported."
end
end

35
src/server/main.cr Normal file
View File

@ -0,0 +1,35 @@
require "option_parser"
require "ipc"
require "json"
require "../common/colors"
require "../common/filestorage.cr"
require "./context.cr"
require "./handlers.cr"
# TODO: if the user is disconnected, we should ask him if it still want to process
# for old requests.
#
# Example: the user is on a web page, the connection is broken for some reason.
# The user can still browse the website, change page and discard what
# he was doing. Regardless of the result. With or without finishing to
# upload or download its files.
# TODO:
# * elegantly handling errors
# * store the file, /files/userid/UID.bin for example: /files/1002/UID.bin
# * metadata should be in a dodb
# /storage/partitions/by_uid/UID.json -> content:
# data: /files/uid/UID.bin (storing raw files)
# uid: 1002
# name: "The File About Things"
# size: 1500
# tags: thing1 thing2
# * authd integration
# * knowing which parts of the files are still to be sent
# * rights
# * quotas
require "./cli.cr"
require "./main-loop.cr"

87
src/tests/common-tests.cr Normal file
View File

@ -0,0 +1,87 @@
require "../common/filestorage.cr"
# This file test the following code
# classes:
# * Chunk, FileInfo
# * UploadRequest, DownloadRequest
# * AuthenticationMessage, Response, TransferMessage
# functions:
# * data_digest, file_digest
# data_digest
# `echo -n "coucou" | sha256sum`
# => 110812f67fa1e1f0117f6f3d70241c1a42a7b07711a93c2477cc516d9042f9db
filename = "./README.md"
data = "coucou".chomp.to_slice
pp! FileStorage.data_digest data
puts
# file_digest
# `cat README.md | sha256sum`
# => 79c66991a965185958a1efb17d12652bdd8dc2de0da89b2dc152e2eeb2e02eff
File.open(filename) do |file|
pp! FileStorage.file_digest file
end
puts
# Chunk
pp! FileStorage::Chunk.new 1, 2, "blablabla"
puts
# FileInfo
File.open(filename) do |file|
pp! FileStorage::FileInfo.new file, [ "tag1", "tag2" ]
end
puts
# Token
# XXX: should not exist, it will be replaced by an authd JWT token soon.
token = FileStorage::Token.new 1002, "jean-dupont"
pp! token
puts
# for later
requests = Array(FileStorage::Message::Request).new
# UploadRequest
File.open(filename) do |file|
file_info = FileStorage::FileInfo.new file, [ "tag1", "tag2" ]
upload_request = FileStorage::Message::UploadRequest.new file_info
pp! upload_request
requests << upload_request
end
puts
# DownloadRequest
pp! FileStorage::Message::DownloadRequest.new uuid: "abc"
pp! FileStorage::Message::DownloadRequest.new name: "the other one"
pp! FileStorage::Message::DownloadRequest.new tags: [ "tag1", "tag2" ]
puts
# AuthenticationMessage
pp! FileStorage::Message::Authentication.new token, requests
puts
# Response
pp! FileStorage::Message::Response.new "UUID", "Ok"
pp! FileStorage::Message::Response.new "UUID", "Error", "Cannot store the file"
puts
# TransferMessage
File.open(filename) do |file|
file_info = FileStorage::FileInfo.new file, [ "tag1", "tag2" ]
somedata = "coucou".to_slice
pp! FileStorage::Message::Transfer.new file_info, 1, somedata
end

32
src/tests/json_tests.cr Normal file
View File

@ -0,0 +1,32 @@
require "json"
require "../common/filestorage.cr"
unless ARGV.size > 0
raise "Usage: json_tests file"
end
files_info = Array(FileStorage::FileInfo).new
ARGV.each do |filename|
File.open(filename) do |file|
files_info << FileStorage::FileInfo.new file, %w(important truc machin)
end
end
token = FileStorage::Token.new 1002, "karchnu"
requests = Array(FileStorage::Message::Request).new
files_info.each do |file_info|
requests << FileStorage::Message::UploadRequest.new file_info
end
authentication_message = FileStorage::Message::Authentication.new token, requests
# TODO, TEST, DEBUG, XXX, FIXME
pp! authentication_message.to_json
am_from_json = FileStorage::Message::Authentication.from_json authentication_message.to_json
pp! am_from_json