Exercises¶
Background¶
Happy Meatings AB ('HM') arranges meetings with charcuterie themes, but of course they also offer vegetarian and vegan alternatives. In addition to their master system Lime, they also have some other systems for various tasks. Their ambition is to have as much information in Lime as possible and eventually delete the other systems that were built in-house by former employees. Until then, some simple integrations using custom endpoints and custom lime objects will be used to keep the other systems up to date. Unfortunately, those systems cannot be modified to use Lime's standard REST API - all source code was lost in an unfortunate 'mishap' (an ex-employee and his laptop 'fell' in the mincer on his last day at work). They didn't use Git - that was a mistake.
Solution¶
- Create a solution for HM.
Endpoints¶
- Generate a custom endpoint and test that the generated example works. The url for counting companies should be similar to this:
https://localhost/APP_NAME/solution-happy-meatings/count/?limetype=company
🤔 Could the args
variable be moved into the LimeobjectCounter
class instead?
- Create a custom endpoint that takes a PUT request with the deal id in the url and increases the deal's value with an amount passed as a query parameter in the URL and return the new deal value in the response..
https://localhost/APP_NAME/solution-happy-meatings/deal/1023/?increase=10000
🆔 Having the id of the resource in the url (in this case the deal id) is good REST practice. Specify it and datatype when registering the endpoint api.add_resource(Deal, '/deal/<int:iddeal>/')
☯️ In this case we're using a combination of the id in the url and query params (increase). Define the function as def put(self, args, iddeal):
to get them both.
📠 It's often a good idea to return stuff as JSON, rather than just a simple value. If you import Flask with import flask
you can easily create a JSON response from a simple object or dict.
python
return flask.jsonify({
'value': deal.properties.value.value,
'message': 'Value was updated for deal {}'.format(deal.id)
})
- Create a custom endpoint that takes a POST request with a JSON payload that creates a company and a person and attach them to each other.
https://localhost/APP_NAME/solution-happy-meatings/companywithperson/
The payload should look like this:
json
{
"company": {
"name": "Carlito's pizzeria",
"city": "Miami"
},
"person": {
"firstname": "Carlito",
"lastname": "Calzone",
"email": "[email protected]",
"position": "Pizzaman"
}
}
- Create a custom endpoint that takes a DELETE request with the email as a query parameter so that HM can delete persons they don't like by email address.
https://localhost/APP_NAME/solution-happy-meatings/person/[email protected]
🔫 After you delete()
an object, you must add it to the Unit of Work, just as you would if you created or updated the object.
- Create a custom endpoint that GETs all the deals with the requested value or more. Use query objects.
https://localhost/APP_NAME/solution-happy-meatings/deals/?value=4000
🐫 The result you get from the query objects is well formed and could be directly returned with flask.jsonify()
Custom Lime Objects¶
-
Generate a custom lime object and test that the generated example works.
-
In before_update(), create a history note if the person is new, if the person already exists in the database, do nothing.
👶 self.is_new
can be used to check if the lime object is new.
🚀 Don't try to commit the Unit of Work (uow
) yourself, it will be automatically commited later on (including the extra stuff that you've added to uow).
- In before_update(), validate that the person's firstname is not "Nisse" and that the e-mail is not empty. Throw custom exceptions if the person is invalid.
🖥️ There's currently no good way to show the exception in neither of the Lime clients. However, the exception and stacktrace will be logged in the server log (if Lime Webserver is running as a service). It's in C:\ProgramData\Lundalogik\LIME Pro Server\Web Server\logs\webserver-errors.log
on Windows.
💥 It's good practise to inherit the base Exception class in a new Error class. Then inherit the Error class to create specific errors that you can try/catch on.
class Error(Exception):
pass
class InvalidNameError(Error):
pass
class InvalidEmailError(Error):
pass
🔙 If an unhandled exception occurs, the transaction will not be commited.
🎅 Try Endpoint exercise 4 with "Nisse" as the name instead of "Carlito" in the payload.
- All deals with a value of more than 10000 should only be visible and editable by the group "Sales managers" (this group is not in the Core database by default).
👯 You can find the correct group by importing from lime_authentication.users import Groups
and use Groups(self.application.connection).get_by_name('sales_managers')
- Create a pick_winner_and_prize() function on a custom Company limeobject that randomly selects and returns winner among the company's persons and a prize from a constant prizes list. You can use limefu to test the function.
PRIZES = ['Lollipop', 'Chocolate', 'Imprisonment']
🥂 A Python function can return several values, example:
def pick_winner_and_prize(self):
winner = ...
prize = ...
return winner, prize
Call it with:
winner, prize = company.pick_winner_and_prize()
Web Component¶
-
Generate a web component and test that the generated example works.
-
Create a button that communicates with a custom endpoint using the
pick_winner_and_prize()
function to draw a winner from a company.