floranet/web/rest/device.py
class RestDevices(DeviceResource):
    """ RestDevices Resource class.
    
    Manages REST API GET AND POST transactions for reading multiple devices,
    and creating devices.
    """
    def __init__(self, **kwargs):
        super(RestDevices, self).__init__(**kwargs)

    @login_required
    @wait_for(timeout=TIMEOUT)
    @inlineCallbacks
    def get(self):
        """Method to get all devices"""
        try:
            devices = yield Device.all()
            if devices is None:
                returnValue({})
            data = {}
            for i,d in enumerate(devices):
                data[i] = marshal(d, self.fields)
            returnValue(data)
            
        except TimeoutError:
            # Exception returns 500 to client
            log.error("REST API timeout retrieving all devices")

    @login_required
    @wait_for(timeout=TIMEOUT)
    @inlineCallbacks
    def post(self):
        """Method to create a device"""
        deveui = self.args['deveui']
        name = self.args['name']
        devclass = self.args['devclass']
        enabled = self.args['enabled']
        otaa = self.args['otaa']
        devaddr = self.args['devaddr']
        appeui = self.args['appeui']
        nwkskey = self.args['nwkskey']
        appskey = self.args['appskey']
        
        message = {}

        # Check for required args
        required = {'deveui', 'name', 'devclass', 'enabled',
                    'otaa', 'appeui', 'devaddr', 'nwkskey', 'appskey'}
        for r in required:
            if otaa is True:
                if r in {'devaddr', 'nwkskey', 'appskey'}:
                    continue
            if self.args[r] is None:
                message[r] = "Missing the {} parameter.".format(r)
        if message:
            abort(400, message=message)

        # Check this device does not currently exist
        exists = yield Device.exists(where=['deveui = ?', deveui])
        if exists:
            message[r] = "Device {} currently exists.".format(euiString(deveui))
            abort(400, message=message)

        # Set devaddr to None if this is an otaa device
        if otaa:
            devaddr = None
        
        # Create and validate
        device = Device(deveui=deveui, name=name, devclass=devclass,
                        enabled=enabled, otaa=otaa, devaddr=devaddr,
                        appeui=appeui, nwkskey=nwkskey, appskey=appskey,
                        fcntup=0, fcntdown=0, fcnterror=False)
        (valid, message) = yield device.valid(self.server)
        if not valid:
            abort(400, message=message)

        try:
            d = yield device.save()
            if d is None:
                abort(500, message={'error': "Error saving device"})
            location = self.restapi.api.prefix + '/device/' + str(device.deveui)
            returnValue(({}, 201, {'Location': location}))
        except TimeoutError:
            # Exception returns 500 to client
            log.error("REST API timeout for device POST request")
floranet/web/rest/appproperty.py
class RestAppPropertys(AppPropertyResource):
    """RestAppPropertys Resource class.
    
    Manages REST API GET and POST transactions for reading multiple
    application properties, and creating properties.
    
    """
    def __init__(self, **kwargs):
        super(RestAppPropertys, self).__init__(**kwargs)
        
    @login_required
    @wait_for(timeout=TIMEOUT)
    @inlineCallbacks
    def get(self, appeui):
        """Method to get all app properties"""
        try:
            
            # Return a 404 if the application is not found.            
            app = yield Application.find(where=['appeui = ?', appeui], limit=1)
            if app is None:
                abort(404, message={'error': "Application {} doesn't exist."
                                    .format(euiString(appeui))})
                
            # Get the properties
            properties = yield app.properties.get()
            if properties is None:
                returnValue({})
            
            data = {}
            for i,p in enumerate(properties):
                data[i] = marshal(p, self.fields)
            returnValue(data)
            
        except TimeoutError:
            log.error("REST API timeout retrieving application {appeui} "
                      "properties", appeui=euiString(appeui))


    @login_required
    @wait_for(timeout=TIMEOUT)
    @inlineCallbacks
    def post(self):
        """Method to create an application property"""
        appeui = self.args['appeui']
        port = self.args['port']
        name = self.args['name']
        type = self.args['type']
        
        message = {}
        # Check for required args
        required = {'appeui', 'port', 'name', 'type'}
        for r in required:
            if self.args[r] is None:
                message[r] = "Missing the {} parameter.".format(r)
        if message:
            abort(400, message=message)

        # Return a 404 if the application is not found.            
        app = yield Application.find(where=['appeui = ?', appeui], limit=1)
        if app is None:
            abort(404, message={'error': "Application {} doesn't exist."
                                .format(euiString(appeui))})

        # Check this property does not currently exist
        exists = yield AppProperty.exists(where=['application_id = ? AND port = ?',
                                              app.id, port])
        if exists:
            message = {'error': "Application property for appeui {}, port "
                       "{} exists".format(euiString(appeui), port)}
            abort(400, message=message)

        # Create and validate
        p = AppProperty(application_id=app.id, port=port, name=name, type=type)
        
        (valid, message) = yield p.valid()
        if not valid:
            abort(400, message=message)

        try:
            prop = yield p.save()
            if prop is None:
                abort(500, message={'error': "Error saving the property."})
            location = self.restapi.api.prefix + '/property/' + str(prop.id)
            returnValue(({}, 201, {'Location': location}))
            
        except TimeoutError:
            log.error("REST API timeout post application {appeui} "
                      "property {port}", appeui=euiString(appeui), port=port)
