1
0
mirror of https://github.com/GNS3/gns3-server synced 2024-11-30 20:28:08 +00:00

Item => Shape

This commit is contained in:
Julien Duponchelle 2016-06-21 09:49:16 +02:00
parent 85ebac7eb3
commit 116135b9dc
No known key found for this signature in database
GPG Key ID: CE8B29639E07F5E8
12 changed files with 125 additions and 125 deletions

View File

@ -265,9 +265,9 @@ The available notification are:
* link.created * link.created
* link.updated * link.updated
* link.deleted * link.deleted
* item.created * shape.created
* item.updated * shape.updated
* item.deleted * shape.deleted
* log.error * log.error
* log.warning * log.warning
* log.info * log.info

View File

@ -6,10 +6,10 @@ Node
A Virtual Machine (Dynamips, IOU, Qemu, VPCS...), a cloud, a builtin device (switch, hub...) A Virtual Machine (Dynamips, IOU, Qemu, VPCS...), a cloud, a builtin device (switch, hub...)
Item Shape
----- -----
Item are visual element not used by the network emulation. Like Shape are visual element not used by the network emulation. Like
text, images, rectangle... They are pure SVG elements. text, images, rectangle... They are pure SVG elements.
Adapter Adapter

View File

@ -24,7 +24,7 @@ import shutil
from uuid import UUID, uuid4 from uuid import UUID, uuid4
from .node import Node from .node import Node
from .item import Item from .shape import Shape
from .topology import project_to_topology, load_topology from .topology import project_to_topology, load_topology
from .udp_link import UDPLink from .udp_link import UDPLink
from ..config import Config from ..config import Config
@ -77,7 +77,7 @@ class Project:
self._allocated_node_names = set() self._allocated_node_names = set()
self._nodes = {} self._nodes = {}
self._links = {} self._links = {}
self._items = {} self._shapes = {}
# Create the project on demand on the compute node # Create the project on demand on the compute node
self._project_created_on_compute = set() self._project_created_on_compute = set()
@ -266,42 +266,42 @@ class Project:
return self._nodes return self._nodes
@property @property
def items(self): def shapes(self):
""" """
:returns: Dictionary of the items :returns: Dictionary of the shapes
""" """
return self._items return self._shapes
@asyncio.coroutine @asyncio.coroutine
def add_item(self, item_id=None, **kwargs): def add_shape(self, shape_id=None, **kwargs):
""" """
Create an item or return an existing item Create an shape or return an existing shape
:param kwargs: See the documentation of item :param kwargs: See the documentation of shape
""" """
if item_id not in self._items: if shape_id not in self._shapes:
item = Item(self, item_id=item_id, **kwargs) shape = Shape(self, shape_id=shape_id, **kwargs)
self._items[item.id] = item self._shapes[shape.id] = shape
self.controller.notification.emit("item.created", item.__json__()) self.controller.notification.emit("shape.created", shape.__json__())
self.dump() self.dump()
return item return shape
return self._items[item_id] return self._shapes[shape_id]
def get_item(self, item_id): def get_shape(self, shape_id):
""" """
Return the Item or raise a 404 if the item is unknown Return the Shape or raise a 404 if the shape is unknown
""" """
try: try:
return self._items[item_id] return self._shapes[shape_id]
except KeyError: except KeyError:
raise aiohttp.web.HTTPNotFound(text="Item ID {} doesn't exist".format(item_id)) raise aiohttp.web.HTTPNotFound(text="Shape ID {} doesn't exist".format(shape_id))
@asyncio.coroutine @asyncio.coroutine
def delete_item(self, item_id): def delete_shape(self, shape_id):
item = self.get_item(item_id) shape = self.get_shape(shape_id)
del self._items[item.id] del self._shapes[shape.id]
self.dump() self.dump()
self.controller.notification.emit("item.deleted", item.__json__()) self.controller.notification.emit("shape.deleted", shape.__json__())
@asyncio.coroutine @asyncio.coroutine
def add_link(self, link_id=None): def add_link(self, link_id=None):
@ -397,8 +397,8 @@ class Project:
node = self.get_node(node_link["node_id"]) node = self.get_node(node_link["node_id"])
yield from link.add_node(node, node_link["adapter_number"], node_link["port_number"]) yield from link.add_node(node, node_link["adapter_number"], node_link["port_number"])
for item_data in topology.get("items", []): for shape_data in topology.get("shapes", []):
item = yield from self.add_item(**item_data) shape = yield from self.add_shape(**shape_data)
self._status = "opened" self._status = "opened"
def dump(self): def dump(self):

