{"id":16514508,"url":"https://github.com/gregturn/todo","last_synced_at":"2026-06-13T14:04:53.587Z","repository":{"id":18020508,"uuid":"21050494","full_name":"gregturn/todo","owner":"gregturn","description":"Demo of Spring Data REST's metadata features","archived":false,"fork":false,"pushed_at":"2014-07-14T14:33:56.000Z","size":200,"stargazers_count":1,"open_issues_count":0,"forks_count":4,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-02-24T08:16:11.000Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/gregturn.png","metadata":{"files":{"readme":"README.adoc","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2014-06-20T19:54:14.000Z","updated_at":"2017-05-09T19:45:53.000Z","dependencies_parsed_at":"2022-08-19T11:41:48.784Z","dependency_job_id":null,"html_url":"https://github.com/gregturn/todo","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gregturn%2Ftodo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gregturn%2Ftodo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gregturn%2Ftodo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gregturn%2Ftodo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/gregturn","download_url":"https://codeload.github.com/gregturn/todo/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":241502915,"owners_count":19972956,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-11T16:12:47.903Z","updated_at":"2026-06-13T14:04:53.534Z","avatar_url":"https://github.com/gregturn.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"== What is this?\n\nThis project is a small demonstration of the metadata served up by Spring Data REST. The app is basically a list of TODOs that can be created, updated, altered, and deleted\nthrough REST.\n\n== How to run the app\n\n. Clone a copy of this repo.\n. Build with `mvn clean package`\n. Run it with `mvn spring-boot:run`\n\n== How to check things out\n\nAssuming you have started it up, try this:\n\n----\n$ curl localhost:8080\n{\n  \"_links\" : {\n    \"todos\" : {\n      \"href\" : \"http://localhost:8080/todos\"\n    },\n    \"profile\" : {\n      \"href\" : \"http://localhost:8080/alps\"\n    }\n  }\n}\n----\n\nBy visiting the root node, `/`, you can see there is a link to the list of TODOs with the **rel** of **todos**.\n\nNOTE: What is a **rel**? It stands for \"relationship\". In hypermedia, it's the logical name given to a URI, meant to define its relationship. It's kind of analogous to the domain name \nassociated with an IP address.By focusing on rels, you don't have to remember URIs, which are more complex and subject to change.\n\nThere is also a **profile** link to `/alps` which we'll see quickly.\n\nBut first, let's look at what is behind **todos**.\n\n----\n$ curl localhost:8080/todos\n{ }\n----\n\nNo TODOs apparently! So what if we want to create one? We have to put together a POST combined with some data elements. But what ARE the data elements? We could cheat by peeking at\nthe code in this project. But if this was deployed somewhere on the internet, we might not have access to that. Instead, we can tap the app's ALPS metadata and figure it out directly.\n\nLet's do some exploration of the ALPS link.\n\n----\n$ curl http://localhost:8080/alps\n{\n  \"version\" : \"1.0\",\n  \"descriptors\" : [ {\n    \"href\" : \"http://localhost:8080/alps//todos\",\n    \"name\" : \"todos\"\n  } ]\n}\n$ curl http://localhost:8080/alps//todos\n{\n  \"version\" : \"1.0\",\n  \"descriptors\" : [ {\n    \"id\" : \"todo-representation\",\n    \"descriptors\" : [ {\n      \"name\" : \"description\",\n      \"doc\" : {\n        \"value\" : \"Details about the TODO item\",\n        \"format\" : \"TEXT\"\n      },\n      \"type\" : \"SEMANTIC\"\n    }, {\n      \"name\" : \"title\",\n      \"doc\" : {\n        \"value\" : \"Title for the TODO item\",\n        \"format\" : \"TEXT\"\n      },\n      \"type\" : \"SEMANTIC\"\n    }, {\n      \"name\" : \"id\",\n      \"type\" : \"SEMANTIC\"\n    }, {\n      \"name\" : \"completed\",\n      \"doc\" : {\n        \"value\" : \"Is it completed?\",\n        \"format\" : \"TEXT\"\n      },\n      \"type\" : \"SEMANTIC\"\n    } ]\n  }, {\n    \"id\" : \"create-todos\",\n    \"name\" : \"todos\",\n    \"type\" : \"UNSAFE\",\n    \"rt\" : \"#todo-representation\"\n  }, {\n    \"id\" : \"get-todos\",\n    \"name\" : \"todos\",\n    \"type\" : \"SAFE\",\n    \"rt\" : \"#todo-representation\"\n  }, {\n    \"id\" : \"patch-todo\",\n    \"name\" : \"todo\",\n    \"type\" : \"UNSAFE\",\n    \"rt\" : \"#todo-representation\"\n  }, {\n    \"id\" : \"delete-todo\",\n    \"name\" : \"todo\",\n    \"type\" : \"IDEMPOTENT\",\n    \"rt\" : \"#todo-representation\"\n  }, {\n    \"id\" : \"update-todo\",\n    \"name\" : \"todo\",\n    \"type\" : \"IDEMPOTENT\",\n    \"rt\" : \"#todo-representation\"\n  }, {\n    \"id\" : \"get-todo\",\n    \"name\" : \"todo\",\n    \"type\" : \"SAFE\",\n    \"rt\" : \"#todo-representation\"\n  } ]\n}\n----\n\nWhat the heck is all this? If we read this document bit-by-bit, it explains itself.\n\n----\n\"id\" : \"todo-representation\"\n----\n\nIt identifies itself as a **todo-representation**. If we look at the descriptors array (inside the outer descriptors entry):\n\n----\n\"descriptors\" : [ {\n  \"name\" : \"description\",\n  \"doc\" : {\n    \"value\" : \"Details about the TODO item\",\n    \"format\" : \"TEXT\"\n  },\n  \"type\" : \"SEMANTIC\"\n}, {\n  \"name\" : \"title\",\n  \"doc\" : {\n    \"value\" : \"Title for the TODO item\",\n    \"format\" : \"TEXT\"\n  },\n  \"type\" : \"SEMANTIC\"\n}, {\n  \"name\" : \"id\",\n  \"type\" : \"SEMANTIC\"\n}, {\n  \"name\" : \"completed\",\n  \"doc\" : {\n    \"value\" : \"Is it completed?\",\n    \"format\" : \"TEXT\"\n  },\n  \"type\" : \"SEMANTIC\"\n} ]\n----\n\nWe see:\n\n[options=\"header\"]\n|====\n| Name | Description \n| description | Details about the TODO item\n| title | Title for the TODO item\n| id | |\n| completed | Is it completed?\n|====\n{empty}\n\nWe can see the names and a description of what they do. Each one is marked as TEXT, meaning we can feed it a text value. But Spring Data REST will use Spring MVC's\nmessage converters to convert it to the right value when populating a back end POJO.\n\nNOTE: **id** doesn't have any details and we don't need them, because in general, the back end will handle creating a new id.\n\nSo armed with this information, we can craft a POST.\n\n----\n$ curl -X POST -H \"Content-Type:application/json\" -d '{\"title\": \"Write a README for todo project\", \"description\": \"Write a detailed doc introducing readers to Spring Data REST + ALPS\", \"completed\": \"false\"}' -i localhost:8080/todos\nHTTP/1.1 201 Created\nServer: Apache-Coyote/1.1\nX-Application-Context: application\nLocation: http://localhost:8080/todos/1\nContent-Length: 0\nDate: Tue, 24 Jun 2014 21:20:57 GMT\n----\n\nIt worked! (See the 200 status code?) \n\nFirst, let's look at all the inputs used to create this entity:\n\n[options=\"header\"]\n|====\n| Argument | Details\n| -X POST | This is a POST. curl defaults to GET\n| -H \"Content-Type:application/json\" | the payload being sent is JSON\n| -d \\'{ json content....}' | the data, i.e. payload, being sent\n| -i | print out all headers sent back\n| localhost:8080/todos | the URI where we can interact with the collection of TODOs\n|====\n{empty}\n\nNow let's examine the outputs. It replied by giving us a **Location** header entry of http://localhost:8080/todos/1, the location of the newly created resource. We can check it\nout:\n\n----\n$ curl localhost:8080/todos/1\n{\n  \"title\" : \"Write a README for todo project\",\n  \"description\" : \"Write a detailed doc introducing readers to Spring Data REST + ALPS\",\n  \"completed\" : false,\n  \"_links\" : {\n    \"self\" : {\n      \"href\" : \"http://localhost:8080/todos/1\"\n    }\n  }\n}\n----\n\nWe can definitely interact with it.\n\n----\n$ curl -X PATCH -H \"Content-Type:application/json\" -d '{\"completed\": \"true\"}' -i localhost:8080/todos/1\nHTTP/1.1 204 No Content\nServer: Apache-Coyote/1.1\nX-Application-Context: application\nDate: Tue, 24 Jun 2014 21:23:43 GMT\n\n$ curl localhost:8080/todos/1\n{\n  \"title\" : \"Write a README for todo project\",\n  \"description\" : \"Write a detailed doc introducing readers to Spring Data REST + ALPS\",\n  \"completed\" : true,\n  \"_links\" : {\n    \"self\" : {\n      \"href\" : \"http://localhost:8080/todos/1\"\n    }\n  }\n}\n----\n\nThere are more operations. In fact, it already told us what operations were available.\n\n----\n{\n  \"id\" : \"create-todos\",\n  \"name\" : \"todos\",\n  \"type\" : \"UNSAFE\",\n  \"rt\" : \"#todo-representation\"\n}, {\n  \"id\" : \"get-todos\",\n  \"name\" : \"todos\",\n  \"type\" : \"SAFE\",\n  \"rt\" : \"#todo-representation\"\n}, {\n  \"id\" : \"patch-todo\",\n  \"name\" : \"todo\",\n  \"type\" : \"UNSAFE\",\n  \"rt\" : \"#todo-representation\"\n}, {\n  \"id\" : \"delete-todo\",\n  \"name\" : \"todo\",\n  \"type\" : \"IDEMPOTENT\",\n  \"rt\" : \"#todo-representation\"\n}, {\n  \"id\" : \"update-todo\",\n  \"name\" : \"todo\",\n  \"type\" : \"IDEMPOTENT\",\n  \"rt\" : \"#todo-representation\"\n}, {\n  \"id\" : \"get-todo\",\n  \"name\" : \"todo\",\n  \"type\" : \"SAFE\",\n  \"rt\" : \"#todo-representation\"\n}\n----\n\nWe have support for:\n\n* create (HTTP POST)\n* get all (HTTP GET)\n* patch one (HTTP PATCH)\n* delete one (HTTP DELETE)\n* update one (HTTP PUT)\n* get one (HTTP GET)\n\nIt describes how these operations alter the system:\n\n* UNSAFE - can cause changes to the back end\n* SAFE - will not alter the state of the back end\n* IDEMPOTENT - repeat the same operation, get the same output. Implicitly unsafe because it does alter the back end.\n\nThis is just a preliminary glimpse of the metadata. Look for more details at http://alps.io.\n\nIf we peek at the domain object:\n\n[source,java]\n----\n@Entity\npublic class Todo {\n\n    @Id\n    @GeneratedValue(strategy = GenerationType.AUTO)\n    private long id;\n\n    @Description(\"Title for the TODO item\")\n    private String title;\n\n    @Description(\"Details about the TODO item\")\n    private String description;\n\n    @Description(\"Is it completed?\")\n    private boolean completed;\n...\n----\n\nWe can see the back end. \n\nGiven this information, we can build a front end however we want. \n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgregturn%2Ftodo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgregturn%2Ftodo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgregturn%2Ftodo/lists"}