floranet/web/rest/system.py
class RestSystem(Resource):
    """System configuration resource base class.
    
    Attributes:
        restapi (RestApi): Flask Restful API object
        server (NetServer): FloraNet network server object
        fields (dict): Dictionary of attributes to be returned to a REST request
        parser (RequestParser): Flask RESTful request parser
        args (dict): Parsed request argument
    """
        
    def __init__(self, **kwargs):
        self.restapi = kwargs['restapi']
        self.server = kwargs['server']
        self.fields = {
            'name': fields.String,
            'listen': fields.String,
            'port': fields.Integer,
            'webport': fields.Integer,
            'apitoken': fields.String,
            'freqband': fields.String,
            'netid': fields.Integer,
            'duplicateperiod': fields.Integer,
            'fcrelaxed': fields.Boolean,
            'otaastart': fields.Integer,
            'otaaend':  fields.Integer,
            'macqueueing': fields.Boolean,
            'macqueuelimit': fields.Integer,
            'adrenable': fields.Boolean,
            'adrmargin': fields.Float,
            'adrcycletime': fields.Integer,
            'adrmessagetime': fields.Integer,
        }
        self.parser = reqparse.RequestParser(bundle_errors=True)
        self.parser.add_argument('name', type=str)
        self.parser.add_argument('listen', type=str)
        self.parser.add_argument('port', type=int)
        self.parser.add_argument('webport', type=int)
        self.parser.add_argument('apitoken', type=str)
        self.parser.add_argument('freqband', type=str)
        self.parser.add_argument('netid', type=int)
        self.parser.add_argument('duplicateperiod', type=int)
        self.parser.add_argument('fcrelaxed', type=bool)
        self.parser.add_argument('otaastart', type=int)
        self.parser.add_argument('otaaend', type=int)
        self.parser.add_argument('macqueuing', type=bool)
        self.parser.add_argument('macqueuelimit', type=int)
        self.parser.add_argument('adrenable', type=bool)
        self.parser.add_argument('adrmargin', type=float)
        self.parser.add_argument('adrcycletime', type=int)
        self.parser.add_argument('adrmessagetime', type=int)
        self.args = self.parser.parse_args()
                            
    @login_required
    @wait_for(timeout=TIMEOUT)
    @inlineCallbacks
    def get(self):
        """Method to handle system configuration GET requests"""
        try:
            config = yield Config.find(limit=1)
            # Return a 404 if not found.
            if config is None:
                abort(404, message={'error': "Could not get the system configuration"})
            returnValue(marshal(config, self.fields))
            
        except TimeoutError:
            log.error("REST API timeout retrieving application {appeui}",
                      appeui=euiString(appeui))

    @login_required
    @wait_for(timeout=TIMEOUT)
    @inlineCallbacks
    def put(self):
        """Method to handle system configuration PUT requests
        
        Args:
            appeui (int): Application EUI
        """
        try:
            config = yield Config.find(limit=1)
            # Return a 404 if not found.
            if config is None:
                abort(404, message={'error': "Could not get the system configuration"})
            
            # Filter args
            params = {k: v for k, v in self.args.iteritems() if v is not None}
            
            # Set the new attributes
            for a,v in params.items():
                setattr(config, a, v)

            # Validate the configuration
            (valid, message) = config.check()
            if not valid:
                abort(400, message=message)                
            
            # Reload the config
            (success, message) = self.server.reload(config)
            if not success:
                abort(500, message=message)                
            
            yield config.save()
            returnValue(({}, 200))

        except TimeoutError:
            log.error("REST API timeout retrieving application {appeui}",
                      appeui=euiString(appeui))


        except TimeoutError:
            log.error("REST API timeout retrieving application {appeui}",
                      appeui=euiString(appeui))