View File

@ -19,18 +19,18 @@ import asyncio
import uuid import uuid
class Item: class Shape:
""" """
Item are visual element not used by the network emulation. Like Shape are visual element not used by the network emulation. Like
text, images, rectangle... They are pure SVG elements. text, images, rectangle... They are pure SVG elements.
""" """
def __init__(self, project, item_id=None, svg=None, x=0, y=0, z=0): def __init__(self, project, shape_id=None, svg=None, x=0, y=0, z=0):
self.svg = "<svg></svg>" self.svg = "<svg></svg>"
self._project = project self._project = project
if item_id is None: if shape_id is None:
self._id = str(uuid.uuid4()) self._id = str(uuid.uuid4())
else: else:
self._id = item_id self._id = shape_id
self._x = x self._x = x
self._y = y self._y = y
self._z = z self._z = z
@ -83,7 +83,7 @@ class Item:
for prop in kwargs: for prop in kwargs:
if getattr(self, prop) != kwargs[prop]: if getattr(self, prop) != kwargs[prop]:
setattr(self, prop, kwargs[prop]) setattr(self, prop, kwargs[prop])
self._project.controller.notification.emit("item.updated", self.__json__()) self._project.controller.notification.emit("shape.updated", self.__json__())
self._project.dump() self._project.dump()
def __json__(self, topology_dump=False): def __json__(self, topology_dump=False):
@ -92,20 +92,20 @@ class Item:
""" """
if topology_dump: if topology_dump:
return { return {
"item_id": self._id, "shape_id": self._id,
"x": self._x, "x": self._x,
"y": self._y, "y": self._y,
"z": self._z, "z": self._z,
} }
return { return {
"project_id": self._project.id, "project_id": self._project.id,
"item_id": self._id, "shape_id": self._id,
"x": self._x, "x": self._x,
"y": self._y, "y": self._y,
"z": self._z, "z": self._z,
} }
def __repr__(self): def __repr__(self):
return "<gns3server.controller.Item {}>".format(self._id) return "<gns3server.controller.Shape {}>".format(self._id)

View File

@ -34,7 +34,7 @@ def project_to_topology(project):
"nodes": [], "nodes": [],
"links": [], "links": [],
"computes": [], "computes": [],
"items": [] "shapes": []
}, },
"type": "topology", "type": "topology",
"revision": GNS3_FILE_FORMAT_REVISION, "revision": GNS3_FILE_FORMAT_REVISION,
@ -47,8 +47,8 @@ def project_to_topology(project):
data["topology"]["nodes"].append(node.__json__(topology_dump=True)) data["topology"]["nodes"].append(node.__json__(topology_dump=True))
for link in project.links.values(): for link in project.links.values():
data["topology"]["links"].append(link.__json__(topology_dump=True)) data["topology"]["links"].append(link.__json__(topology_dump=True))
for item in project.items.values(): for shape in project.shapes.values():
data["topology"]["items"].append(item.__json__(topology_dump=True)) data["topology"]["shapes"].append(shape.__json__(topology_dump=True))
for compute in computes: for compute in computes:
if hasattr(compute, "__json__"): if hasattr(compute, "__json__"):
data["topology"]["computes"].append(compute.__json__(topology_dump=True)) data["topology"]["computes"].append(compute.__json__(topology_dump=True))

