Page Menu
Home
Software Heritage
Search
Configure Global Search
Log In
Files
F9349598
api.py
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
8 KB
Subscribers
None
api.py
View Options
#!/usr/bin/env python3
# Copyright (C) 2015 The Software Heritage developers
# See the AUTHORS file at the top-level directory of this distribution
# License: GNU General Public License version 3, or any later version
# See top-level LICENSE file for more information
import
logging
from
flask
import
Flask
,
Response
,
make_response
,
request
from
swh.loader.git.storage
import
storage
,
db
,
service
from
swh.loader.git.protocols
import
serial
# api's definition
app
=
Flask
(
__name__
)
def
read_request_payload
(
request
):
"""Read the request's payload.
"""
# TODO: Check the signed pickled data?
return
serial
.
load
(
request
.
stream
)
def
write_response
(
data
):
"""Write response from data.
"""
return
Response
(
serial
.
dumps
(
data
),
mimetype
=
serial
.
MIMETYPE
)
@app.route
(
'/'
)
def
hello
():
"""A simple api to define what the server is all about.
FIXME: A redirect towards a static page defining the routes would be nice.
"""
return
'Dev SWH API'
# from uri to type
_uri_types
=
{
'revisions'
:
storage
.
Type
.
revision
,
'directories'
:
storage
.
Type
.
directory
,
'contents'
:
storage
.
Type
.
content
,
'releases'
:
storage
.
Type
.
release
,
'occurrences'
:
storage
.
Type
.
occurrence
,
'persons'
:
storage
.
Type
.
person
}
def
_do_action_with_payload
(
conf
,
action_fn
,
uri_type
,
id
,
map_result_fn
):
uri_type_ok
=
_uri_types
.
get
(
uri_type
,
None
)
if
uri_type_ok
is
None
:
return
make_response
(
'Bad request!'
,
400
)
vcs_object
=
read_request_payload
(
request
)
vcs_object
.
update
({
'id'
:
id
,
'type'
:
uri_type_ok
})
return
action_fn
(
conf
,
vcs_object
,
map_result_fn
)
# occurrence type is not dealt the same way
_post_all_uri_types
=
{
'revisions'
:
storage
.
Type
.
revision
,
'directories'
:
storage
.
Type
.
directory
,
'contents'
:
storage
.
Type
.
content
}
@app.route
(
'/vcs/<uri_type>/'
,
methods
=
[
'POST'
])
def
filter_unknowns_type
(
uri_type
):
"""Filters unknown sha1 to the backend and returns them.
"""
if
request
.
headers
.
get
(
'Content-Type'
)
!=
serial
.
MIMETYPE
:
return
make_response
(
'Bad request. Expected
%s
data!'
%
serial
.
MIMETYPE
,
400
)
obj_type
=
_post_all_uri_types
.
get
(
uri_type
)
if
obj_type
is
None
:
return
make_response
(
'Bad request. Type not supported!'
,
400
)
sha1s
=
read_request_payload
(
request
)
config
=
app
.
config
[
'conf'
]
with
db
.
connect
(
config
[
'db_url'
])
as
db_conn
:
unknowns_sha1s
=
service
.
filter_unknowns_type
(
db_conn
,
obj_type
,
sha1s
)
if
unknowns_sha1s
is
None
:
return
make_response
(
'Bad request!'
,
400
)
else
:
return
write_response
(
unknowns_sha1s
)
@app.route
(
'/vcs/persons/'
,
methods
=
[
'POST'
])
def
post_person
():
"""Find a person.
"""
if
request
.
headers
.
get
(
'Content-Type'
)
!=
serial
.
MIMETYPE
:
return
make_response
(
'Bad request. Expected
%s
data!'
%
serial
.
MIMETYPE
,
400
)
origin
=
read_request_payload
(
request
)
config
=
app
.
config
[
'conf'
]
with
db
.
connect
(
config
[
'db_url'
])
as
db_conn
:
try
:
person_found
=
service
.
find_person
(
db_conn
,
origin
)
if
person_found
:
return
write_response
(
person_found
)
else
:
return
make_response
(
'Person not found!'
,
404
)
except
:
return
make_response
(
'Bad request!'
,
400
)
@app.route
(
'/origins/'
,
methods
=
[
'POST'
])
def
post_origin
():
"""Find an origin.
"""
if
request
.
headers
.
get
(
'Content-Type'
)
!=
serial
.
MIMETYPE
:
return
make_response
(
'Bad request. Expected
%s
data!'
%
serial
.
MIMETYPE
,
400
)
origin
=
read_request_payload
(
request
)
config
=
app
.
config
[
'conf'
]
with
db
.
connect
(
config
[
'db_url'
])
as
db_conn
:
try
:
origin_found
=
service
.
find_origin
(
db_conn
,
origin
)
if
origin_found
:
return
write_response
(
origin_found
)
else
:
return
make_response
(
'Origin not found!'
,
404
)
except
:
return
make_response
(
'Bad request!'
,
400
)
@app.route
(
'/origins/'
,
methods
=
[
'PUT'
])
def
put_origin
():
"""Create an origin or returns it if already existing.
"""
if
request
.
headers
.
get
(
'Content-Type'
)
!=
serial
.
MIMETYPE
:
return
make_response
(
'Bad request. Expected
%s
data!'
%
serial
.
MIMETYPE
,
400
)
origin
=
read_request_payload
(
request
)
config
=
app
.
config
[
'conf'
]
with
db
.
connect
(
config
[
'db_url'
])
as
db_conn
:
try
:
origin_found
=
service
.
add_origin
(
db_conn
,
origin
)
return
write_response
(
origin_found
)
# FIXME: 204
except
:
return
make_response
(
'Bad request!'
,
400
)
@app.route
(
'/vcs/<uri_type>/'
,
methods
=
[
'PUT'
])
def
put_all
(
uri_type
):
"""Store or update given objects (uri_type in {contents, directories, releases).
"""
if
request
.
headers
.
get
(
'Content-Type'
)
!=
serial
.
MIMETYPE
:
return
make_response
(
'Bad request. Expected
%s
data!'
%
serial
.
MIMETYPE
,
400
)
payload
=
read_request_payload
(
request
)
obj_type
=
_uri_types
[
uri_type
]
config
=
app
.
config
[
'conf'
]
with
db
.
connect
(
config
[
'db_url'
])
as
db_conn
:
service
.
persist
(
db_conn
,
config
,
obj_type
,
payload
)
return
make_response
(
'Successful creation!'
,
204
)
def
add_object
(
config
,
vcs_object
,
map_result_fn
):
"""Add object in storage.
- config is the configuration needed for the backend to execute query
- vcs_object is the object to look for in the backend
- map_result_fn is a mapping function which takes the backend's result
and transform its output accordingly.
This function returns an http response of the result.
"""
type
=
vcs_object
[
'type'
]
id
=
vcs_object
[
'id'
]
logging
.
debug
(
'storage
%s
%s
'
%
(
type
,
id
))
with
db
.
connect
(
config
[
'db_url'
])
as
db_conn
:
res
=
service
.
add_objects
(
db_conn
,
config
,
type
,
[
vcs_object
])
return
make_response
(
map_result_fn
(
id
,
res
),
204
)
def
_do_lookup
(
conf
,
uri_type
,
id
,
map_result_fn
):
"""Looking up type object with sha1.
- config is the configuration needed for the backend to execute query
- vcs_object is the object to look for in the backend
- map_result_fn is a mapping function which takes the backend's result
and transform its output accordingly.
This function returns an http response of the result.
"""
uri_type_ok
=
_uri_types
.
get
(
uri_type
,
None
)
if
not
uri_type_ok
:
return
make_response
(
'Bad request!'
,
400
)
with
db
.
connect
(
conf
[
'db_url'
])
as
db_conn
:
res
=
storage
.
find
(
db_conn
,
id
,
uri_type_ok
)
if
res
:
return
write_response
(
map_result_fn
(
id
,
res
))
# 200
return
make_response
(
'Not found!'
,
404
)
@app.route
(
'/vcs/occurrences/<id>'
)
def
list_occurrences_for
(
id
):
"""Return the occurrences pointing to the revision id.
"""
return
_do_lookup
(
app
.
config
[
'conf'
],
'occurrences'
,
id
,
lambda
_
,
result
:
list
(
map
(
lambda
col
:
col
[
1
],
result
)))
@app.route
(
'/vcs/<uri_type>/<id>'
)
def
object_exists_p
(
uri_type
,
id
):
"""Assert if the object with sha1 id, of type uri_type, exists.
"""
return
_do_lookup
(
app
.
config
[
'conf'
],
uri_type
,
id
,
lambda
sha1
,
_
:
{
'id'
:
sha1
})
@app.route
(
'/vcs/<uri_type>/<id>'
,
methods
=
[
'PUT'
])
def
put_object
(
uri_type
,
id
):
"""Put an object in storage.
"""
return
_do_action_with_payload
(
app
.
config
[
'conf'
],
add_object
,
uri_type
,
id
,
lambda
sha1
,
_2
:
sha1
)
# FIXME: use id or result instead
def
run
(
conf
):
"""Run the api's server.
conf is a dictionary of keywords:
- 'db_url' the db url's access (through psycopg2 format)
- 'content_storage_dir' revisions/directories/contents storage on disk
- 'host' to override the default 127.0.0.1 to open or not the server to
the world
- 'port' to override the default of 5000 (from the underlying layer:
flask)
- 'debug' activate the verbose logs
"""
print
(
"""SWH Api run
host: %s
port: %s
debug: %s"""
%
(
conf
[
'host'
],
conf
.
get
(
'port'
,
None
),
conf
[
'debug'
]))
# app.config is the app's state (accessible)
app
.
config
.
update
({
'conf'
:
conf
})
app
.
run
(
host
=
conf
[
'host'
],
port
=
conf
.
get
(
'port'
,
None
),
debug
=
conf
[
'debug'
]
==
'true'
)
File Metadata
Details
Attached
Mime Type
text/x-python
Expires
Jul 4 2025, 7:32 PM (7 w, 6 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3356057
Attached To
rDLDG Git loader
Event Timeline
Log In to Comment