floranet/web/rest/application.py
class RestApplication(ApplicationResource):
    """RestApplication Resource class.
    
    Manages RESTAPI GET and PUT transactions for applications.
    
    """
    def __init__(self, **kwargs):
        super(RestApplication, self).__init__(**kwargs)
        
    @login_required
    @wait_for(timeout=TIMEOUT)
    @inlineCallbacks
    def get(self, appeui):
        """Method to handle application GET requests
        
        Args:
            appeui (int): Application EUI
        """
        try:
            a = yield Application.find(where=['appeui = ?', appeui], limit=1)
            # Return a 404 if not found.
            if a is None:
                abort(404, message={'error': "Application {} doesn't exist."
                                    .format(euiString(appeui))})
            
            data = marshal(a, self.fields)
            data['properties'] = yield self.getProperties(a)
            returnValue(data)
            
        except TimeoutError:
            log.error("REST API timeout retrieving application {appeui}",
                      appeui=euiString(appeui))

    @login_required
    @wait_for(timeout=TIMEOUT)
    @inlineCallbacks
    def put(self, appeui):
        """Method to handle application PUT requests
        
        Args:
            appeui (int): Application EUI
        """
        try:
            app = yield Application.find(where=['appeui = ?', appeui], limit=1)
            # Return a 404 if not found.
            if app is None:
                abort(404, message={'error': "Application {} doesn't exist."
                                    .format(euiString(appeui))})
            
            self.args['appinterface_id'] = self.args.pop('interface')
            current_appif = app.appinterface_id
            
            kwargs = {}
            for a,v in self.args.items():
                if v is not None and v != getattr(app, a):
                    kwargs[a] = v
                    setattr(app, a, v)
            (valid, message) = yield app.valid()
            if not valid:
                abort(400, message=message)
            
            # Update the model
            if kwargs:
                app.update(**kwargs)
            
            # Check the interface being removed.
            if current_appif != app.appinterface_id:
                yield interfaceManager.checkInterface(current_appif)
            returnValue(({}, 200))

        except TimeoutError:
            log.error("REST API timeout retrieving application {appeui}",
                      appeui=euiString(appeui))

    @login_required
    @wait_for(timeout=TIMEOUT)
    @inlineCallbacks
    def delete(self, appeui):
        """Method to handle application DELETE requests
        
        Args:
            appeui (int): Application EUI
        """
        
        try:
            # Check that no devices exist with this AppEUI.
            devices = yield Device.find(where=['appeui = ?', appeui], limit=1)
            if devices is not None:
                abort(400, message={'error': "Cannot delete - devices exist " \
                    "with Application EUI {}".format(euiString(appeui))})
            
            # Return a 404 if not found.            
            app = yield Application.find(where=['appeui = ?', appeui], limit=1)
            if app is None:
                abort(404, message={'error': "Application {} doesn't exist."
                                    .format(euiString(appeui))})
            yield app.delete()
            returnValue(({}, 200))

        except TimeoutError:
            log.error("REST API timeout retrieving application {appeui}",
                      appeui=euiString(appeui))