View File

@ -20,4 +20,4 @@ from .project_handler import ProjectHandler
from .node_handler import NodeHandler from .node_handler import NodeHandler
from .link_handler import LinkHandler from .link_handler import LinkHandler
from .server_handler import ServerHandler from .server_handler import ServerHandler
from .item_handler import ItemHandler from .shape_handler import ShapeHandler

View File

@ -20,88 +20,88 @@ import aiohttp
from gns3server.web.route import Route from gns3server.web.route import Route
from gns3server.controller import Controller from gns3server.controller import Controller
from gns3server.schemas.item import ( from gns3server.schemas.shape import (
ITEM_OBJECT_SCHEMA, SHAPE_OBJECT_SCHEMA,
) )
class ItemHandler: class ShapeHandler:
""" """
API entry point for Item API entry point for Shape
""" """
@Route.get( @Route.get(
r"/projects/{project_id}/items", r"/projects/{project_id}/shapes",
parameters={ parameters={
"project_id": "Project UUID" "project_id": "Project UUID"
}, },
status_codes={ status_codes={
200: "List of items returned", 200: "List of shapes returned",
}, },
description="List items of a project") description="List shapes of a project")
def list_items(request, response): def list_shapes(request, response):
controller = Controller.instance() controller = Controller.instance()
project = controller.get_project(request.match_info["project_id"]) project = controller.get_project(request.match_info["project_id"])
response.json([v for v in project.items.values()]) response.json([v for v in project.shapes.values()])
@Route.post( @Route.post(
r"/projects/{project_id}/items", r"/projects/{project_id}/shapes",
parameters={ parameters={
"project_id": "Project UUID" "project_id": "Project UUID"
}, },
status_codes={ status_codes={
201: "Item created", 201: "Shape created",
400: "Invalid request" 400: "Invalid request"
}, },
description="Create a new item instance", description="Create a new shape instance",
input=ITEM_OBJECT_SCHEMA, input=SHAPE_OBJECT_SCHEMA,
output=ITEM_OBJECT_SCHEMA) output=SHAPE_OBJECT_SCHEMA)
def create(request, response): def create(request, response):
controller = Controller.instance() controller = Controller.instance()
project = controller.get_project(request.match_info["project_id"]) project = controller.get_project(request.match_info["project_id"])
item = yield from project.add_item(**request.json) shape = yield from project.add_shape(**request.json)
response.set_status(201) response.set_status(201)
response.json(item) response.json(shape)
@Route.put( @Route.put(
r"/projects/{project_id}/items/{item_id}", r"/projects/{project_id}/shapes/{shape_id}",
parameters={ parameters={
"project_id": "Project UUID", "project_id": "Project UUID",
"item_id": "Item UUID" "shape_id": "Shape UUID"
}, },
status_codes={ status_codes={
201: "Item updated", 201: "Shape updated",
400: "Invalid request" 400: "Invalid request"
}, },
description="Create a new item instance", description="Create a new shape instance",
input=ITEM_OBJECT_SCHEMA, input=SHAPE_OBJECT_SCHEMA,
output=ITEM_OBJECT_SCHEMA) output=SHAPE_OBJECT_SCHEMA)
def update(request, response): def update(request, response):
controller = Controller.instance() controller = Controller.instance()
project = controller.get_project(request.match_info["project_id"]) project = controller.get_project(request.match_info["project_id"])
item = project.get_item(request.match_info["item_id"]) shape = project.get_shape(request.match_info["shape_id"])
yield from item.update(**request.json) yield from shape.update(**request.json)
response.set_status(201) response.set_status(201)
response.json(item) response.json(shape)
@Route.delete( @Route.delete(
r"/projects/{project_id}/items/{item_id}", r"/projects/{project_id}/shapes/{shape_id}",
parameters={ parameters={
"project_id": "Project UUID", "project_id": "Project UUID",
"item_id": "Item UUID" "shape_id": "Shape UUID"
}, },
status_codes={ status_codes={
204: "Item deleted", 204: "Shape deleted",
400: "Invalid request" 400: "Invalid request"
}, },
description="Delete a item instance") description="Delete a shape instance")
def delete(request, response): def delete(request, response):
controller = Controller.instance() controller = Controller.instance()
project = controller.get_project(request.match_info["project_id"]) project = controller.get_project(request.match_info["project_id"])
yield from project.delete_item(request.match_info["item_id"]) yield from project.delete_shape(request.match_info["shape_id"])
response.set_status(204) response.set_status(204)

