Skip to content

Latest commit

 

History

History
213 lines (148 loc) · 7.23 KB

002-response-filters.md

File metadata and controls

213 lines (148 loc) · 7.23 KB

Response Filters

  • Implementation Owner: @eldadfux
  • Start Date: 16-12-2020
  • Target Date: 20-12-2020
  • Appwrite Issue: None

Summary

Add a the option to enable different response filters to the Appwrite response object. This filters will act as a middleware and allow us to manipulate the response code before sending it to the HTTP client.

Problem Statement (Step 1)

What problem are you trying to solve?

While in beta we are changing some of our APIs response formats.

What is the context or background in which this problem exists?

Appwrite is still in beta, and as we progress with the project development, we need to break some existing APIs response format from time to time. Initially, this wasn't an issue because the product is still in beta, but it has become a requirement with growing usage by the developers' community.

Once the proposal is implemented, how will the system change?

A new API option will allow devs to keep backward compatibility.

Design proposal (Step 2)

We should introduce a new env variable and HTTP header that will allow to change the response format. This variable & header will allow devs to enable a response object filter which will parse the Appwrite response to continue support for old Appwrite response formats without breaking or changing our developers apps when upgrading an appwrite version.

New ENV Var (_APP_SYSTEM_RESPONSE_FORMAT)

Should be checked on server startup. If the value is not empty we should init the relevant filter. If not filter is avaliable we should throw an error using the Console::error method and stop the proccess with exit(1);. Make sure to add the new var to Appwrite docs page: https://appwrite.io/docs/environment-variables#system-settings

New Optional Header (X-Appwrite-Response-Format)

Should be checked on each request (general.php / ->init() can be a good location). If the value is not empty we should init the relevant filter. If not filter is avaliable we should throw an 404 HTTP error with relevant error message.

Response Filters and Adapters

Added 2 new method to Appwrite response class (src/Appwrite/Utopia/Response.php) called static function setFilter(Filter $filter): self and static function getFilter(): Filter. The new method will set and get the new filter to a static property called self::$filter. Add a method called static function isFilter(): bool to check if a filter has been set or is null.

Create a Filter Interface

Create a new Appwrite\Utopia\Response\Filter class (src/Appwrite/Utopia/Response/Filter.php). The interface should decalre an implemntation for the parse(array $content): array method. Each new filter will implement the interface and use the parse method to manipulate incoming data. Create our first filter (Appwrite\Utopia\Response\Filters\V06) to accept current version (v0.7) data and convert it to v0.6 data. For that we'll need to run both instances of Appwrite to manually convert data structures.

Run the Filter

Add a piece of code in the response output stage, that will check if a filter is set and will execute his parse method and alter the response output before it is returned to the client. A good location for this method to run might be: https://github.com/appwrite/appwrite/blob/0.7.x/src/Appwrite/Utopia/Response.php#L296

Examples

/v1/locale/countries?project=console

0.6.2

{
"AF": "Afghanistan",
"AL": "Albania",
"DZ": "Algeria",
"AD": "Andorra",
"AO": "Angola",
"AG": "Antigua and Barbuda",
"AR": "Argentina",
"AM": "Armenia",
"AU": "Australia",
"AT": "Austria",
...

0.7.0

{
  "sum": 194,
  "countries": [
    {
      "name": "Afghanistan",
      "code": "AF"
    },
    {
      "name": "Albania",
      "code": "AL"
    },
...

/v1/teams?project=console

0.6.2

{
  "sum": 25,
  "teams": [
    {
      "$id": "5e5ea5d40c1b1",
      "name": "New Project",
      "dateCreated": 1583261140,
      "sum": 1
    },
    {
      "$id": "5e6081aca576c",
      "name": "Project Y",
      "dateCreated": 1583382956,
      "sum": 1
    },
...

0.7.0

{
	"sum": 1,
	"teams": [{
		"$id": "5fdca0529143a",
		"$collection": "teams",
		"$permissions": {
			"read": ["team:{self}"],
			"write": ["team:{self}\/owner"]
		},
		"name": "Project 1",
		"sum": 1,
		"dateCreated": 1608294482
	}
...

In case the parse method is missing some data to properly construct the response, just use a reasonable default value of the same type and structure.

Tests

Add unit-tests for the new methods in the Response class, and for our first filter using mock data as input.

Prior art

Unresolved questions

In the future we might want to do something similar to the Request input in case we'll introduce any API signature breaking changes.

Future possibilities

None.