floranet/web/rest/appinterface.py
class RestAppInterface(AppInterfaceResource):
    """RestAppInterface Resource class
    
    Manages REST API GET PUT and DELETE transactions
    for application interfaces.
    
    """
        
    def __init__(self, **kwargs):
        super(RestAppInterface, self).__init__(**kwargs)

    @login_required
    @wait_for(timeout=TIMEOUT)
    @inlineCallbacks
    def get(self, appinterface_id):
        """Method to handle application GET requests
        
        Args:
            appinterface_id (int): Application Interface ID
        """
        try:
            interface = interfaceManager.getInterface(appinterface_id)
            # Return a 404 if not found.
            if interface is None:
                abort(404, message={'error': "Application interface id {} "
                            "doesn't exist.".format(str(appinterface_id))})
            
            # Return the interface's marshalled attributes
            returnValue(interface.marshal())
            yield
            
        except TimeoutError:
            log.error("REST API timeout retrieving application interface "
                      "{id}", id=appinterface_id)

    @login_required
    @wait_for(timeout=TIMEOUT)
    @inlineCallbacks
    def put(self, appinterface_id):
        """Method to handle AppInterface PUT requests
        
        Args:
            appinterface_id (int): Application Interface ID
        """
        try:
            interface = interfaceManager.getInterface(appinterface_id)
            # Return a 404 if not found.
            if interface is None:
                abort(404, message={'error': "Application interface id {} "
                                    "doesn't exist.".format(str(appinterface_id))})

            kwargs = {}
            for a,v in self.args.items():
                if hasattr(interface, a) and v is not None and v != getattr(interface, a):
                    kwargs[a] = v
                    setattr(interface, a, v)
            (valid, message) = yield interface.valid()
            if not valid:
                abort(400, message=message)
            
            # Update the interface via interfaceManager
            if kwargs:
                yield interfaceManager.updateInterface(interface)
            returnValue(({}, 200))
            
        except TimeoutError:
            log.error("REST API timeout retrieving application interface "
                      "{id}", id=appinterface_id)

    @login_required
    @wait_for(timeout=TIMEOUT)
    @inlineCallbacks
    def delete(self, appinterface_id):
        """Method to handle AppInterface DELETE requests
        
        Args:
            appinterface_id (int): Application inetrface id
        """
        
        try:
            # Check that no interfaces exist with this interface_id.
            interface = yield interfaceManager.getInterface(appinterface_id)
            if interface is None:
                abort(404, message={'error': "Interface {} doesn't exist."
                                    .format(str(appinterface_id))})

            # Delete the interface via interfaceManager
            yield interfaceManager.deleteInterface(interface)
            returnValue(({}, 200))

        except TimeoutError:
            log.error("REST API timeout retrieving application interface "
                      "{id}", id=appinterface_id)
floranet/web/rest/appproperty.py
class RestAppProperty(AppPropertyResource):
    """RestAppProperty Resource class.
    
    Manages RESTAPI GET and PUT transactions for applicaiton
    properties.
    
    """
    def __init__(self, **kwargs):
        super(RestAppProperty, self).__init__(**kwargs)
        
    @login_required
    @wait_for(timeout=TIMEOUT)
    @inlineCallbacks
    def get(self, appeui):
        """Method to handle application property GET requests
        
        Args:
            appeui (int): Application EUI
            port (int): Application property port
        """
        try:
            app = yield Application.find(where=['appeui = ?', appeui], limit=1)
            # Return a 404 if not found.
            if app is None:
                abort(404, message={'error': "Application {} doesn't exist."
                                    .format(euiString(appeui))})
            
            port = self.args['port']
            p = yield AppProperty.find(where=['application_id = ? AND port = ?',
                                             app.id, port])
            if p is None:
                 abort(404, message={'error': "Application property doesn't exist."})
            
            data = marshal(p, self.fields)
            returnValue(data)
            
        except TimeoutError:
            log.error("REST API timeout get request for application {appeui} "
                      "property {port}", appeui=euiString(appeui), port=port)

    @login_required
    @wait_for(timeout=TIMEOUT)
    @inlineCallbacks
    def put(self, appeui):
        """Method to handle application property PUT requests
        
        Args:
            appeui (int): Application EUI
        """
        try:
            app = yield Application.find(where=['appeui = ?', appeui], limit=1)
            # Return a 404 if not found.
            if app is None:
                abort(404, message={'error': "Application {} doesn't exist."
                                    .format(euiString(appeui))})
            port = self.args['port']
            
            p = yield AppProperty.find(where=['application_id = ? AND port = ?',
                                             app.id, port], limit=1)
            if p is None:
                 abort(404, message={'error': "Application property doesn't exist."})
            
            kwargs = {}
            for a,v in self.args.items():
                if a not in {'name', 'type', 'port'}:
                    continue
                if v is not None and v != getattr(p, a):
                    kwargs[a] = v
                    setattr(p, a, v)
            (valid, message) = yield p.valid()
            if not valid:
                abort(400, message=message)
            
            # Update the model
            if kwargs:
                p.update(**kwargs)
            returnValue(({}, 200))

        except TimeoutError:
            log.error("REST API timeout put request for application {appeui} "
                      "property {port}", appeui=euiString(appeui), port=port)

    @login_required
    @wait_for(timeout=TIMEOUT)
    @inlineCallbacks
    def delete(self, appeui):
        """Method to handle application property DELETE requests
        
        Args:
            appeui (int): application EUI
        """
        
        try:
            # Return a 404 if the application is not found.            
            app = yield Application.find(where=['appeui = ?', appeui], limit=1)
            if app is None:
                abort(404, message={'error': "Application {} doesn't exist."
                                    .format(euiString(appeui))})
            port = self.args['port']
            
            # Return a 404 if the property is not found. 
            p = yield AppProperty.find(where=['application_id = ? AND port = ?',
                                             app.id, port], limit=1)
            if p is None:
                 abort(404, message={'error': "Application property doesn't exist."})
            
            yield p.delete()
            returnValue(({}, 200))

        except TimeoutError:
            log.error("REST API timeout retrieving application {appeui} "
                      "property {port}", appeui=euiString(appeui), port=port)