View File

@ -16,13 +16,13 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
ITEM_OBJECT_SCHEMA = { SHAPE_OBJECT_SCHEMA = {
"$schema": "http://json-schema.org/draft-04/schema#", "$schema": "http://json-schema.org/draft-04/schema#",
"description": "An item object", "description": "An shape object",
"type": "object", "type": "object",
"properties": { "properties": {
"item_id": { "shape_id": {
"description": "Link UUID", "description": "Shape UUID",
"type": "string", "type": "string",
"minLength": 36, "minLength": 36,
"maxLength": 36, "maxLength": 36,
@ -48,7 +48,7 @@ ITEM_OBJECT_SCHEMA = {
"type": "integer" "type": "integer"
}, },
"svg": { "svg": {
"description": "SVG content of the item", "description": "SVG content of the shape",
"type": "string", "type": "string",
"pattern": "^<.+>$" "pattern": "^<.+>$"
} }

View File

@ -213,30 +213,30 @@ def test_deleteLink(async_run, project, controller):
assert len(project._links) == 0 assert len(project._links) == 0
def test_addItem(async_run, project, controller): def test_addShape(async_run, project, controller):
controller.notification.emit = MagicMock() controller.notification.emit = MagicMock()
item = async_run(project.add_item(None, svg="<svg></svg>")) shape = async_run(project.add_shape(None, svg="<svg></svg>"))
assert len(project._items) == 1 assert len(project._shapes) == 1
controller.notification.emit.assert_any_call("item.created", item.__json__()) controller.notification.emit.assert_any_call("shape.created", shape.__json__())
def test_getItem(async_run, project): def test_getShape(async_run, project):
item = async_run(project.add_item(None)) shape = async_run(project.add_shape(None))
assert project.get_item(item.id) == item assert project.get_shape(shape.id) == shape
with pytest.raises(aiohttp.web_exceptions.HTTPNotFound): with pytest.raises(aiohttp.web_exceptions.HTTPNotFound):
project.get_item("test") project.get_shape("test")
def test_deleteItem(async_run, project, controller): def test_deleteShape(async_run, project, controller):
assert len(project._items) == 0 assert len(project._shapes) == 0
item = async_run(project.add_item()) shape = async_run(project.add_shape())
assert len(project._items) == 1 assert len(project._shapes) == 1
controller._notification = MagicMock() controller._notification = MagicMock()
async_run(project.delete_item(item.id)) async_run(project.delete_shape(shape.id))
controller.notification.emit.assert_any_call("item.deleted", item.__json__()) controller.notification.emit.assert_any_call("shape.deleted", shape.__json__())
assert len(project._items) == 0 assert len(project._shapes) == 0
def test_delete(async_run, project, controller): def test_delete(async_run, project, controller):

View File

@ -22,7 +22,7 @@ import uuid
from tests.utils import AsyncioMagicMock from tests.utils import AsyncioMagicMock
from gns3server.controller.item import Item from gns3server.controller.shape import Shape
from gns3server.controller.project import Project from gns3server.controller.project import Project
@ -32,43 +32,43 @@ def project(controller, async_run):
@pytest.fixture @pytest.fixture
def item(project): def shape(project):
return Item(project, None, svg="<svg></svg>") return Shape(project, None, svg="<svg></svg>")
def test_init_without_uuid(project): def test_init_without_uuid(project):
item = Item(project, None, svg="<svg></svg>") shape = Shape(project, None, svg="<svg></svg>")
assert item.id is not None assert shape.id is not None
def test_init_with_uuid(project): def test_init_with_uuid(project):
id = str(uuid.uuid4()) id = str(uuid.uuid4())
item = Item(project, id, svg="<svg></svg>") shape = Shape(project, id, svg="<svg></svg>")
assert item.id == id assert shape.id == id
def test_json(project): def test_json(project):
i = Item(project, None, svg="<svg></svg>") i = Shape(project, None, svg="<svg></svg>")
assert i.__json__() == { assert i.__json__() == {
"item_id": i.id, "shape_id": i.id,
"project_id": project.id, "project_id": project.id,
"x": i.x, "x": i.x,
"y": i.y, "y": i.y,
"z": i.z "z": i.z
} }
assert i.__json__(topology_dump=True) == { assert i.__json__(topology_dump=True) == {
"item_id": i.id, "shape_id": i.id,
"x": i.x, "x": i.x,
"y": i.y, "y": i.y,
"z": i.z "z": i.z
} }
def test_update(item, project, async_run, controller): def test_update(shape, project, async_run, controller):
controller._notification = AsyncioMagicMock() controller._notification = AsyncioMagicMock()
project.dump = MagicMock() project.dump = MagicMock()
async_run(item.update(x=42)) async_run(shape.update(x=42))
assert item.x == 42 assert shape.x == 42
controller._notification.emit.assert_called_with("item.updated", item.__json__()) controller._notification.emit.assert_called_with("shape.updated", shape.__json__())
assert project.dump.called assert project.dump.called

View File

@ -38,7 +38,7 @@ def test_project_to_topology_empty(tmpdir):
"nodes": [], "nodes": [],
"links": [], "links": [],
"computes": [], "computes": [],
"items": [] "shapes": []
}, },
"type": "topology", "type": "topology",
"version": __version__ "version": __version__
@ -58,14 +58,14 @@ def test_basic_topology(tmpdir, async_run, controller):
async_run(link.add_node(node1, 0, 0)) async_run(link.add_node(node1, 0, 0))
async_run(link.add_node(node2, 0, 0)) async_run(link.add_node(node2, 0, 0))
item = async_run(project.add_item(svg="<svg></svg>")) shape = async_run(project.add_shape(svg="<svg></svg>"))
topo = project_to_topology(project) topo = project_to_topology(project)
assert len(topo["topology"]["nodes"]) == 2 assert len(topo["topology"]["nodes"]) == 2
assert node1.__json__(topology_dump=True) in topo["topology"]["nodes"] assert node1.__json__(topology_dump=True) in topo["topology"]["nodes"]
assert topo["topology"]["links"][0] == link.__json__(topology_dump=True) assert topo["topology"]["links"][0] == link.__json__(topology_dump=True)
assert topo["topology"]["computes"][0] == compute.__json__(topology_dump=True) assert topo["topology"]["computes"][0] == compute.__json__(topology_dump=True)
assert topo["topology"]["items"][0] == item.__json__(topology_dump=True) assert topo["topology"]["shapes"][0] == shape.__json__(topology_dump=True)
def test_load_topology(tmpdir): def test_load_topology(tmpdir):
@ -77,7 +77,7 @@ def test_load_topology(tmpdir):
"nodes": [], "nodes": [],
"links": [], "links": [],
"computes": [], "computes": [],
"items": [] "shapes": []
}, },
"type": "topology", "type": "topology",
"version": __version__} "version": __version__}

