{"id":18336570,"url":"https://github.com/kumuluz/kumuluzee-rest","last_synced_at":"2025-04-06T04:35:43.810Z","repository":{"id":21668922,"uuid":"93623846","full_name":"kumuluz/kumuluzee-rest","owner":"kumuluz","description":"KumuluzEE REST extension for implementation of common, advanced and flexible REST API functionalities and patterns as microservices.","archived":false,"fork":false,"pushed_at":"2024-03-15T17:50:40.000Z","size":321,"stargazers_count":8,"open_issues_count":5,"forks_count":4,"subscribers_count":14,"default_branch":"master","last_synced_at":"2024-04-16T20:13:07.206Z","etag":null,"topics":["cloud-native","java","javaee","kumuluzee","microservices","patterns","rest"],"latest_commit_sha":null,"homepage":"https://ee.kumuluz.com","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kumuluz.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2017-06-07T10:38:38.000Z","updated_at":"2024-03-31T14:19:33.000Z","dependencies_parsed_at":"2024-03-15T18:51:51.783Z","dependency_job_id":"27939f08-8620-4bd1-b49a-6be40fbd1c4c","html_url":"https://github.com/kumuluz/kumuluzee-rest","commit_stats":null,"previous_names":[],"tags_count":13,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kumuluz%2Fkumuluzee-rest","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kumuluz%2Fkumuluzee-rest/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kumuluz%2Fkumuluzee-rest/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kumuluz%2Fkumuluzee-rest/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kumuluz","download_url":"https://codeload.github.com/kumuluz/kumuluzee-rest/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":223238131,"owners_count":17111362,"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":["cloud-native","java","javaee","kumuluzee","microservices","patterns","rest"],"created_at":"2024-11-05T20:08:17.501Z","updated_at":"2024-11-05T20:08:18.193Z","avatar_url":"https://github.com/kumuluz.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# KumuluzEE REST\n[![KumuluzEE CI](https://github.com/kumuluz/kumuluzee-rest/actions/workflows/kumuluzee-ci.yml/badge.svg)](https://github.com/kumuluz/kumuluzee-rest/actions/workflows/kumuluzee-ci.yml)\n\n\u003e KumuluzEE REST greatly simplifies implementation of common REST patterns, such as paging, sorting and filtering. In conjunction with JPA it supports automated querying and retrieving of entities.\n\nKumuluzEE REST provides support for common patterns and best practices for REST services using JAX-RS 2. It greatly simplifies the implementation of paging, sorting and filtering of REST resources and introduces a common syntax. It provides support for automatic parsing of query parameters. In conjunction with JPA it provides support for automated retrieving of entities, parsing query parameters from the URIs and using these query parameters when building JPA queries. \n\n## Usage\n\nYou can enable the KumuluzEE REST by adding the following dependency:\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003ecom.kumuluz.ee.rest\u003c/groupId\u003e\n    \u003cartifactId\u003ekumuluzee-rest-core\u003c/artifactId\u003e\n    \u003cversion\u003e${kumuluzee-rest.version}\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n### JAX-RS implementation\n\nWhen implementing REST services the URI context information is needed. The URI can be obtained by adding `UriInfo` context to selected Resource:\n\n```java\n@RequestScoped\n@Consumes(MediaType.APPLICATION_JSON)\n@Produces(MediaType.APPLICATION_JSON)\n@Path(\"customers\")\npublic class CustomerResource {\n\n    @Context\n    protected UriInfo uriInfo;\n\n    @Inject\n    private CustomerService customerBean;\n    \n    ...\n    \n}\n```\n\nUsing the URI context information the query parameters can be constructed by using the `QueryParameters` class:\n\n```java\n@GET\npublic Response getAllCustomers() {\n    QueryParameters query = QueryParameters.query(uriInfo.getRequestUri().getQuery()).build();\n    List\u003cCustomer\u003e customers = customerBean.getCustomers(query);\n    return Response.ok(customers).build();\n}\n```\n\n### CDI implementation\n\nAfter parsing the query parameters they can be used to query or count entities using the `JPAUtils` class:\n\n\n```java\n@RequestScoped\npublic class CustomerService {\n\n    @PersistenceContext\n    private EntityManager em;\n\n    public List\u003cCustomer\u003e getCustomers(QueryParameters query) {\n        List\u003cCustomer\u003e customers = JPAUtils.queryEntities(em, Customer.class, query);\n        return customers;\n    }\n\n\n    public Long getCustomerCount(QueryParameters query) {\n        Long count = JPAUtils.queryEntitiesCount(em, Customer.class, query);\n        return count;\n    }\n}\n```\nWe can also build the query using `QueryStringDefaults` class which applies the following defaults (if not specified by the client):\n- max results: maximum number of entities that can be returned\n- limit: default number of entities returned\n- offset: default offset\n\n```java\n@Context\nprivate UriInfo uriInfo;\n\n@Inject\nprivate QueryStringDefaults qsd;\n\n@Inject\nprivate EntityManager em;\n\n@GET\npublic Response getList() {\n    QueryParameters query = qsd.builder().queryEncoded(uriInfo.getRequestUri().getRawQuery()).build();\n\n    List\u003cCustomer\u003e allCustomers = JPAUtils.queryEntities(em, Customer.class, query);\n    Long allCustomersCount = JPAUtils.queryEntitiesCount(em, Customer.class, query);\n\n    return Response.ok(allCustomers).header(\"X-Total-Count\", allCustomersCount).build();\n}\n```\nDefaults can either be constructed:\n```java\nprivate QueryStringDefaults qsd = new QueryStringDefaults().maxLimit(100).defaultLimit(20).defaultOffset(0);\n```\nor injected with CDI and a producer class:\n```java\npublic class RestProducer {\n\n    @Produces\n    @ApplicationScoped\n    public QueryStringDefaults getQueryStringDefaults() {\n        return new QueryStringDefaults()\n                .maxLimit(100)\n                .defaultLimit(20)\n                .defaultOffset(0);\n    }\n}\n```\n\n### Examples\n\nAfter the implementation of Rest resources and CDI beans, the query parameters can be used for pagination, sorting and filtering of JPA entities.\n\n#### Pagination\n\nThe offset parameter indicates the position of the first entity which should be returned and the limit parameter indicates the number of entities.\n\n```\nGET /v1/customers?offset=10\nGET /v1/customers?limit=5\nGET /v1/customers?offset=10\u0026limit=5\n```\nWe must also return the number of all entities so the client can display correct number of page buttons. One way of doing this is with a custom HTTP header.\n```java\n...\nQueryParameters query = qsd.builder().queryEncoded(uriInfo.getRequestUri().getRawQuery()).build();\n\nList\u003cCustomer\u003e allCustomers = JPAUtils.queryEntities(em, Customer.class, query);\nLong allCustomersCount = JPAUtils.queryEntitiesCount(em, Customer.class, query);\n\nreturn Response.ok(allCustomers).header(\"X-Total-Count\", allCustomersCount).build();\n```\n\nWith very large datasets counting all filtered records can be very slow. That is why it's possible to specify the `count` parameter, which can than be used to decide whether or not to perform the count.\n\n```\nGET /v1/customers?offset=10\nGET /v1/customers?count=true\u0026limit=5\nGET /v1/customers?count=false\u0026offset=10\u0026limit=5\n```\n\nParameter `count` is set to `true`by default.\n\n__NOTE__: When using `JpaUtils.getQueried` counting is performed (or not performed) automatically, depending on the value of `count`.\n\n#### Sorting\n\nSorting of entities can be specified by providing the field and direction.\n\n```\nGET v1/customers?order=id DESC\nGET v1/customers?order=lastName ASC\n```\nWe can chain several sorts together.\n```\nGET v1/customers?order=email ASC,lastname DESC\n```\nAfter the last user specified sort, order by unique ID is automatically appended at the end of the query for deterministic sorting of same-valued columns.\n\n#### Filtering\n\nThe entities can be filtered by using multiple operations:\n\n* EQ | Equals\n* EQIC | Case-insensitive equals\n* NEQ | Not equal\n* NEQIC | Case-insensitive not equal\n* LIKE | Pattern matching (% replaces characters, _ replaces a single character)\n* LIKEIC | Case-insensitive pattern matching (% replaces characters, _ replaces a single character)\n* NLIKE | Negated pattern matching (% replaces characters, _ replaces a single character)\n* NLIKEIC | Case-insensitive negated pattern matching (% replaces characters, _ replaces a single character)\n* GT | Greater than\n* GTE | Greater than or equal\n* LT | Lower than\n* LTE | Lower than or equal\n* IN | In set\n* INIC | Case-insensitive in set\n* NIN | Not in set\n* NINIC | Case-insensitive not in set\n* ISNULL | Null\n* ISNOTNULL | Not null\n\n```\nGET v1/customers?filter=id:EQ:1\nGET v1/customers?filter=lastName:NEQIC:'doe'\nGET v1/customers?filter=lastName:LIKE:H%\nGET v1/customers?filter=age:GT:10\nGET v1/customers?filter=id:IN:[1,2,3]\nGET v1/customers?filter=lastName:ISNULL\nGET v1/customers?filter=lastName:ISNOTNULL\n\nGET v1/customers?filter=age:GT:10 id:IN:[1,2,3] lastName:ISNOTNULL\n```\n\nBy default, filters are chained together with an `AND` operator (represented by an empty space). \n\n#### Complex queries\nIt is possible to write more complex queries by using `OR` and `AND` operators and by grouping them together with \n__parentheses__. Both `OR` and `AND` operator can be written in several different ways:\n* OR | `,`, `or`\n* AND | ` `, `;`, `and`\n\n__NOTE__: `AND` has precedence over `OR`, __parantheses__ have precedence over `AND`\n\n```\nGET v1/customers?filter=age:GT:10 id:IN:[1,2,3] lastName:ISNOTNULL, firstName:LIKE:'B%'\nGET v1/customers?filter=age:GT:10 id:IN:[1,2,3] and lastName:ISNOTNULL or firstName:LIKE:'B%'\nGET v1/customers?filter=age:GT:10 id:IN:[1,2,3]; lastName:ISNOTNULL, firstName:LIKE:'B%'\nGET v1/customers?filter=age:GT:10 id:IN:[1,2,3] and (lastName:ISNOTNULL or firstName:LIKE:'B%')\n```\n\nThere are some special cases:\n- If we want to use `LIKE` filter and query values that include a percent sign, it needs to be URL encoded (%25).\n- Dates and instants must be in ISO-8601 format, `+` sign must be URL encoded (%2B). Single quotes for value are required.\n```\nGET v1/customers?where=firstName:LIKE:'%somestring%25doe'\nGET v1/customers?where=createdAt:GT:'2017-06-12T11:57:00%2B00:00'\n```\n\n#### Partial responses\nWe can select which fields we want returned in the resulting JSON with the `fields` parameter.\n```\nGET v1/customers?fields=firstName,lastName\n```\n\n#### Traversing OneToMany and ManyToOne relations\nWe can traverse entity attributes similar to JPQL style. Let's say each customer has many `cars` and we want to find owners of specific brand:\n```\nGET v1/customers?filter=cars.brand:EQ:bmw\n```\nIt also works in reverse:\n```\nGET v1/cars?filter=customer.firstName:EQ:John\n```\nThis would find all `Cars` that have an owner named `John`.\n\n#### Combine pagination, sorting and filtering\n\nPagination, sorting and filtering of entities can be combined by separating them with \u0026.\n\n```\nGET /v1/customers?offset=10\u0026limit=5\u0026order=id DESC\u0026filter=age:GT:10 id:IN:[1,2,3] lastName:ISNOTNULL\n```\n\n#### Custom field mapping\nLibrary supports custom property mappings set on entity properties in order to detach API schema from database model. This functionality turns out to be useful when persistence changes are needed and API needs to stay backwards compatible.\n```java\n@RestMapping(\"experience\")\nprivate Integer years;\n```\nIn this case both of the following queries will return same result:\n```\nGET /v1/customers?filter=years:EQ:5\nGET /v1/customers?filter=experience:EQ:5\n```\nLibrary supports combination of properties with child properties using OneToOne mapping:\n```java\n@RestMapping(\"emailAndCurrentPosition\")\nprivate String email;\n\n@RestMapping(value = \"emailAndCurrentPosition\", toChildField = \"currentPosition\")\n@OneToOne(mappedBy = \"user\")\nprivate UserCareer career;\n```\nwhere result will return only email and current position within career relation.\n\n#### Ignoring REST fields\nBy default, REST library returns field not found exception for non-existing entity fields. For some cases we may want to omit field check with annotation:\n ```java\n@Entity\n@RestIgnore(\"userIgnoredField\")\n@Table(name = \"users\")\npublic class User {\n    ...\n}\n ```\n\n\n#### Additional criteria query manipulation\nPredicate constructed from query parameters can be further changed. For example:\n\n```java\nList\u003cCustomer\u003e allCustomers = JPAUtils.queryEntities(em, Customer.class,\n(p, cb, r) -\u003e cb.and(p, cb.equal(r.get(\"firstName\"), \"John\")));\n```\nWhere:\n- `p` is existing Predicate\n- `cb` is CriteriaBuilder and\n- `r` is Root \n\nWith this, programmer has the full power of Criteria API to further manipulate the query.\n\n## Building\n\nEnsure you have JDK 8 (or newer), Maven 3.2.1 (or newer) and Git installed\n\n```bash\n    java -version\n    mvn -version\n    git --version\n```\n\nFirst clone the repository:\n\n```bash\n    git clone https://github.com/kumuluz/kumuluzee-rest.git\n    cd kumuluzee-rest\n```\n    \nTo build run:\n\n```bash\n    mvn install\n```\n\nThis will build all modules and run the testsuite. \n    \nOnce completed you will find the build archives in the modules respected `target` folder and local `.m2` repository.\n\n## Changelog\n\nRecent changes can be viewed on Github on the [Releases Page](https://github.com/kumuluz/kumuluzee-rest/releases)\n\n## Contribute\n\nSee the [contributing docs](https://github.com/kumuluz/kumuluzee-rest/blob/master/CONTRIBUTING.md)\n\nWhen submitting an issue, please follow the \n[guidelines](https://github.com/kumuluz/kumuluzee-rest/blob/master/CONTRIBUTING.md#bugs).\n\nWhen submitting a bugfix, write a test that exposes the bug and fails before applying your fix. Submit the test \nalongside the fix.\n\nWhen submitting a new feature, add tests that cover the feature.\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkumuluz%2Fkumuluzee-rest","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkumuluz%2Fkumuluzee-rest","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkumuluz%2Fkumuluzee-rest/lists"}