floranet/web/rest/gateway.py
class RestGateways(GatewayResource):
    """ RestGateways Resource class.
    
    Manages REST API GET and POST transactions for reading multiple gateways,
    and creating gateways.
    
    """
    def __init__(self, **kwargs):
        super(RestGateways, self).__init__(**kwargs)
        
    @login_required
    @wait_for(timeout=TIMEOUT)
    @inlineCallbacks
    def get(self):
        """Method to get all gateways"""
        try:
            gateways = yield Gateway.all()
            if gateways is None:
                returnValue({})
            data = {}
            for i,g in enumerate(gateways):
                data[i] = marshal(g, self.fields)
            returnValue(data)
            
        except TimeoutError:
            # Exception returns 500 to client
            log.error("REST API timeout retrieving all gateways")


    @login_required
    @wait_for(timeout=TIMEOUT)
    @inlineCallbacks
    def post(self):
        """Method to create a gateway"""
        host = self.args['host']
        name = self.args['name']
        eui = self.args['eui']
        enabled = self.args['enabled']
        power = self.args['power']
        
        message = {}
        # Check for required args
        required = {'host', 'name', 'eui', 'enabled', 'power'}
        for r in required:
            if self.args[r] is None:
                message[r] = "Missing the {} parameter.".format(r)
        if message:
            abort(400, message=message)
            
        # Ensure we have a valid address
        try:
            ipaddress.ip_address(host)
        except (ipaddress.AddressValueError, ValueError):
            message = {'error': "Invalid IP address {} ".format(host)}
            abort(400, message=message)
            
        # Ensure we have a valid EUI
        if not isinstance(eui, (int, long)):
            message = {'error': "Invalid gateway EUI {} ".format(eui)}
            abort(400, message=message)

        # Check this gateway does not currently exist
        exists = yield Gateway.exists(where=['host = ?', host])
        if exists:
            message = {'error': "Gateway address {} ".format(host) + \
                                 "currently exists."}
            abort(400, message=message)

        # Check the EUI does not currently exist
        exists = yield Gateway.exists(where=['eui = ?', eui])
        if exists:
            message = {'error': "Gateway EUI {} ".format(eui) + \
                                 "currently exists."}
            abort(400, message=message)

        # Create and validate
        gateway = Gateway(host=host, eui=eui, name=name, enabled=enabled, power=power)
        (valid, message) = gateway.valid()
        if not valid:
            abort(400, message=message)

        try:
            g = yield gateway.save()
            if g is None:
                abort(500, message={'error': "Error saving the gateway."})
            # Add the new gateway to the server.
            self.server.lora.addGateway(g)
            location = self.restapi.api.prefix + '/gateway/' + str(host)
            returnValue(({}, 201, {'Location': location}))
        except TimeoutError:
            # Exception returns 500 to client
            log.error("REST API timeout for gateway POST request")