View File

@ -30,7 +30,7 @@ from tests.utils import asyncio_patch
from gns3server.handlers.api.controller.project_handler import ProjectHandler from gns3server.handlers.api.controller.project_handler import ProjectHandler
from gns3server.controller import Controller from gns3server.controller import Controller
from gns3server.controller.item import Item from gns3server.controller.shape import Shape
@ -39,49 +39,49 @@ def project(http_controller, async_run):
return async_run(Controller.instance().add_project()) return async_run(Controller.instance().add_project())
def test_create_item(http_controller, tmpdir, project, async_run): def test_create_shape(http_controller, tmpdir, project, async_run):
response = http_controller.post("/projects/{}/items".format(project.id), { response = http_controller.post("/projects/{}/shapes".format(project.id), {
"svg": '<svg height="210" width="500"><line x1="0" y1="0" x2="200" y2="200" style="stroke:rgb(255,0,0);stroke-width:2" /></svg>', "svg": '<svg height="210" width="500"><line x1="0" y1="0" x2="200" y2="200" style="stroke:rgb(255,0,0);stroke-width:2" /></svg>',
"x": 10, "x": 10,
"y": 20, "y": 20,
"z": 0 "z": 0
}, example=True) }, example=True)
assert response.status == 201 assert response.status == 201
assert response.json["item_id"] is not None assert response.json["shape_id"] is not None
def test_update_item(http_controller, tmpdir, project, async_run): def test_update_shape(http_controller, tmpdir, project, async_run):
response = http_controller.post("/projects/{}/items".format(project.id), { response = http_controller.post("/projects/{}/shapes".format(project.id), {
"svg": '<svg height="210" width="500"><line x1="0" y1="0" x2="200" y2="200" style="stroke:rgb(255,0,0);stroke-width:2" /></svg>', "svg": '<svg height="210" width="500"><line x1="0" y1="0" x2="200" y2="200" style="stroke:rgb(255,0,0);stroke-width:2" /></svg>',
"x": 10, "x": 10,
"y": 20, "y": 20,
"z": 0 "z": 0
},) },)
response = http_controller.put("/projects/{}/items/{}".format(project.id, response.json["item_id"]), { response = http_controller.put("/projects/{}/shapes/{}".format(project.id, response.json["shape_id"]), {
"x": 42, "x": 42,
}, example=True) }, example=True)
assert response.status == 201 assert response.status == 201
assert response.json["x"] == 42 assert response.json["x"] == 42
def test_list_item(http_controller, tmpdir, project, async_run): def test_list_shape(http_controller, tmpdir, project, async_run):
response = http_controller.post("/projects/{}/items".format(project.id), { response = http_controller.post("/projects/{}/shapes".format(project.id), {
"svg": '<svg height="210" width="500"><line x1="0" y1="0" x2="200" y2="200" style="stroke:rgb(255,0,0);stroke-width:2" /></svg>', "svg": '<svg height="210" width="500"><line x1="0" y1="0" x2="200" y2="200" style="stroke:rgb(255,0,0);stroke-width:2" /></svg>',
"x": 10, "x": 10,
"y": 20, "y": 20,
"z": 0 "z": 0
}, example=False) }, example=False)
response = http_controller.get("/projects/{}/items".format(project.id), example=True) response = http_controller.get("/projects/{}/shapes".format(project.id), example=True)
assert response.status == 200 assert response.status == 200
assert len(response.json) == 1 assert len(response.json) == 1
def test_delete_item(http_controller, tmpdir, project, async_run): def test_delete_shape(http_controller, tmpdir, project, async_run):
item = Item(project) shape = Shape(project)
project._items = {item.id: item} project._shapes = {shape.id: shape}
response = http_controller.delete("/projects/{}/items/{}".format(project.id, item.id), example=True) response = http_controller.delete("/projects/{}/shapes/{}".format(project.id, shape.id), example=True)
assert response.status == 204 assert response.status == 204
assert item.id not in project._items assert shape.id not in project._shapes