floranet/web/rest/appinterface.py
class RestAppInterfaces(AppInterfaceResource):
    """RestAppInterfaces Resource class.
    
    Manages REST API GET and POST transactions for reading multiple
    application interfaces and creating interfaces.
    
    """
    def __init__(self, **kwargs):
        super(RestAppInterfaces, self).__init__(**kwargs)
        
    @login_required
    @wait_for(timeout=TIMEOUT)
    @inlineCallbacks
    def get(self):
        """Method to get all application interfaces"""
        try:
            interfaces = interfaceManager.getAllInterfaces()
            if interfaces is None:
                returnValue({})
            marshal_fields = {
                'type': fields.String(attribute='__class__.__name__'),
                'id': fields.Integer(attribute='appinterface.id'),
                'name': fields.String
            }
            data = {}
            for i,interface in enumerate(interfaces):
                data[i] = marshal(interface, marshal_fields)
            returnValue(data)
            yield
            
        except TimeoutError:
            log.error("REST API timeout retrieving application interfaces")

    @login_required
    @wait_for(timeout=TIMEOUT)
    @inlineCallbacks
    def post(self):
        """Method to create an Application Interface"""
        klass = self.args['type']
        name = self.args['name']

        message = {}
        
        try:
            
            if klass == 'azure':        
                protocol = self.args['protocol']
                iothost = self.args['iothost']
                keyname = self.args['keyname']
                keyvalue = self.args['keyvalue']                
                poll_interval = self.args['pollinterval']
            
                # Check for required args
                required = {'type', 'name', 'protocol', 'iothost',
                            'keyname', 'keyvalue'}
                for r in required:
                    if self.args[r] is None:
                        message[r] = "Missing the {} parameter.".format(r)
                if protocol == 'https' and poll_interval is None:
                    message['poll_interval'] = "Missing the poll_interval parameter."
                if message:
                    abort(400, message=message)
                
                # Check protocol type
                if not protocol in {'https', 'mqtt'}:
                    message = "Unknown protocol type {}.".format(protocol)
                    abort(400, message=message)
                                
                # Create the interface
                if protocol == 'https':
                    interface = AzureIotHttps(name=name, iothost=iothost, keyname=keyname,
                                      keyvalue=keyvalue, poll_interval=poll_interval)
                elif protocol == 'mqtt':
                    interface = AzureIotMqtt(name=name, iothost=iothost, keyname=keyname,
                                      keyvalue=keyvalue)
            
            elif klass == 'filetext':
                file = self.args['file']
                required = {'type', 'name', 'file'}
                for r in required:
                    if self.args[r] is None:
                        message[r] = "Missing the {} parameter.".format(r)
                if message:
                    abort(400, message=message)
                
                # Create the interface
                interface = FileTextStore(name=name, file=file)
                
            elif klass == 'reflector':
                
                # Required args
                required = {'type', 'name'}
                for r in required:
                    if self.args[r] is None:
                        message[r] = "Missing the {} parameter.".format(r)
                if message:
                    abort(400, message=message)
                
                # Create the interface
                interface = Reflector(name=name)            
            
            else:
                message = {'error': "Unknown interface type"}
                abort(400, message=message)
            
            (valid, message) = yield interface.valid()
            if not valid:
                abort(400, message=message)

            # Add the new interface
            id = interfaceManager.createInterface(interface)
            location = self.restapi.api.prefix + '/interface/' + str(id)
            returnValue(({}, 201, {'Location': location}))
            
        except TimeoutError:
            # Exception returns 500 to client
            log.error("REST API timeout for application interface POST request")
floranet/web/rest/device.py
class RestDevice(DeviceResource):
    """RestDevice Resource class.
    
    Manages RESTAPI GET, PUT and DELETE transactions for a single device.
    """
    def __init__(self, **kwargs):
        super(RestDevice, self).__init__(**kwargs)

    @login_required
    @wait_for(timeout=TIMEOUT)
    @inlineCallbacks
    def get(self, deveui):
        """Method to handle device GET request
        
        Args:
            deveui (int): Device deveui
        """
        try:
            d = yield Device.find(where=['deveui = ?', deveui], limit=1)
            # Return a 404 if not found.
            if d is None:
               abort(404, message={'error': "Device {} doesn't exist".
                                   format(euiString(deveui))})
            returnValue(marshal(d, self.fields))

        except TimeoutError:
            log.error("REST API timeout for device GET request")

    @login_required
    @wait_for(timeout=TIMEOUT)
    @inlineCallbacks
    def put(self, deveui):
        """Method to handle device PUT requests
        
        Args:
            deveui (int): Device deveui
        """
        try:
            device = yield Device.find(where=['deveui = ?', deveui], limit=1)
            # Return a 404 if not found.
            if device is None:
                abort(404, message={'error': "Device {} doesn't exist".
                                    format(euiString(deveui))})
            
            kwargs = {}
            for a,v in self.args.items():
                if v is not None and v != getattr(device, a):
                    kwargs [a] = v
                    setattr(device, a, v)
            (valid, message) = yield device.valid(self.server)
            if not valid:
                abort(400, message=message)
            
            # Update the device with the new attributes
            if kwargs:
                device.update(**kwargs)
            returnValue(({}, 200))

        except TimeoutError:
            log.error("REST API timeout for device PUT request")

    @login_required
    @wait_for(timeout=TIMEOUT)
    @inlineCallbacks
    def delete(self, deveui):
        """Method to handle device DELETE requests
        
        Args:
            deveui (int): Device deveui
        """
        try:
            d = yield Device.find(where=['deveui = ?', deveui], limit=1)
            # Return a 404 if not found.
            if d is None:
                abort(404, message={'error': "Device {} doesn't exist".
                                    format(euiString(deveui))})
            deleted = yield d.delete()
            returnValue(({}, 200))

        except TimeoutError:
            log.error("REST API timeout for device DELETE request")
floranet/web/rest/gateway.py
class RestGateway(GatewayResource):
    """RestGateway Resource class.
    
    Manages RESTAPI GET and PUT transactions for gateways.
    
    """
    def __init__(self, **kwargs):
        super(RestGateway, self).__init__(**kwargs)
        
    @login_required
    @wait_for(timeout=TIMEOUT)
    @inlineCallbacks
    def get(self, host):
        """Method to handle gateway GET requests"""  
        try:
            g = yield Gateway.find(where=['host = ?', host], limit=1)
            # Return a 404 if not found.
            if g is None:
                abort(404, message={'error': "Gateway {} doesn't exist.".format(host)})
            returnValue(marshal(g, self.fields))
            
        except TimeoutError:
            log.error("REST API timeout retrieving gateway {host}",
                      host=host)

    @login_required
    @wait_for(timeout=TIMEOUT)
    @inlineCallbacks
    def put(self, host):
        """Method to handle gateway PUT requests
        
        Args:
            host (str): Gateway host address
        """
        try:
            gateway = yield Gateway.find(where=['host = ?', host], limit=1)
            # Return a 404 if not found.
            if gateway is None:
                abort(404, message={'error': "Gateway {} doesn't exist".format(host)})

            kwargs = {}
            for a,v in self.args.items():
                if v is not None and v != getattr(gateway, a):
                    kwargs[a] = v
                    setattr(gateway, a, v)
            (valid, message) = yield gateway.valid()
            if not valid:
                abort(400, message=message)
            
            # Update the gateway and server with the new attributes
            if kwargs:
                gateway.update(**kwargs)
                self.server.lora.updateGateway(host, gateway)
            returnValue(({}, 200))

        except TimeoutError:
            log.error("REST API timeout retrieving gateway {host}",
                      host=host)

    @login_required
    @wait_for(timeout=TIMEOUT)
    @inlineCallbacks
    def delete(self, host):
        """Method to handle gateway DELETE requests
        
        Args:
            host (str): Gateway host
        """
        try:
            g = yield Gateway.find(where=['host = ?', host], limit=1)
            # Return a 404 if not found.
            if g is None:
                abort(404, message={'error': "Gateway {} doesn't exist.".format(host)})
            deleted = yield g.delete()
            self.server.lora.deleteGateway(g)
            returnValue(({}, 200))

        except TimeoutError:
            log.error("REST API timeout retrieving gateway {host}",
                      host=host)
floranet/web/rest/application.py
class RestApplications(ApplicationResource):
    """RestApplications Resource class.
    
    Manages REST API GET and POST transactions for reading multiple
    applications, and creating applications.
    
    """
    def __init__(self, **kwargs):
        super(RestApplications, self).__init__(**kwargs)
        
    @login_required
    @wait_for(timeout=TIMEOUT)
    @inlineCallbacks
    def get(self):
        """Method to get all applications"""
        try:
            apps = yield Application.all()
            if apps is None:
                returnValue({})
            data = {}
            for i,a in enumerate(apps):
                data[i] = marshal(a, self.fields)
                data[i]['properties'] = yield self.getProperties(a)
            returnValue(data)
            
        except TimeoutError:
            log.error("REST API timeout retrieving application {appeui}",
                      appeui=euiString(appeui))


    @login_required
    @wait_for(timeout=TIMEOUT)
    @inlineCallbacks
    def post(self):
        """Method to create an application"""
        appeui = self.args['appeui']
        name = self.args['name']
        domain = self.args['domain']
        appnonce = self.args['appnonce']
        appkey = self.args['appkey']
        fport = self.args['fport']
        appinterface_id = self.args['appinterface_id']
        
        message = {}
        # Check for required args
        required = {'appeui', 'name', 'appnonce', 'appkey', 'fport'}
        for r in required:
            if self.args[r] is None:
                message[r] = "Missing the {} parameter.".format(r)
        if message:
            abort(400, message=message)
            
        # Check this application does not currently exist
        exists = yield Application.exists(where=['appeui = ?', appeui])
        if exists:
            message = {'error': "Application EUI {} currently exists"
                       .format(euiString(appeui))}
            abort(400, message=message)

        # Check the appkey doesn't exist
        exists = yield Application.exists(where=['appkey = ?', appkey])
        if exists:
            message = {'error': "Application key {} currently exists".
                       format(intHexString(appkey,16))}
            abort(400, message=message)

        # Create and validate
        app = Application(appeui=appeui, name=name, domain=domain,
                          appnonce=appnonce, appkey=appkey,
                          fport=fport, appinterface_id=appinterface_id)
        (valid, message) = yield app.valid()
        if not valid:
            abort(400, message=message)

        try:
            a = yield app.save()
            if a is None:
                abort(500, message={'error': "Error saving the application."})
            location = self.restapi.api.prefix + '/app/' + str(appeui)
            returnValue(({}, 201, {'Location': location}))
            
        except TimeoutError:
            # Exception returns 500 to client
            log.error("REST API timeout for application POST request")


Question: from flask_restful import Api, Resource
from floranet.web.rest.system import RestSystem
from floranet.web.rest.device import RestDevice, RestDevices
from floranet.web.rest.gateway import RestGateway, RestGateways
from floranet.web.rest.application import RestApplication, RestApplications
from floranet.web.rest.appinterface import RestAppInterface, RestAppInterfaces
from floranet.web.rest.appproperty import RestAppProperty, RestAppPropertys

class RestApi(object):
    """Defines the Floranet REST API.
    
    Attributes:
        api (Api): Flask RESTful API object
        version (float): API version
        server (NetServer): Network server
        
    """
    def __init__(self, app, server):
        
        # NetServer
        self.server = server
        
        # Set the version
        self.version = 1.0

        # Initialise the API
        self.api = Api(app, prefix='/api/v' + str(self.version))
        
        # Setup routing
        self.resources = {
            # System endpoint
            '/system':                      RestSystem,
            # Device endpoints
            '/device/<int:deveui>':         RestDevice,
            '/devices':                     RestDevices,
            # Application endpoints
            '/app/<int:appeui>':            RestApplication,
            '/apps':                        RestApplications,
            # Gateway endpoints
            '/gateway/<host>':              RestGateway,
            '/gateways':                    RestGateways,
            # Application interface endpoints
            '/interface/<appinterface_id>': RestAppInterface,
            '/interfaces':                  RestAppInterfaces,
            # Application property endpoints
            '/property/<int:appeui>':       RestAppProperty,

Answer:             '/propertys':                   RestAppPropertys