diff --git a/.classpath b/.classpath
deleted file mode 100755
index 1641ef18..00000000
--- a/.classpath
+++ /dev/null
@@ -1,26 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 00000000..00a51aff
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,6 @@
+#
+# https://help.github.com/articles/dealing-with-line-endings/
+#
+# These are explicitly windows files and should use crlf
+*.bat text eol=crlf
+
diff --git a/.github/workflows/build_container.yaml b/.github/workflows/build_container.yaml
new file mode 100644
index 00000000..8eb03d1e
--- /dev/null
+++ b/.github/workflows/build_container.yaml
@@ -0,0 +1,76 @@
+on: [ push ]
+
+name: Build Container
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+
+ services:
+ postgres:
+ image: postgres:13
+ ports:
+ - 5432:5432
+ env:
+ POSTGRES_USER: reqbaz
+ POSTGRES_PASSWORD: reqbaz
+ POSTGRES_DB: reqbaz
+ options: >-
+ --health-cmd pg_isready
+ --health-interval 10s
+ --health-timeout 5s
+ --health-retries 5
+
+ steps:
+ - uses: actions/checkout@v2
+ - name: Set up JDK 14
+ uses: actions/setup-java@v1
+ with:
+ java-version: 14
+ - name: Build release bundle with Gradle
+ run: ./gradlew export -x test #-Pservice.version=${{ steps.get_version.outputs.version-without-v }}
+ - name: Docker metadata setup
+ id: meta
+ uses: docker/metadata-action@v3
+ with:
+ # list of Docker images to use as base name for tags
+ images: |
+ ghcr.io/rwth-acis/requirementsbazaar
+ # generate Docker tags based on the following events/attributes
+ tags: |
+ type=ref,event=branch
+ type=semver,pattern={{version}}
+ type=sha
+ - name: Docker metadata setup for init container
+ id: meta-init
+ uses: docker/metadata-action@v3
+ with:
+ # list of Docker images to use as base name for tags
+ images: |
+ ghcr.io/rwth-acis/requirementsbazaar-init
+ # generate Docker tags based on the following events/attributes
+ tags: |
+ type=ref,event=branch
+ type=semver,pattern={{version}}
+ type=sha
+ - name: Login to Packages Container registry
+ uses: docker/login-action@v1
+ with:
+ registry: ghcr.io
+ username: ${{ github.repository_owner }}
+ password: ${{ secrets.GITHUB_TOKEN }}
+ - name: Build and push
+ uses: docker/build-push-action@v2
+ with:
+ context: .
+ push: true
+ tags: ${{ steps.meta.outputs.tags }}
+ labels: ${{ steps.meta.outputs.labels }}
+ - name: Build and push init container
+ uses: docker/build-push-action@v2
+ with:
+ context: .
+ file: DockerfileInit
+ push: true
+ tags: ${{ steps.meta-init.outputs.tags }}
+ labels: ${{ steps.meta-init.outputs.labels }}
diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml
new file mode 100644
index 00000000..0b54c9d5
--- /dev/null
+++ b/.github/workflows/gradle.yml
@@ -0,0 +1,36 @@
+name: Gradle Build & Test
+on: [ pull_request, push ]
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+
+ services:
+ postgres:
+ image: postgres:13
+ ports:
+ - 5432:5432
+ env:
+ POSTGRES_USER: reqbaz
+ POSTGRES_PASSWORD: reqbaz
+ POSTGRES_DB: reqbaz
+ options: >-
+ --health-cmd pg_isready
+ --health-interval 10s
+ --health-timeout 5s
+ --health-retries 5
+
+ steps:
+ - uses: actions/checkout@v2
+ - name: Set up JDK 14
+ uses: actions/setup-java@v1
+ with:
+ java-version: 14
+ - name: Build with Gradle
+ run: ./gradlew clean build --info
+ - uses: codecov/codecov-action@v1
+ with:
+ flags: unittests
+ files: ./reqbaz/build/reports/jacoco/test/jacocoTestReport.xml
+ fail_ci_if_error: true
+ verbose: true
diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml
new file mode 100644
index 00000000..4953d0fc
--- /dev/null
+++ b/.github/workflows/release.yaml
@@ -0,0 +1,61 @@
+on:
+ push:
+ # Sequence of patterns matched against refs/tags
+ tags:
+ - 'v[0-9]+.[0-9]+.[0-9]+'
+
+name: Create Release
+
+jobs:
+ build:
+ name: Create Pre-Release
+ runs-on: ubuntu-latest
+ services:
+ postgres:
+ image: postgres:13
+ ports:
+ - 5432:5432
+ env:
+ POSTGRES_USER: reqbaz
+ POSTGRES_PASSWORD: reqbaz
+ POSTGRES_DB: reqbaz
+ options: >-
+ --health-cmd pg_isready
+ --health-interval 10s
+ --health-timeout 5s
+ --health-retries 5
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v2
+ - name: Get Version
+ id: get_version
+ uses: battila7/get-version-action@v2
+ - name: Set up JDK 14
+ uses: actions/setup-java@v1
+ with:
+ java-version: 14
+ - name: Build release bundle with Gradle
+ run: ./gradlew packageDistribution -x test -Pservice.version=${{ steps.get_version.outputs.version-without-v }}
+ - name: Create Release
+ id: create_release
+ uses: actions/create-release@v1
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ with:
+ tag_name: ${{ github.ref }}
+ release_name: Release ${{ github.ref }}
+ body: |
+ This is the ${{ steps.get_version.outputs.version }} release. For changes please check the [Changelog](https://github.com/rwth-acis/RequirementsBazaar/blob/develop/CHANGELOG.md).
+ draft: false
+ prerelease: false
+ - name: Upload Release Asset
+ id: upload-release-asset
+ uses: actions/upload-release-asset@v1
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ with:
+ upload_url: ${{ steps.create_release.outputs.upload_url }}
+ asset_path: ./reqbaz/build/dist/BazaarService.zip
+ asset_name: RequirementsBazaar-${{ steps.get_version.outputs.version }}.zip
+ asset_content_type: application/zip
diff --git a/.github/workflows/release_rc.yaml b/.github/workflows/release_rc.yaml
new file mode 100644
index 00000000..4e7baaec
--- /dev/null
+++ b/.github/workflows/release_rc.yaml
@@ -0,0 +1,61 @@
+on:
+ push:
+ # Sequence of patterns matched against refs/tags
+ tags:
+ - 'v[0-9]+.[0-9]+.[0-9]+-rc\.[0-9]+'
+
+name: Create Pre-Release
+
+jobs:
+ build:
+ name: Create Pre-Release
+ runs-on: ubuntu-latest
+ services:
+ postgres:
+ image: postgres:13
+ ports:
+ - 5432:5432
+ env:
+ POSTGRES_USER: reqbaz
+ POSTGRES_PASSWORD: reqbaz
+ POSTGRES_DB: reqbaz
+ options: >-
+ --health-cmd pg_isready
+ --health-interval 10s
+ --health-timeout 5s
+ --health-retries 5
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v2
+ - name: Get Version
+ id: get_version
+ uses: battila7/get-version-action@v2
+ - name: Set up JDK 14
+ uses: actions/setup-java@v1
+ with:
+ java-version: 14
+ - name: Build release bundle with Gradle
+ run: ./gradlew packageDistribution -x test -Pservice.version=${{ steps.get_version.outputs.version-without-v }}
+ - name: Create Release
+ id: create_release
+ uses: actions/create-release@v1
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ with:
+ tag_name: ${{ github.ref }}
+ release_name: Release ${{ github.ref }}
+ body: |
+ This is a pre-release. For changes please check the [Changelog](https://github.com/rwth-acis/RequirementsBazaar/blob/develop/CHANGELOG.md).
+ draft: false
+ prerelease: true
+ - name: Upload Release Asset
+ id: upload-release-asset
+ uses: actions/upload-release-asset@v1
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ with:
+ upload_url: ${{ steps.create_release.outputs.upload_url }}
+ asset_path: ./reqbaz/build/dist/BazaarService.zip
+ asset_name: RequirementsBazaar-${{ steps.get_version.outputs.version }}.zip
+ asset_content_type: application/zip
diff --git a/.gitignore b/.gitignore
index 9278c1d8..5538276d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,7 +11,7 @@
/.las2peer
/node-storage
/startup
-/log
+log/
/lib
etc/ivy/ivy.jar
/service
@@ -21,6 +21,7 @@ etc/ivy/ivy.jar
restMapping.xml
/out
/output/
+/servicebundle
# IntelliJ
.idea/
@@ -62,3 +63,10 @@ Temporary Items
.apdisk
# End of https://www.gitignore.io/api/macos
+# Ignore Gradle project-specific cache directory
+.gradle
+
+# Ignore Gradle build output directory
+build
+
+.db_data
diff --git a/.project b/.project
deleted file mode 100755
index 762f646b..00000000
--- a/.project
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
- Requirement Bazaar
-
-
-
-
- org.eclipse.jdt.core.javabuilder
-
-
-
-
- org.eclipse.jdt.core.javanature
-
-
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 00000000..5433387b
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,90 @@
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres
+to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+Releases prior to v0.7.2 are only documented on
+the [GitHub Release Page](https://github.com/rwth-acis/RequirementsBazaar/releases)
+
+## [Unreleased]
+
+## [0.9.0] - 2021-09-08
+
+### Added
+
+- Added new testcases [#73](https://github.com/rwth-acis/RequirementsBazaar/pull/73)
+- Added endpoints to provide anonymous feedback which can be read by project
+ admins [#85](https://github.com/rwth-acis/RequirementsBazaar/pull/85)
+- Added endpoint to search for users by name or email [#90](https://github.com/rwth-acis/RequirementsBazaar/pull/90)
+- Categories, projects and requirements now have a `lastActivity`
+ attribute [#91](https://github.com/rwth-acis/RequirementsBazaar/pull/91).
+- Categories, projects and requirements now have a `userContext` encapsuling the dynamic user related information (
+ permissions, votes, contribution) [#94](https://github.com/rwth-acis/RequirementsBazaar/pull/94).
+- If a user is a member of a project the respective role is now returned in the`usertRole` attribute of the
+ new `userContext`
+ attribute [#94](https://github.com/rwth-acis/RequirementsBazaar/pull/94) [#96](https://github.com/rwth-acis/RequirementsBazaar/pull/96)
+ .
+- Add a delete projects endpoint [#100](https://github.com/rwth-acis/RequirementsBazaar/pull/100).
+- Add an update comment endpoint [#100](https://github.com/rwth-acis/RequirementsBazaar/pull/100).
+- Redacted comments now have a deleted flag [#103](https://github.com/rwth-acis/RequirementsBazaar/pull/103).
+- Added a user dashboard listing the last 10 most recent active followed projects, categories and
+ requirements [#106](https://github.com/rwth-acis/RequirementsBazaar/pull/106).
+- Requirements can now have arbitrary tags next to the categories. Tags are a project scoped
+ entity [#108](https://github.com/rwth-acis/RequirementsBazaar/pull/108).
+- Projects, categories and requirements now have an `additionalProperties` object which allows storing and providing
+ additional context as plain json [#109](https://github.com/rwth-acis/RequirementsBazaar/pull/109).
+- The projects endpoint now has a recursive flag which includes projects with requirements matching the search term into the results [#116](https://github.com/rwth-acis/RequirementsBazaar/pull/116)
+
+### Changed
+
+- Updated all dependencies, most notably las2peer 1.0.0 [#68](https://github.com/rwth-acis/RequirementsBazaar/pull/68)
+- Updated las2peer to 1.1.0 and thereby requiring Java 14 [#73](https://github.com/rwth-acis/RequirementsBazaar/pull/73)
+- Updated las2peer to 1.1.2 [#115](https://github.com/rwth-acis/RequirementsBazaar/pull/115)
+- Changed buildsystem to use gradle [#73](https://github.com/rwth-acis/RequirementsBazaar/pull/73)
+- Automatically generate jooq code from migration files at build
+ time [#73](https://github.com/rwth-acis/RequirementsBazaar/pull/73)
+- Replaced vtor with Java Bean Validation (this implies changes to the API documentation as these annotations included)
+ [#73](https://github.com/rwth-acis/RequirementsBazaar/pull/73)
+- Fixed a bug in the swagger documentation where `GET /categories/{categoryId/requirements` was incorrectly annotated to
+ return a list of categories instead of a list of
+ requirements [#78](https://github.com/rwth-acis/RequirementsBazaar/pull/78)
+- Revised and automated release process [#78](https://github.com/rwth-acis/RequirementsBazaar/pull/78)
+- Split leading `+`/`-`from sort parameter in favour of a `sortDirection` (ASC/DESC) parameter.
+ [#82](https://github.com/rwth-acis/RequirementsBazaar/pull/82)
+- Order by name now implies natural sorting [#82](https://github.com/rwth-acis/RequirementsBazaar/pull/82)
+- Remove static code from data classes and generate getter/setters and builder with lombok. This renames the `category`
+ attribute in `EntityContext` to `categories` [#83](https://github.com/rwth-acis/RequirementsBazaar/pull/82)
+- Rework permission system as lined out
+ in [Privileges](docs/Privileges.md) [#85](https://github.com/rwth-acis/RequirementsBazaar/pull/85)
+- Category `leader` attribute has been renamed
+ to `creator` [#85](https://github.com/rwth-acis/RequirementsBazaar/pull/85)
+- Voting now returns a 303 response with reference to the modified
+ object [#85](https://github.com/rwth-acis/RequirementsBazaar/pull/85)
+- Restrict user attributes normally returned to id, username, profile image and
+ las2peerid [#90](https://github.com/rwth-acis/RequirementsBazaar/pull/90)
+- Requirements no longer return the category objects in the `categories` attribute but a list of category
+ ids [#91](https://github.com/rwth-acis/RequirementsBazaar/pull/91).
+- Vote direction can no longer be provided as a query
+ parameter [#94](https://github.com/rwth-acis/RequirementsBazaar/pull/94) but instead as a direction object strictly
+ defined by an enum [#96](https://github.com/rwth-acis/RequirementsBazaar/pull/96),
+- Moved user related information in categories, requirements and projects (isFollower/Developer/Contributor, userVoted)
+ into the new `userContext` [#94](https://github.com/rwth-acis/RequirementsBazaar/pull/94).
+- Comments with existing responses will no longer be deleted but
+ redacted [#103](https://github.com/rwth-acis/RequirementsBazaar/pull/103).
+- Comments for a requirement are no longer paginated but instead return all
+ comments [#103](https://github.com/rwth-acis/RequirementsBazaar/pull/103).
+- Postgres is now the backend database [#112](https://github.com/rwth-acis/RequirementsBazaar/pull/112)
+- Attachments are now updated via PUT towards the requirement
+ endpoint [#114](https://github.com/rwth-acis/RequirementsBazaar/pull/114).
+- All timestamps now contain timezone information [#115](https://github.com/rwth-acis/RequirementsBazaar/pull/115)
+
+### Removed
+
+- Personalisation endpoints [#94](https://github.com/rwth-acis/RequirementsBazaar/pull/94)
+- Attachment endpoint [#114](https://github.com/rwth-acis/RequirementsBazaar/pull/114)
+
+## [0.7.2] - 2017-10-25
+
+See GH Releases
diff --git a/Dockerfile b/Dockerfile
index ef4f617b..24b51e1f 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,21 +1,26 @@
-FROM openjdk:8-jdk-alpine
+# Build final container without build dependencies etc.
+FROM openjdk:14-jdk-alpine
ENV HTTP_PORT=8080
ENV HTTPS_PORT=8443
ENV LAS2PEER_PORT=9011
-RUN apk add --update bash mysql-client apache-ant && rm -f /var/cache/apk/*
RUN addgroup -g 1000 -S las2peer && \
adduser -u 1000 -S las2peer -G las2peer
+RUN apk add --update bash iproute2 && rm -f /var/cache/apk/*
+
-COPY --chown=las2peer:las2peer . /src
WORKDIR /src
+COPY --chown=las2peer:las2peer ./reqbaz/build/export/ .
+COPY --chown=las2peer:las2peer docker-entrypoint.sh /src/docker-entrypoint.sh
+COPY --chown=las2peer:las2peer gradle.properties /src/gradle.properties
+RUN mkdir /src/log && chown -R las2peer:las2peer /src
# run the rest as unprivileged user
USER las2peer
-RUN ant jar
EXPOSE $HTTP_PORT
EXPOSE $HTTPS_PORT
EXPOSE $LAS2PEER_PORT
+
ENTRYPOINT ["/src/docker-entrypoint.sh"]
diff --git a/DockerfileInit b/DockerfileInit
new file mode 100644
index 00000000..9aa094b2
--- /dev/null
+++ b/DockerfileInit
@@ -0,0 +1,5 @@
+FROM liquibase/liquibase:4.4.0
+
+ADD reqbaz/src/main/resources/changelog.yaml reqbaz/src/main/resources/base-permissions.sql /liquibase/changelog/
+
+CMD docker-entrypoint.sh --url=jdbc:postgresql://${HOST}:5432/reqbaz --username=${USERNAME} --password=${PASSWORD} --classpath=/liquibase/changelog --changeLogFile=changelog.yaml update
diff --git a/README.md b/README.md
index 9b31ec71..f2fa7c00 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,7 @@
-Requirements Bazaar
-===================
+# Requirements Bazaar
+
+## Idea
-Idea
--------------------
In years of research at RWTH Aachen we have developed and actually operated an **Open Innovation Platform for gathering requirements** for prototypes in large international academic projects. The last version of the current product is available under http://requirements-bazaar.org . End-users can enter their requirements by providing short descriptions including user stories, screenshots and other images. The requirements can then be shared amongst its users. On the other side of the chain, developers may take up ideas and transfer the accepted requirements to an issue system like JIRA.
Over the last years it turned out that people want more lightweight and mobile-friendly tools; we found the old monolithic system to be very hard to maintain to add new features. Therefore we are currently redeveloping it from scratch integrating many ideas from our users and incorporating new research findings. We are further driven by a mobile-first approach to support a wide variety of devices. We additionally want to support various social features like sharing on social networks or blogs and allowing discussions and rating amongst end-users and developers.
@@ -15,52 +14,50 @@ The service is under development. You can participate by creating pull requests
More information about our microservice ecosystem is explained in our **[Requirements-Bazaar wiki on Github](https://github.com/rwth-acis/RequirementsBazaar/wiki)**.
-----------
+---
+
+## Service Documentation
-Service Documentation
--------------------
We use **[Swagger](http://swagger.io/specification/)** for documenting this microservice. You can use **[Swagger UI](http://swagger.io/swagger-ui/)** to inspect the API.
Please use our deployed **[Swagger UI instance](https://requirements-bazaar.org/docs)** to inspect our API. You can authorize yourself via Oauth 2.0 by clicking at the `Authorize` button at the top. After you are authorized you can try out all the API calls directly from Swagger UI.
-To try out one API call open one method, fill the parameters and click `Try it out!`. The Swagger UI instance is connected to our development environment where we test nex features. So feel free to play around.
+To try out one API call open one method, fill the parameters and click `Try it out!`. The Swagger UI instance is also connected to our development environment where we test nex features. You can select this in the dropdown menu at the top. Feel free to play around there.
API documentation endpoints:
- `baseURL/bazaar/swagger.json`
-----------
+---
+
+## Technology
-Technology
--------------------
Requirements bazaar itself is a web application, which is separated to a client-only side and a back-end side. This GitHub repo holds the codebase of the back-end side only. If you are looking for the front-end, take a look at this GitHub project: **[Requirement Bazaar Web Frontend](https://github.com/rwth-acis/RequirementsBazaar-WebFrontend)**
-The backend is built on Java technologies. As a service framework we use our in-house developed **[las2peer](https://github.com/rwth-acis/LAS2peer)** project. For persisting our data we use MySQL database and jOOQ to access it. User input validation is done using Jodd Vtor library and for serializing our data into JSON format, we use the Jackson library.
+The backend is built on Java technologies. As a service framework we use our in-house developed **[las2peer](https://github.com/rwth-acis/LAS2peer)** project. For persisting our data we use a PostgreSQL database and jOOQ to access it and for serializing our data into JSON format, we use the Jackson library.
+
+## Dependencies
-Dependencies
--------------------
In order to be able to run this service project the following components should be installed on your system:
- - JDK (min v1.8) + Java Cryptography Extension (JCE)
- - MySQL 5.7
- - Apache Ant to build
-
-How to set up the database
--------------------
- 1. `git clone` this repo
- 2. To configure your database access look at the [Configuration](#configuration) section
- 3. Compile the project with `ant`
- 4. Create a new database called `reqbaz`, possibly with UTF-8 collation
- 5. Run `ant migrate-db` to create your db schema or migrate to a newer version while updating your service
- 6. If you need sample data run the file `\etc\add_reqbaz_demo_data.sql` or `\etc\add_reqbaz_demo_data_full.sql`
-
-Configuration
--------------------
+ - JDK 14
+ - PostgreSQL 12 or newer
+ - Gradle to build
+
+## How to set up the database (non developer guide)
+
+ 1. Download a release bundle from [GitHub](https://github.com/rwth-acis/RequirementsBazaar/releases)
+ 2. Create a new database called `reqbaz`, possibly with UTF-8 collation
+ 3. To configure your database access look at the [Configuration](#configuration) section
+ 4. Use liquibase to migrate the database
+
+## Configuration
+
You need to configure this service to work with your own specific environment. Here is the list of configuration variables:
`\etc\de.rwth.dbis.acis.bazaar.service.BazaarService.properties`:
- `dbUserName`: A database user's name, which will be used to access the database
- `dbPassword`: The database user's password
- `dbUrl`: JDBC Connection string to access the database. Modify it if you have changed the name of your database
- - `land`: Default language setting
+ - `lang`: Default language setting
- `country`: Default country setting
- `baseURL`: Base URL this service runs on
- `frontendBaseURL`: Base URL for the frontend which uses Requirements-Bazaar service
@@ -71,38 +68,43 @@ You need to configure this service to work with your own specific environment. H
For other configuration settings, check the **[las2peer](https://github.com/rwth-acis/LAS2peer)** project.
-Build
--------------------
-For build management we use Ant. To build the cloned code, please using a console/terminal navigate to the home directory, where the build.xml file is located and run the following commands:
+### How to run
+ 1. First please make sure you have already [set up the database](#how-to-set-up-the-database).
+ 2. Make sure your [config settings](#configuration) are properly set.
+ 3. When not using a release asset, check [Building](#building).
+ 4. Open a console/terminal window and navigate to the project directory.
+ 5. Run the `bin\start_network.bat` or `bin/start_network.sh` script.
- - `ant`
-
-You can also generate a bundled jar with all the dependencies with the command
-
- - `ant jar-big`
+---
-How to run
--------------------
- 1. First please make sure you have already [set up the database](#how-to-set-up-the-database)
- 2. Make sure your [config settings](#configuration) are properly set.
- 3. [Build](#build)
- 4. Open a console/terminal window and navigate to the project directory
- 5. Run the `bin\start_network.bat` or `bin/start_network.sh` script
+## Developer guide
+
+### Prerequisites
+
+Building and testing requires a database which will be cleaned on every build.
+
+Set the credentials for this database in the gradle.properties and in the `reqbaz/etc` folder.
+This configuration folder is relevant for any testing related settings.
+
+In the `etc` folder on top level you may configure a different database if you want to distinguish between local testing and automated testing.
+However you'll have to apply the migrations with liquibase manually to this database.
+
+### Building
+
+The build and test process is done with gradle. A simple `gradle build` should be sufficient to build the project and run the test cases.
+
+You may have to adjust either your global java environment or the one in your IDE to use Java 14 or the built asset won't start.
+
+### Debugging
+
+Add the debugger parameters below to your startscript and set up your IDE to connect to a remote debugging port.
- How to run using Docker
- -------------------
- Docker is providing the simplest way to run the Requirement Bazaar back-end. Just follow the following steps if Docker is already installed on your system:
+`-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5433`.
- 1. `git clone` this repo
- 2. `cd` into the cloned repo
- 3. `docker build -t rwthacis/reqbaz-service .`
- 4. `docker run -i -t --rm -v "$(pwd)":/build rwthacis/reqbaz-service`
+Now you should be able to set breakpoints and inspect the runtime.
-----------
+## Troubleshooting & FAQ
-Troubleshooting & FAQ
--------------------
- - I get Java encryption errors: Did you install Java Cryptography Extension?
- I can not run the start script: Check if you have OS permission to run the file.
- The service does not start: Check if all jars in the lib and service folder are readable.
- The start script seems broken: Check if the start script has the correct encoding. If you ran the service on Unix use `dos2unix` to change the encoding.
diff --git a/bin/JOOQGeneration/reqbaz_generation_info.xml b/bin/JOOQGeneration/reqbaz_generation_info.xml
deleted file mode 100644
index 7c563ab3..00000000
--- a/bin/JOOQGeneration/reqbaz_generation_info.xml
+++ /dev/null
@@ -1,50 +0,0 @@
-
-
-
-
- com.mysql.jdbc.Driver
- jdbc:mysql://localhost:3306/reqbaz
- root
- reqbaz
-
-
-
-
- org.jooq.util.DefaultGenerator
-
-
-
- org.jooq.util.mysql.MySQLDatabase
-
-
- reqbaz
-
-
- .*
-
-
-
-
-
- BOOLEAN
- .*\.admin
-
-
-
-
-
-
- de.rwth.dbis.acis.bazaar.service.dal.jooq
-
-
- ../../src/main
-
-
-
diff --git a/bin/JOOQGeneration/run.bat b/bin/JOOQGeneration/run.bat
deleted file mode 100644
index fe58ffc6..00000000
--- a/bin/JOOQGeneration/run.bat
+++ /dev/null
@@ -1 +0,0 @@
-java -classpath ../../service/jooq-3.9.1.jar;../../service/jooq-meta-3.9.1.jar;../../service/jooq-codegen-3.9.1.jar;../../service/mysql-connector-java-6.0.5.jar;. org.jooq.util.GenerationTool /reqbaz_generation_info.xml
diff --git a/bin/JOOQGeneration/run.sh b/bin/JOOQGeneration/run.sh
deleted file mode 100755
index 01bb562d..00000000
--- a/bin/JOOQGeneration/run.sh
+++ /dev/null
@@ -1 +0,0 @@
-java -classpath ../../service/jooq-3.9.1.jar:../../service/jooq-meta-3.9.1.jar:../../service/jooq-codegen-3.9.1.jar:../../service/mysql-connector-java-6.0.5.jar:. org.jooq.util.GenerationTool /reqbaz_generation_info.xml
diff --git a/build.xml b/build.xml
deleted file mode 100755
index a33adf53..00000000
--- a/build.xml
+++ /dev/null
@@ -1,317 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- #!/bin/bash
-
- # this script is autogenerated by 'ant startscripts'
- # it starts a LAS2peer node providing the service '${service.name}.${service.class}' of this project
- # pls execute it from the root folder of your deployment, e. g. ./bin/start_network.sh
-
- java -cp "lib/*" i5.las2peer.tools.L2pNodeLauncher -p 9011 uploadStartupDirectory --service-directory service startService\(\'${service.name}.${service.class}@${service.version}\',\'${service.passphrase}\'\) startWebConnector interactive
-
- :: this script is autogenerated by 'ant
- startscripts'
- :: it starts a LAS2peer node providing the service '${service.name}.${service.class}' of this project
- :: pls execute it from the bin folder of your deployment by double-clicking on it
-
- %~d0
- cd %~p0
- cd ..
- set BASE=%CD%
- set CLASSPATH="%BASE%/lib/*;"
-
- java -cp %CLASSPATH% i5.las2peer.tools.L2pNodeLauncher -p 9011 uploadStartupDirectory --service-directory service startService('${service.name}.${service.class}@${service.version}','${service.passphrase}') startWebConnector interactive
-
- pause
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- agent-user-${las2peer_user.name}.xml;${las2peer_user.password}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh
index 7716f83a..416137a2 100755
--- a/docker-entrypoint.sh
+++ b/docker-entrypoint.sh
@@ -4,29 +4,28 @@ set -e
# print all comands to console if DEBUG is set
if [[ ! -z "${DEBUG}" ]]; then
- set -x
+ set -x
fi
# set some helpful variables
export SERVICE_PROPERTY_FILE='etc/de.rwth.dbis.acis.bazaar.service.BazaarService.properties'
export WEB_CONNECTOR_PROPERTY_FILE='etc/i5.las2peer.webConnector.WebConnector.properties'
-export SERVICE_VERSION=$(awk -F "=" '/service.version/ {print $2}' etc/ant_configuration/service.properties)
-export SERVICE_NAME=$(awk -F "=" '/service.name/ {print $2}' etc/ant_configuration/service.properties)
-export SERVICE_CLASS=$(awk -F "=" '/service.class/ {print $2}' etc/ant_configuration/service.properties)
+
+export SERVICE_VERSION=$(awk -F "=" '/service.version/ {print $2}' gradle.properties)
+export SERVICE_NAME=$(awk -F "=" '/service.name/ {print $2}' gradle.properties)
+export SERVICE_CLASS=$(awk -F "=" '/service.class/ {print $2}' gradle.properties)
export SERVICE=${SERVICE_NAME}.${SERVICE_CLASS}@${SERVICE_VERSION}
-export DEMO_DATA_SQL='etc/migrations/add_reqbaz_demo_data.sql'
-export DEMO_DATA_SQL_FULL='etc/migrations/add_reqbaz_demo_data_full.sql'
-export MYSQL_DATABASE='reqbaz'
+export POSTGRES_DATABASE='reqbaz'
# check mandatory variables
-[[ -z "${MYSQL_USER}" ]] && \
- echo "Mandatory variable MYSQL_USER is not set. Add -e MYSQL_USER=myuser to your arguments." && exit 1
-[[ -z "${MYSQL_PASSWORD}" ]] && \
- echo "Mandatory variable MYSQL_PASSWORD is not set. Add -e MYSQL_PASSWORD=mypasswd to your arguments." && exit 1
+[[ -z "${POSTGRES_USER}" ]] &&
+ echo "Mandatory variable POSTGRES_USER is not set. Add -e POSTGRES_USER=reqbaz to your arguments." && exit 1
+[[ -z "${POSTGRES_PASSWORD}" ]] &&
+ echo "Mandatory variable POSTGRES_PASSWORD is not set. Add -e POSTGRES_PASSWORD=mypasswd to your arguments." && exit 1
# set defaults for optional service parameters
-[[ -z "${MYSQL_HOST}" ]] && export MYSQL_HOST='mysql'
-[[ -z "${MYSQL_PORT}" ]] && export MYSQL_PORT='3306'
+[[ -z "${POSTGRES_HOST}" ]] && export POSTGRES_HOST='postgres'
+[[ -z "${POSTGRES_PORT}" ]] && export POSTGRES_PORT='5432'
[[ -z "${SERVICE_PASSPHRASE}" ]] && export SERVICE_PASSPHRASE='Passphrase'
[[ -z "${BAZAAR_LANG}" ]] && export BAZAAR_LANG='en'
@@ -51,12 +50,12 @@ export MYSQL_DATABASE='reqbaz'
# configure service properties
-function set_in_service_config {
- sed -i "s?${1}[[:blank:]]*=.*?${1}=${2}?g" ${SERVICE_PROPERTY_FILE}
+function set_in_service_config() {
+ sed -i "s?${1}[[:blank:]]*=.*?${1}=${2}?g" ${SERVICE_PROPERTY_FILE}
}
-set_in_service_config dbUserName ${MYSQL_USER}
-set_in_service_config dbPassword ${MYSQL_PASSWORD}
-set_in_service_config dbUrl "jdbc:mysql://${MYSQL_HOST}:${MYSQL_PORT}/${MYSQL_DATABASE}"
+set_in_service_config dbUserName ${POSTGRES_USER}
+set_in_service_config dbPassword ${POSTGRES_PASSWORD}
+set_in_service_config dbUrl "jdbc:postgresql://${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DATABASE}"
set_in_service_config lang ${BAZAAR_LANG}
set_in_service_config country ${BAZAAR_COUNTRY}
set_in_service_config baseURL ${BASE_URL}
@@ -67,11 +66,10 @@ set_in_service_config smtpServer ${SMTP_SERVER}
set_in_service_config emailFromAddress ${EMAIL_FROM_ADDRESS}
set_in_service_config emailSummaryTimePeriodInMinutes ${EMAIL_SUMMARY_TIME_PERIOD_IN_MINUTES}
-
# configure web connector properties
-function set_in_web_config {
- sed -i "s?${1}[[:blank:]]*=.*?${1}=${2}?g" ${WEB_CONNECTOR_PROPERTY_FILE}
+function set_in_web_config() {
+ sed -i "s?${1}[[:blank:]]*=.*?${1}=${2}?g" ${WEB_CONNECTOR_PROPERTY_FILE}
}
set_in_web_config httpPort ${HTTP_PORT}
set_in_web_config httpsPort ${HTTPS_PORT}
@@ -84,50 +82,35 @@ set_in_web_config crossOriginResourceMaxAge ${CROSS_ORIGIN_RESOURCE_MAX_AGE}
set_in_web_config enableCrossOriginResourceSharing ${ENABLE_CROSS_ORIGIN_RESOURCE_SHARING}
set_in_web_config oidcProviders ${OIDC_PROVIDERS}
-# ensure the database is ready
-while ! mysqladmin ping -h${MYSQL_HOST} -P${MYSQL_PORT} -u${MYSQL_USER} -p${MYSQL_PASSWORD} --silent; do
- echo "Waiting for mysql at ${MYSQL_HOST}:${MYSQL_PORT}..."
- sleep 1
-done
-echo "${MYSQL_HOST}:${MYSQL_PORT} is available. Continuing..."
-
-# run migrations (does nothing if already migrated)
-ant migrate-db
-
-if [[ ! -z "${INSERT_DEMO_DATA}" ]]; then
- echo "Inserting demo data into the database..."
- mysql -h${MYSQL_HOST} -P${MYSQL_PORT} -u${MYSQL_USER} -p${MYSQL_PASSWORD} ${MYSQL_DATABASE} < ${DEMO_DATA_SQL}
-fi
-
-if [[ ! -z "${INSERT_DEMO_DATA_FULL}" ]]; then
- echo "Inserting demo data into the database..."
- mysql -h${MYSQL_HOST} -P${MYSQL_PORT} -u${MYSQL_USER} -p${MYSQL_PASSWORD} ${MYSQL_DATABASE} < ${DEMO_DATA_SQL_FULL}
+# set pod ip in pastry conf
+if [[ ! -z "${POD_IP}" ]]; then
+ echo external_address = ${POD_IP}:${LAS2PEER_PORT} > etc/pastry.properties
+ echo socket_bindAddress = ${POD_IP} >> etc/pastry.properties
fi
# wait for any bootstrap host to be available
if [[ ! -z "${BOOTSTRAP}" ]]; then
- echo "Waiting for any bootstrap host to become available..."
- for host_port in ${BOOTSTRAP//,/ }; do
- arr_host_port=(${host_port//:/ })
- host=${arr_host_port[0]}
- port=${arr_host_port[1]}
- if { /dev/null; then
- echo "${host_port} is available. Continuing..."
- break
- fi
- done
+ echo "Waiting for any bootstrap host to become available..."
+ for host_port in ${BOOTSTRAP//,/ }; do
+ arr_host_port=(${host_port//:/ })
+ host=${arr_host_port[0]}
+ port=${arr_host_port[1]}
+ if { /dev/null; then
+ echo "${host_port} is available. Continuing..."
+ break
+ fi
+ done
fi
# prevent glob expansion in lib/*
set -f
LAUNCH_COMMAND='java -cp lib/*:service/* i5.las2peer.tools.L2pNodeLauncher -s service -p '"${LAS2PEER_PORT} ${SERVICE_EXTRA_ARGS}"
if [[ ! -z "${BOOTSTRAP}" ]]; then
- LAUNCH_COMMAND="${LAUNCH_COMMAND} -b ${BOOTSTRAP}"
+ LAUNCH_COMMAND="${LAUNCH_COMMAND} -b ${BOOTSTRAP}"
fi
# start the service within a las2peer node
-if [[ -z "${@}" ]]
-then
+if [[ -z "${@}" ]]; then
exec ${LAUNCH_COMMAND} startService\("'""${SERVICE}""'", "'""${SERVICE_PASSPHRASE}""'"\) startWebConnector
else
exec ${LAUNCH_COMMAND} ${@}
diff --git a/docs/AttachmentUpload.md b/docs/AttachmentUpload.md
new file mode 100644
index 00000000..f750e873
--- /dev/null
+++ b/docs/AttachmentUpload.md
@@ -0,0 +1,26 @@
+# File Upload
+
+## Implications
+
+Uploads and references in the database are separate operations. This may lead to incomplete and unreferenced uploads.
+This is a tradeoff made in favour of having multipart API endpoints.
+
+A way to resolve this is to regularly synchronize the file index with the references in database.
+
+## Workflows
+
+### Upload with requirement creation
+
+1. Upload the file to the file service and receive or generate an identifier.
+2. Include reference to the uploaded file as a regular attachment object in the attachment array. Then send this to the
+ create requirement endpoint.
+
+### Add attachments to an existing requirement
+
+1. Upload the file to the file service and receive or generate an identifier.
+2. Add reference to the uploaded file as a regular attachment object to the attachment array and use the requirements
+ update endpoint.
+
+### Remove attachments from an existing requirement
+
+1. Remove the reference in the attachments array and use the update requirement endpoint.
diff --git a/docs/Privileges.md b/docs/Privileges.md
new file mode 100644
index 00000000..0a309b7e
--- /dev/null
+++ b/docs/Privileges.md
@@ -0,0 +1,52 @@
+# Privileges
+
+With Version 0.9 the privilege model has been extended and partially redesigned.
+
+## Roles
+There are 6 roles, which inherit the privileges of the roles mentioned before. The `Project.*` roles are scoped to a specific project.
+
+1. Anonymous
+2. Logged in user
+3. Project Member (scoped)
+4. Project Manager (scoped)
+5. Project Admin (scoped)
+6. System Administrator
+
+## Privilege Map
+|name |name |
+|--------------|---------------------------|
+|Anonymous |Read_PUBLIC_ATTACHMENT |
+|Anonymous |Read_PUBLIC_CATEGORY |
+|Anonymous |Read_PUBLIC_COMMENT |
+|Anonymous |Read_PUBLIC_PROJECT |
+|Anonymous |Read_PUBLIC_REQUIREMENT |
+|LoggedInUser |Create_ATTACHMENT |
+|LoggedInUser |Create_COMMENT |
+|LoggedInUser |Create_FOLLOW |
+|LoggedInUser |Create_PERSONALISATION_DATA|
+|LoggedInUser |Create_PROJECT |
+|LoggedInUser |Create_REQUIREMENT |
+|LoggedInUser |Create_VOTE |
+|LoggedInUser |Delete_FOLLOW |
+|LoggedInUser |Delete_VOTE |
+|LoggedInUser |Read_PERSONALISATION_DATA |
+|LoggedInUser |Read_USERS |
+|ProjectAdmin |Modify_CATEGORY |
+|ProjectAdmin |Modify_PROJECT |
+|ProjectAdmin |Modify_ADMIN_MEMBERS |
+|ProjectAdmin |Delete_PROJECT |
+|ProjectManager|Create_CATEGORY |
+|ProjectManager|Modify_ATTACHMENT |
+|ProjectManager|Modify_COMMENT |
+|ProjectManager|Modify_REQUIREMENT |
+|ProjectManager|Promote_USER |
+|ProjectManager|Read_FEEDBACK |
+|ProjectManager|Modify_MEMBERS |
+|ProjectMember |Create_DEVELOP |
+|ProjectMember |Delete_DEVELOP |
+|ProjectMember |Read_ATTACHMENT |
+|ProjectMember |Read_CATEGORY |
+|ProjectMember |Read_COMMENT |
+|ProjectMember |Read_PROJECT |
+|ProjectMember |Read_REQUIREMENT |
+|ProjectMember |Realize_REQUIREMENT |
diff --git a/etc/ant_configuration/service.properties b/etc/ant_configuration/service.properties
deleted file mode 100755
index 9146bacc..00000000
--- a/etc/ant_configuration/service.properties
+++ /dev/null
@@ -1,6 +0,0 @@
-service.version=0.8.0
-service.name=de.rwth.dbis.acis.bazaar.service
-service.path=de/rwth/dbis/acis/bazaar/service
-service.class=BazaarService
-service.passphrase=Passphrase
-service.dependencies=commons-codec;version="1.9",commons-dbcp2;version="2.0",commons-io;version="2.4",commons-logging;version="1.2",commons-pool2;version="2.2",emoji-java;version="3.1.3",gson;version="2.3",httpclient;version="4.5.1",httpcore;version="4.4.3",jodd-bean;version="3.6.1",jodd-core;version="3.6.1",jodd-vtor;version="3.6.1",jooq;version="3.9.1",jooq-codegen;version="3.9.1",jooq-meta;version="3.9.1",json;version="20140107",mysql-connector-java;version="6.0.5"
diff --git a/etc/ant_configuration/user.properties b/etc/ant_configuration/user.properties
deleted file mode 100755
index 1ba6e2ca..00000000
--- a/etc/ant_configuration/user.properties
+++ /dev/null
@@ -1,3 +0,0 @@
-las2peer_user.name=anonymous
-las2peer_user.password=anonymous
-las2peer_user.email=anonymous@mail.com
\ No newline at end of file
diff --git a/etc/de.rwth.dbis.acis.bazaar.service.BazaarService.properties b/etc/de.rwth.dbis.acis.bazaar.service.BazaarService.properties
index 67ea03e9..082404f1 100644
--- a/etc/de.rwth.dbis.acis.bazaar.service.BazaarService.properties
+++ b/etc/de.rwth.dbis.acis.bazaar.service.BazaarService.properties
@@ -1,13 +1,13 @@
-dbUserName=root
+dbUserName=reqbaz
dbPassword=reqbaz
-dbUrl=jdbc:mysql://localhost:3306/reqbaz
+dbUrl=jdbc:postgresql://localhost:5432/reqbaz
lang=en
country=us
baseURL=http://localhost:8080/bazaar/
frontendBaseURL=http://localhost:5000/
-activityTrackerService=de.rwth.dbis.acis.activitytracker.service.ActivityTrackerService@0.4.1
+activityTrackerService=de.rwth.dbis.acis.activitytracker.service.ActivityTrackerService@0.8.0
activityOrigin=https://requirements-bazaar.org
smtpServer=
emailFromAddress=
emailSummaryTimePeriodInMinutes=
-monitor=
\ No newline at end of file
+monitor=
diff --git a/etc/i5.las2peer.webConnector.WebConnector.properties b/etc/i5.las2peer.webConnector.WebConnector.properties
index 8c815727..8bcbca0e 100755
--- a/etc/i5.las2peer.webConnector.WebConnector.properties
+++ b/etc/i5.las2peer.webConnector.WebConnector.properties
@@ -8,7 +8,8 @@ crossOriginResourceDomain = *
crossOriginResourceMaxAge = 60
enableCrossOriginResourceSharing = TRUE
preferLocalServices = TRUE
-xmlPath =
+xmlPath =
defaultLoginUser=anonymous
defaultLoginPassword=anonymous
-oidcProviders = https://api.learning-layers.eu/o/oauth2,https://accounts.google.com
\ No newline at end of file
+oidcProviders = https://api.learning-layers.eu/o/oauth2,https://accounts.google.com
+#oidcProviders = http://api.learning-layers.eu:8081/auth/realms/main,https://accounts.google.com
diff --git a/etc/ivy/ivy.xml b/etc/ivy/ivy.xml
deleted file mode 100755
index 33c81a82..00000000
--- a/etc/ivy/ivy.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/etc/ivy/ivysettings.xml b/etc/ivy/ivysettings.xml
deleted file mode 100755
index 042d228b..00000000
--- a/etc/ivy/ivysettings.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
-
-
-
-
-
-
-
diff --git a/etc/migrations/V1__create_reqbaz_schema.sql b/etc/migrations/V1__create_reqbaz_schema.sql
deleted file mode 100644
index 296fb54f..00000000
--- a/etc/migrations/V1__create_reqbaz_schema.sql
+++ /dev/null
@@ -1,318 +0,0 @@
-SET FOREIGN_KEY_CHECKS = 0;
-
--- tables
--- Table attachment
-CREATE TABLE IF NOT EXISTS reqbaz.attachment (
- id INT NOT NULL AUTO_INCREMENT,
- creation_date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
- last_updated_date TIMESTAMP NULL,
- requirement_id INT NOT NULL,
- user_id INT NOT NULL,
- name VARCHAR(255) NOT NULL,
- description TEXT NULL,
- mime_type VARCHAR(255) NOT NULL,
- identifier VARCHAR(900) NOT NULL,
- file_url VARCHAR(1000) NOT NULL,
- CONSTRAINT attachment_pk PRIMARY KEY (id),
- CONSTRAINT attachment_requirement FOREIGN KEY attachment_requirement (requirement_id) REFERENCES requirement (id)
- ON DELETE CASCADE,
- CONSTRAINT attachment_user FOREIGN KEY attachment_user (user_id) REFERENCES user (id)
-);
-
--- Table comment
-CREATE TABLE IF NOT EXISTS reqbaz.comment (
- id INT NOT NULL AUTO_INCREMENT,
- message TEXT NOT NULL,
- creation_date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
- last_updated_date TIMESTAMP NULL,
- requirement_id INT NOT NULL,
- user_id INT NOT NULL,
- reply_to_comment_id INT,
- CONSTRAINT comment_pk PRIMARY KEY (id),
- CONSTRAINT comment_requirement FOREIGN KEY comment_requirement (requirement_id) REFERENCES requirement (id)
- ON DELETE CASCADE,
- CONSTRAINT comment_user FOREIGN KEY comment_user (user_id) REFERENCES user (id),
- CONSTRAINT reply_comment FOREIGN KEY reply_comment (reply_to_comment_id) REFERENCES comment (id)
-);
-
--- Table category
-CREATE TABLE IF NOT EXISTS reqbaz.category (
- id INT NOT NULL AUTO_INCREMENT,
- name VARCHAR(255) NOT NULL,
- description TEXT NOT NULL,
- creation_date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
- last_updated_date TIMESTAMP NULL,
- project_id INT NOT NULL,
- leader_id INT NOT NULL,
- CONSTRAINT category_pk PRIMARY KEY (id),
- CONSTRAINT category_project FOREIGN KEY category_project (project_id) REFERENCES project (id),
- CONSTRAINT category_user FOREIGN KEY category_user (leader_id) REFERENCES user (id)
-);
-
--- Table requirement_developer_map
-CREATE TABLE IF NOT EXISTS reqbaz.requirement_developer_map (
- id INT NOT NULL AUTO_INCREMENT,
- requirement_id INT NOT NULL,
- user_id INT NOT NULL,
- creation_date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
- CONSTRAINT requirement_developer_map_pk PRIMARY KEY (id),
- CONSTRAINT requirement_developer_map_requirement FOREIGN KEY requirement_developer_map_requirement (requirement_id) REFERENCES requirement (id)
- ON DELETE CASCADE,
- CONSTRAINT requirement_developer_map_user FOREIGN KEY requirement_developer_map_user (user_id) REFERENCES user (id)
-);
-
--- Table follower_requirement_map
-CREATE TABLE IF NOT EXISTS reqbaz.requirement_follower_map (
- id INT NOT NULL AUTO_INCREMENT,
- requirement_id INT NOT NULL,
- user_id INT NOT NULL,
- creation_date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
- CONSTRAINT requirement_follower_map_pk PRIMARY KEY (id),
- CONSTRAINT requirement_follower_map_requirement FOREIGN KEY requirement_follower_map_requirement (requirement_id) REFERENCES requirement (id)
- ON DELETE CASCADE,
- CONSTRAINT requirement_follower_map_user FOREIGN KEY requirement_follower_user (user_id) REFERENCES user (id)
-);
-
--- Table category_follower_map
-CREATE TABLE IF NOT EXISTS reqbaz.category_follower_map (
- id INT NOT NULL AUTO_INCREMENT,
- category_id INT NOT NULL,
- user_id INT NOT NULL,
- creation_date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
- CONSTRAINT category_follower_map_pk PRIMARY KEY (id),
- CONSTRAINT category_follower_map_category FOREIGN KEY category_follower_map_category (category_id) REFERENCES category (id)
- ON DELETE CASCADE,
- CONSTRAINT category_follower_map_user FOREIGN KEY category_follower_map_user (user_id) REFERENCES user (id)
-);
-
--- Table project_follower_map
-CREATE TABLE IF NOT EXISTS reqbaz.project_follower_map (
- id INT NOT NULL AUTO_INCREMENT,
- project_id INT NOT NULL,
- user_id INT NOT NULL,
- creation_date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
- CONSTRAINT project_follower_map_pk PRIMARY KEY (id),
- CONSTRAINT project_follower_map_project FOREIGN KEY project_follower_map_project (project_id) REFERENCES project (id)
- ON DELETE CASCADE,
- CONSTRAINT project_follower_map_user FOREIGN KEY project_follower_map_user (user_id) REFERENCES user (id)
-);
-
--- Table privilege
-CREATE TABLE IF NOT EXISTS reqbaz.privilege (
- id INT NOT NULL AUTO_INCREMENT,
- name VARCHAR(100) NOT NULL,
- CONSTRAINT privilege_pk PRIMARY KEY (id)
-);
-
--- Table project
-CREATE TABLE IF NOT EXISTS reqbaz.project (
- id INT NOT NULL AUTO_INCREMENT,
- name VARCHAR(255) NOT NULL,
- description TEXT NOT NULL,
- visibility BOOLEAN NOT NULL,
- creation_date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
- last_updated_date TIMESTAMP NULL,
- leader_id INT NOT NULL,
- default_category_id INT NULL,
- CONSTRAINT project_pk PRIMARY KEY (id),
- CONSTRAINT project_category FOREIGN KEY project_category (default_category_id) REFERENCES category (id),
- CONSTRAINT project_user FOREIGN KEY project_user (leader_id) REFERENCES user (id)
-);
-
--- Table requirement
-CREATE TABLE IF NOT EXISTS reqbaz.requirement (
- id INT NOT NULL AUTO_INCREMENT,
- name VARCHAR(255) NOT NULL,
- description TEXT NULL,
- realized TIMESTAMP NULL DEFAULT NULL,
- creation_date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
- last_updated_date TIMESTAMP NULL,
- lead_developer_id INT NULL,
- creator_id INT NOT NULL,
- project_id INT NOT NULL,
- CONSTRAINT requirement_pk PRIMARY KEY (id),
- CONSTRAINT creator FOREIGN KEY creator (creator_id) REFERENCES user (id),
- CONSTRAINT lead_developer FOREIGN KEY lead_developer (lead_developer_id) REFERENCES user (id),
- CONSTRAINT requirement_project FOREIGN KEY requirement_project (project_id) REFERENCES project (id)
-);
-
--- Table requirement_category_map
-CREATE TABLE IF NOT EXISTS reqbaz.requirement_category_map (
- id INT NOT NULL AUTO_INCREMENT,
- category_id INT NOT NULL,
- requirement_id INT NOT NULL,
- CONSTRAINT requirement_category_map_pk PRIMARY KEY (id),
- CONSTRAINT requirement_category_map_category FOREIGN KEY requirement_category_map_category (category_id) REFERENCES category (id),
- CONSTRAINT requirement_category_map_requirement FOREIGN KEY requirement_category_map_requirement (requirement_id) REFERENCES requirement (id)
- ON DELETE CASCADE
-);
-
--- Table role_privilege_map
-CREATE TABLE IF NOT EXISTS reqbaz.role_privilege_map (
- id INT NOT NULL AUTO_INCREMENT,
- role_id INT NOT NULL,
- privilege_id INT NOT NULL,
- CONSTRAINT role_privilege_map_pk PRIMARY KEY (id),
- CONSTRAINT role_privilege_map_privilege FOREIGN KEY role_privilege_map_privilege (privilege_id) REFERENCES privilege (id)
- ON DELETE CASCADE,
- CONSTRAINT role_privilege_map_role FOREIGN KEY role_privilege_map_role (role_id) REFERENCES role (id)
- ON DELETE CASCADE
-);
-
--- Table role_role_map
-CREATE TABLE IF NOT EXISTS reqbaz.role_role_map (
- id INT NOT NULL AUTO_INCREMENT,
- child_id INT NOT NULL,
- parent_id INT NOT NULL,
- CONSTRAINT role_role_map_pk PRIMARY KEY (id),
- CONSTRAINT role_role_map_child FOREIGN KEY role_role_map_child (child_id) REFERENCES role (id)
- ON DELETE CASCADE,
- CONSTRAINT role_role_map_parent FOREIGN KEY role_role_map_parent (parent_id) REFERENCES role (id)
- ON DELETE CASCADE
-);
-
--- Table role
-CREATE TABLE IF NOT EXISTS reqbaz.role (
- id INT NOT NULL AUTO_INCREMENT,
- name VARCHAR(50) NULL,
- CONSTRAINT role_pk PRIMARY KEY (id),
- UNIQUE KEY role_idx_1 (name)
-);
-
--- Table user_role_map
-CREATE TABLE IF NOT EXISTS reqbaz.user_role_map (
- id INT NOT NULL AUTO_INCREMENT,
- role_id INT NOT NULL,
- user_id INT NOT NULL,
- context_info VARCHAR(255) NULL,
- CONSTRAINT user_role_map_pk PRIMARY KEY (id),
- CONSTRAINT user_role_map_role FOREIGN KEY user_role_map_role (role_id) REFERENCES role (id)
- ON DELETE CASCADE,
- CONSTRAINT user_role_map_user FOREIGN KEY user_role_map_user (user_id) REFERENCES user (id)
- ON DELETE CASCADE
-);
-
--- Table user
-CREATE TABLE IF NOT EXISTS reqbaz.user (
- id INT NOT NULL AUTO_INCREMENT,
- first_name VARCHAR(150) NULL,
- last_name VARCHAR(150) NULL,
- email VARCHAR(255) NOT NULL,
- admin BOOLEAN NOT NULL,
- las2peer_id BIGINT NOT NULL,
- user_name VARCHAR(255) NULL,
- profile_image TEXT NULL,
- email_lead_subscription BOOLEAN NOT NULL DEFAULT TRUE,
- email_follow_subscription BOOLEAN NOT NULL DEFAULT TRUE,
- creation_date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
- last_updated_date TIMESTAMP NULL,
- last_login_date TIMESTAMP NULL,
- CONSTRAINT user_pk PRIMARY KEY (id),
- UNIQUE KEY las2peer_idx (las2peer_id)
-);
-
--- Table vote
-CREATE TABLE IF NOT EXISTS reqbaz.vote (
- id INT NOT NULL AUTO_INCREMENT,
- is_upvote BOOLEAN NOT NULL,
- requirement_id INT NOT NULL,
- user_id INT NOT NULL,
- creation_date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
- CONSTRAINT vote_pk PRIMARY KEY (id),
- CONSTRAINT vote_requirement FOREIGN KEY vote_requirement (requirement_id) REFERENCES requirement (id)
- ON DELETE CASCADE,
- CONSTRAINT vote_user FOREIGN KEY vote_user (user_id) REFERENCES user (id)
-);
-
--- Fill roles and privileges
-REPLACE INTO reqbaz.role
-(id, name)
-VALUES
- (1, 'Anonymous'),
- (2, 'LoggedInUser'),
- (3, 'ProjectAdmin'),
- (4, 'SystemAdmin');
-
-REPLACE INTO reqbaz.privilege
-(id, name)
-VALUES
- (1, 'Create_PROJECT'),
- (2, 'Read_PROJECT'),
- (3, 'Read_PUBLIC_PROJECT'),
- (4, 'Modify_PROJECT'),
- (5, 'Create_CATEGORY'),
- (6, 'Read_CATEGORY'),
- (7, 'Read_PUBLIC_CATEGORY'),
- (8, 'Modify_CATEGORY'),
- (9, 'Create_REQUIREMENT'),
- (10, 'Read_REQUIREMENT'),
- (11, 'Read_PUBLIC_REQUIREMENT'),
- (12, 'Modify_REQUIREMENT'),
- (13, 'Create_COMMENT'),
- (14, 'Read_COMMENT'),
- (15, 'Read_PUBLIC_COMMENT'),
- (16, 'Modify_COMMENT'),
- (17, 'Create_ATTACHMENT'),
- (18, 'Read_ATTACHMENT'),
- (19, 'Read_PUBLIC_ATTACHMENT'),
- (20, 'Modify_ATTACHMENT'),
- (21, 'Create_VOTE'),
- (22, 'Delete_VOTE'),
- (23, 'Create_FOLLOW'),
- (24, 'Delete_FOLLOW'),
- (25, 'Create_DEVELOP'),
- (26, 'Delete_DEVELOP');
-
-REPLACE INTO reqbaz.role_privilege_map
-(id, role_id, privilege_id)
-VALUES
- (1, 1, 3),
- (2, 1, 7),
- (3, 1, 11),
- (4, 1, 15),
- (5, 1, 19),
- (6, 4, 1),
- (7, 4, 2),
- (8, 4, 8),
- (9, 4, 7),
- (10, 4, 6),
- (11, 4, 5),
- (12, 4, 3),
- (13, 4, 4),
- (14, 4, 9),
- (15, 4, 10),
- (16, 4, 11),
- (17, 4, 12),
- (18, 4, 13),
- (19, 4, 14),
- (20, 4, 16),
- (21, 4, 17),
- (22, 4, 18),
- (23, 4, 19),
- (24, 4, 20),
- (25, 4, 21),
- (26, 4, 22),
- (27, 4, 23),
- (28, 4, 24),
- (29, 4, 25),
- (30, 4, 26);
-
-REPLACE INTO reqbaz.role_role_map
-(id, child_id, parent_id)
-VALUES
- (1, 2, 1),
- (2, 3, 2),
- (3, 4, 3);
-
-REPLACE INTO reqbaz.user_role_map
-(id, role_id, user_id)
-VALUES
- (1, 1, 1);
-
-REPLACE INTO reqbaz.user
-(id, first_name, last_name, email, admin, las2peer_id, user_name, profile_image, email_lead_subscription, email_follow_subscription)
-VALUES
- (1, NULL, NULL, 'anonymous@requirements-bazaar.org', 0, '-1722613621014065292', 'anonymous',
- 'https://api.learning-layers.eu/profile.png', 0, 0);
-
-SET FOREIGN_KEY_CHECKS = 1;
\ No newline at end of file
diff --git a/etc/migrations/V2__user_las2peer_id_to_string.sql b/etc/migrations/V2__user_las2peer_id_to_string.sql
deleted file mode 100644
index 48c5493f..00000000
--- a/etc/migrations/V2__user_las2peer_id_to_string.sql
+++ /dev/null
@@ -1,7 +0,0 @@
-SET FOREIGN_KEY_CHECKS = 0;
-
-ALTER TABLE reqbaz.user change las2peer_id las2peer_id VARCHAR(128);
-
-UPDATE reqbaz.user SET las2peer_id = "anonymous" where id = 1;
-
-SET FOREIGN_KEY_CHECKS = 1;
\ No newline at end of file
diff --git a/etc/migrations/add_reqbaz_demo_data.sql b/etc/sample_data/add_reqbaz_demo_data.sql
similarity index 100%
rename from etc/migrations/add_reqbaz_demo_data.sql
rename to etc/sample_data/add_reqbaz_demo_data.sql
diff --git a/etc/migrations/add_reqbaz_demo_data_full.sql b/etc/sample_data/add_reqbaz_demo_data_full.sql
similarity index 100%
rename from etc/migrations/add_reqbaz_demo_data_full.sql
rename to etc/sample_data/add_reqbaz_demo_data_full.sql
diff --git a/gradle.properties b/gradle.properties
new file mode 100644
index 00000000..06ce347d
--- /dev/null
+++ b/gradle.properties
@@ -0,0 +1,14 @@
+org.gradle.parallel=true
+java.version=14
+core.version=1.1.2
+service.version=0.9.0
+service.name=de.rwth.dbis.acis.bazaar.service
+service.class=BazaarService
+jooq.version=3.14.4
+postgres.version=42.2.23
+liquibase.version=4.4.3
+db.port=5432
+db.hostname=localhost
+db.user=reqbaz
+db.password=reqbaz
+db.name=reqbaz
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 00000000..e708b1c0
Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 00000000..442d9132
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.3-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/gradlew b/gradlew
new file mode 100755
index 00000000..4f906e0c
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,185 @@
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=`expr $i + 1`
+ done
+ case $i in
+ 0) set -- ;;
+ 1) set -- "$args0" ;;
+ 2) set -- "$args0" "$args1" ;;
+ 3) set -- "$args0" "$args1" "$args2" ;;
+ 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=`save "$@"`
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+exec "$JAVACMD" "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100644
index 00000000..107acd32
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,89 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/reqbaz/build.gradle b/reqbaz/build.gradle
new file mode 100644
index 00000000..fc2e72ff
--- /dev/null
+++ b/reqbaz/build.gradle
@@ -0,0 +1,251 @@
+/*
+ * This file was generated by the Gradle 'init' task.
+ *
+ * This generated file contains a sample Java application project to get you started.
+ * For more details take a look at the 'Building Java & JVM projects' chapter in the Gradle
+ * User Manual available at https://docs.gradle.org/6.7/userguide/building_java_projects.html
+ */
+buildscript {
+ configurations["classpath"].resolutionStrategy.eachDependency {
+ if (requested.group == "org.jooq") {
+ useVersion("${project.property('jooq.version')}")
+ }
+ }
+}
+
+plugins {
+ // Apply the application plugin to add support for building a CLI application in Java.
+ id 'java'
+ id 'application'
+ id 'idea'
+ id "nu.studer.jooq" version "5.2.1"
+ id "io.freefair.lombok" version "5.3.0"
+ id 'com.github.ben-manes.versions' version '0.38.0'
+ id 'jacoco'
+ id 'org.liquibase.gradle' version '2.0.4'
+}
+
+application {
+ // Define the main class for the application.
+ mainClass = "${project.property('service.name')}.${project.property('service.class')}"
+
+ group = "${project.property('service.name')}"
+ archivesBaseName = group
+
+ version = "${project.property('service.version')}"
+ mainClassName = "i5.las2peer.tools.L2pNodeLauncher"
+ sourceCompatibility = "${project.property('java.version')}"
+ targetCompatibility = "${project.property('java.version')}"
+}
+
+repositories {
+ // Use JCenter for resolving dependencies.
+ jcenter()
+
+ // DBIS Archiva
+ maven {
+ url "https://archiva.dbis.rwth-aachen.de:9911/repository/internal/"
+ }
+}
+
+dependencies {
+ // Use JUnit test framework.
+ testImplementation 'junit:junit:4.13'
+
+ liquibaseRuntime "org.liquibase:liquibase-core:${project.property('liquibase.version')}"
+ liquibaseRuntime 'org.yaml:snakeyaml:1.29'
+ liquibaseRuntime "org.postgresql:postgresql:${project.property('postgres.version')}"
+ liquibaseRuntime 'javax.xml.bind:jaxb-api:2.3.1'
+
+ // las2peer bundle which is not necessary in the runtime path
+ // compileOnly will be moved into the lib dir afterwards
+ compileOnly "i5:las2peer-bundle:${project.property('core.version')}"
+ compileOnly "org.postgresql:postgresql:${project.property('postgres.version')}"
+ compileOnly 'org.hibernate:hibernate-validator:5.4.3.Final'
+ compileOnly 'org.glassfish:jakarta.el:3.0.3'
+ compileOnly 'javax.validation:validation-api:1.1.0.Final'
+
+ // This is for the jooq generation only
+ jooqGenerator "org.jooq:jooq-codegen:${project.property('jooq.version')}"
+
+ implementation 'com.google.code.gson:gson:2.8.6'
+ implementation 'org.apache.commons:commons-pool2:2.9.0'
+ implementation 'org.apache.commons:commons-dbcp2:2.8.0'
+ implementation "org.jooq:jooq:${project.property('jooq.version')}"
+ implementation "org.jooq:jooq-meta:${project.property('jooq.version')}"
+ implementation 'org.apache.httpcomponents:httpclient:4.5.13'
+ implementation 'commons-io:commons-io:2.8.0'
+ implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.8.9'
+}
+
+configurations {
+ // This ensures las2peer is available in the tests, but won't be bundled
+ testCompile.extendsFrom compileOnly
+ jooqGenerator.extendsFrom liquibaseRuntime
+}
+
+test {
+ finalizedBy jacocoTestReport // report is always generated after tests run
+}
+
+jacocoTestReport {
+ dependsOn test // tests are required to run before generating the report
+ reports {
+ xml.enabled true
+ }
+}
+
+jar {
+ manifest {
+ attributes "Main-Class": "${project.property('service.name')}.${project.property('service.class')}"
+ attributes "Library-Version": "${project.property('service.version')}"
+ attributes "Library-SymbolicName": "${project.property('service.name')}"
+ }
+
+ from { (configurations.runtimeClasspath).collect { it.isDirectory() ? it : zipTree(it) } } {
+ // Exclude signatures to be able to natively bundle signed jars
+ exclude 'META-INF/*.RSA', 'META-INF/*.SF', 'META-INF/*.DSA'
+ }
+}
+
+// These two tasks restore the build and runtime environment used
+// in the ant environment
+task copyJar(type: Copy) {
+ from jar // here it automatically reads jar file produced from jar task
+ into '../service'
+}
+
+// Maybe there is a more idiomatic way to separate out las2peer
+task copyToLib(type: Copy) {
+ from configurations.compileOnly
+ into "../lib"
+}
+
+task startscripts {
+ new File("$rootDir/bin", "start_network.sh").text = """#!/bin/bash
+# this script is autogenerated by 'gradle startscripts'
+# it starts a las2peer node providing the service '${project.property('service.name')}.${project.property('service.class')}' of this project
+# pls execute it from the root folder of your deployment, e. g. ./bin/start_network.sh
+java -cp "lib/*" i5.las2peer.tools.L2pNodeLauncher --port 9011 --service-directory service uploadStartupDirectory startService\\(\\'${project.property('service.name')}.${project.property('service.class')}@${project.property('service.version')}\\'\\) startWebConnector interactive
+"""
+ new File("$rootDir/bin", "start_network.bat").text = """:: this script is autogenerated by 'gradle startscripts'
+:: it starts a las2peer node providing the service '${project.property('service.name')}.${project.property('service.class')}' of this project
+:: pls execute it from the bin folder of your deployment by double-clicking on it
+%~d0
+cd %~p0
+cd ..
+set BASE=%CD%
+set CLASSPATH="%BASE%/lib/*;"
+java -cp %CLASSPATH% i5.las2peer.tools.L2pNodeLauncher --port 9011 --service-directory service uploadStartupDirectory startService('${project.property('service.name')}.${project.property('service.class')}@${project.property('service.version')}') startWebConnector interactive
+pause
+"""
+}
+
+build.dependsOn javadoc
+build.dependsOn copyJar
+build.dependsOn copyToLib
+build.dependsOn startscripts
+
+
+// Flyway and jooq configuration for database management
+jooq {
+ version = "${project.property('jooq.version')}"
+ configurations {
+ main { // name of the jOOQ configuration
+ generationTool {
+ logging = org.jooq.meta.jaxb.Logging.WARN
+ jdbc {
+ driver = 'org.postgresql.Driver'
+ url = "jdbc:postgresql://${project.property('db.hostname')}:${project.property('db.port')}/${project.property('db.name')}"
+ user = "${project.property('db.user')}"
+ password = "${project.property('db.password')}"
+ }
+ generator {
+ name = 'org.jooq.codegen.DefaultGenerator'
+ database {
+ name = 'org.jooq.meta.postgres.PostgresDatabase'
+ inputSchema = "public"
+ }
+ generate {
+ deprecated = false
+ records = true
+ immutablePojos = false
+ fluentSetters = true
+ }
+ target {
+ packageName = 'de.rwth.dbis.acis.bazaar.dal.jooq'
+ directory = 'build/generated/jooq/main'
+ }
+ strategy.name = 'org.jooq.codegen.DefaultGeneratorStrategy'
+ }
+ }
+ }
+ }
+}
+
+// liquibase migrations
+generateJooq.dependsOn dropAll
+generateJooq.dependsOn update
+
+liquibase {
+ activities {
+ main {
+ defaultSchemaName 'public'
+ changeLogFile "changelog.yaml"
+ classpath "$projectDir/src/main/resources"
+ url "jdbc:postgresql://${project.property('db.hostname')}:${project.property('db.port')}/${project.property('db.name')}"
+ username "${project.property('db.user')}"
+ password "${project.property('db.password')}"
+ }
+ }
+}
+
+
+// Build export directory which can be shipped on request
+task export(type: Copy, dependsOn: build) {
+ into "$buildDir/export"
+
+ into("service") {
+ from jar
+ }
+ into("lib") {
+ from configurations.compileOnly
+ }
+ into("bin") {
+ from "../bin"
+ }
+ into("etc") {
+ from "../etc"
+ }
+ into("javadoc") {
+ from "$buildDir/docs/javadoc"
+ }
+ into("sql") {
+ from processResources
+ include "*.sql"
+ include "changelog.yaml"
+ }
+}
+
+task packageDistribution(type: Zip, dependsOn: export) {
+ archiveFileName = "${project.property('service.class')}.zip"
+ destinationDirectory = file("$buildDir/dist")
+
+ from "$buildDir/export"
+}
+
+// Cleanup
+clean.doLast {
+ file("$rootDir/lib").deleteDir()
+ file("$rootDir/service").deleteDir()
+}
+
+task cleanAll {
+ dependsOn "clean"
+
+ doLast {
+ file("$rootDir/log").deleteDir()
+ file("$projectDir/log").deleteDir()
+ file("$rootDir/node-storage").deleteDir()
+ }
+}
diff --git a/reqbaz/etc/de.rwth.dbis.acis.bazaar.service.BazaarService.properties b/reqbaz/etc/de.rwth.dbis.acis.bazaar.service.BazaarService.properties
new file mode 100644
index 00000000..194a91bb
--- /dev/null
+++ b/reqbaz/etc/de.rwth.dbis.acis.bazaar.service.BazaarService.properties
@@ -0,0 +1,13 @@
+dbUserName=reqbaz
+dbPassword=reqbaz
+dbUrl=jdbc:postgresql://localhost:5432/reqbaz
+lang=en
+country=us
+baseURL=http://localhost:8080/bazaar/
+frontendBaseURL=http://localhost:5000/
+activityTrackerService=de.rwth.dbis.acis.activitytracker.service.ActivityTrackerService@0.8.0
+activityOrigin=https://requirements-bazaar.org
+smtpServer=
+emailFromAddress=
+emailSummaryTimePeriodInMinutes=
+monitor=''
diff --git a/reqbaz/lombok.config b/reqbaz/lombok.config
new file mode 100644
index 00000000..189c0bef
--- /dev/null
+++ b/reqbaz/lombok.config
@@ -0,0 +1,3 @@
+# This file is generated by the 'io.freefair.lombok' Gradle plugin
+config.stopBubbling = true
+lombok.addLombokGeneratedAnnotation = true
diff --git a/src/main/de/rwth/dbis/acis/bazaar/service/BazaarFunction.java b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/BazaarFunction.java
similarity index 93%
rename from src/main/de/rwth/dbis/acis/bazaar/service/BazaarFunction.java
rename to reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/BazaarFunction.java
index 1f635169..a6fd44af 100644
--- a/src/main/de/rwth/dbis/acis/bazaar/service/BazaarFunction.java
+++ b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/BazaarFunction.java
@@ -21,7 +21,6 @@
package de.rwth.dbis.acis.bazaar.service;
/**
- * @author Adam Gavronek
* @since 1/9/2015
*/
public enum BazaarFunction {
diff --git a/src/main/de/rwth/dbis/acis/bazaar/service/BazaarFunctionRegistrar.java b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/BazaarFunctionRegistrar.java
similarity index 94%
rename from src/main/de/rwth/dbis/acis/bazaar/service/BazaarFunctionRegistrar.java
rename to reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/BazaarFunctionRegistrar.java
index 28d48358..70d7e925 100644
--- a/src/main/de/rwth/dbis/acis/bazaar/service/BazaarFunctionRegistrar.java
+++ b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/BazaarFunctionRegistrar.java
@@ -23,7 +23,6 @@
import java.util.EnumSet;
/**
- * @author Adam Gavronek
* @since 1/9/2015
*/
public interface BazaarFunctionRegistrar {
diff --git a/src/main/de/rwth/dbis/acis/bazaar/service/BazaarService.java b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/BazaarService.java
similarity index 81%
rename from src/main/de/rwth/dbis/acis/bazaar/service/BazaarService.java
rename to reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/BazaarService.java
index 0c4c0254..6bc9130b 100755
--- a/src/main/de/rwth/dbis/acis/bazaar/service/BazaarService.java
+++ b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/BazaarService.java
@@ -22,11 +22,14 @@
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import de.rwth.dbis.acis.bazaar.service.dal.DALFacade;
import de.rwth.dbis.acis.bazaar.service.dal.DALFacadeImpl;
import de.rwth.dbis.acis.bazaar.service.dal.entities.Activity;
import de.rwth.dbis.acis.bazaar.service.dal.entities.Statistic;
+import de.rwth.dbis.acis.bazaar.service.dal.entities.SystemRole;
import de.rwth.dbis.acis.bazaar.service.dal.entities.User;
+import de.rwth.dbis.acis.bazaar.service.dal.helpers.CreateValidation;
import de.rwth.dbis.acis.bazaar.service.dal.helpers.PaginationResult;
import de.rwth.dbis.acis.bazaar.service.exception.BazaarException;
import de.rwth.dbis.acis.bazaar.service.exception.ErrorCode;
@@ -37,6 +40,7 @@
import de.rwth.dbis.acis.bazaar.service.notification.EmailDispatcher;
import de.rwth.dbis.acis.bazaar.service.notification.NotificationDispatcher;
import de.rwth.dbis.acis.bazaar.service.notification.NotificationDispatcherImp;
+import de.rwth.dbis.acis.bazaar.service.resources.*;
import de.rwth.dbis.acis.bazaar.service.security.AuthorizationManager;
import i5.las2peer.api.Context;
import i5.las2peer.api.ManualDeployment;
@@ -49,12 +53,15 @@
import i5.las2peer.restMapper.RESTService;
import i5.las2peer.restMapper.annotations.ServicePath;
import io.swagger.annotations.*;
-import jodd.vtor.Vtor;
import org.apache.commons.dbcp2.BasicDataSource;
import org.apache.http.client.utils.URIBuilder;
import org.jooq.SQLDialect;
import javax.sql.DataSource;
+import javax.validation.ConstraintViolation;
+import javax.validation.Validation;
+import javax.validation.Validator;
+import javax.validation.ValidatorFactory;
import javax.ws.rs.*;
import javax.ws.rs.core.Link;
import javax.ws.rs.core.MediaType;
@@ -62,6 +69,7 @@
import javax.xml.bind.DatatypeConverter;
import java.net.HttpURLConnection;
import java.net.URISyntaxException;
+import java.time.OffsetDateTime;
import java.util.*;
@@ -76,6 +84,13 @@
@ServicePath("/bazaar")
public class BazaarService extends RESTService {
+ private static final ObjectMapper mapper = new ObjectMapper().registerModule(new JavaTimeModule()).setSerializationInclusion(JsonInclude.Include.NON_NULL);
+ private final L2pLogger logger = L2pLogger.getInstance(BazaarService.class.getName());
+ private final ValidatorFactory validatorFactory;
+ private final List functionRegistrar;
+ private final NotificationDispatcher notificationDispatcher;
+ private final DataSource dataSource;
+
//CONFIG PROPERTIES
protected String dbUserName;
protected String dbPassword;
@@ -90,34 +105,15 @@ public class BazaarService extends RESTService {
protected String emailFromAddress;
protected String emailSummaryTimePeriodInMinutes;
- private Vtor vtor;
- private List functionRegistrar;
- private NotificationDispatcher notificationDispatcher;
- private DataSource dataSource;
-
- private final L2pLogger logger = L2pLogger.getInstance(BazaarService.class.getName());
- private static ObjectMapper mapper = new ObjectMapper().setSerializationInclusion(JsonInclude.Include.NON_NULL);
-
- @Override
- protected void initResources() {
- getResourceConfig().register(Resource.class);
- getResourceConfig().register(ProjectsResource.class);
- getResourceConfig().register(CategoryResource.class);
- getResourceConfig().register(RequirementsResource.class);
- getResourceConfig().register(CommentsResource.class);
- getResourceConfig().register(AttachmentsResource.class);
- getResourceConfig().register(UsersResource.class);
- }
-
public BazaarService() throws Exception {
setFieldValues();
Locale locale = new Locale(lang, country);
Localization.getInstance().setResourceBundle(ResourceBundle.getBundle("i18n.Translation", locale));
- Class.forName("com.mysql.jdbc.Driver").newInstance();
-
dataSource = setupDataSource(dbUrl, dbUserName, dbPassword);
+ validatorFactory = Validation.buildDefaultValidatorFactory();
+
functionRegistrar = new ArrayList<>();
functionRegistrar.add(functions -> {
DALFacade dalFacade = null;
@@ -133,12 +129,6 @@ public BazaarService() throws Exception {
}
});
- functionRegistrar.add(functions -> {
- if (functions.contains(BazaarFunction.VALIDATION)) {
- createValidators();
- }
- });
-
functionRegistrar.add(functions -> {
if (functions.contains(BazaarFunction.USER_FIRST_LOGIN_HANDLING)) {
registerUserAtFirstLogin();
@@ -170,11 +160,169 @@ public BazaarService() throws Exception {
notificationDispatcher.setBazaarService(this);
}
+ public static DataSource setupDataSource(String dbUrl, String dbUserName, String dbPassword) {
+ BasicDataSource dataSource = new BasicDataSource();
+ dataSource.setUrl(dbUrl);
+ dataSource.setUsername(dbUserName);
+ dataSource.setPassword(dbPassword);
+ dataSource.setValidationQuery("SELECT 1;");
+ dataSource.setTestOnBorrow(true); // test each connection when borrowing from the pool with the validation query
+ dataSource.setMaxConnLifetimeMillis(1000 * 60 * 60); // max connection life time 1h. mysql drops connection after 8h.
+ return dataSource;
+ }
+
+ @Override
+ protected void initResources() {
+ getResourceConfig().register(Resource.class);
+ getResourceConfig().register(ProjectsResource.class);
+ getResourceConfig().register(CategoryResource.class);
+ getResourceConfig().register(RequirementsResource.class);
+ getResourceConfig().register(CommentsResource.class);
+ getResourceConfig().register(UsersResource.class);
+ //getResourceConfig().register(PersonalisationDataResource.class);
+ getResourceConfig().register(FeedbackResource.class);
+ }
+
+ public String getBaseURL() {
+ return baseURL;
+ }
+
+ public String notifyRegistrars(EnumSet functions) {
+ String resultJSON = null;
+ try {
+ for (BazaarFunctionRegistrar functionRegistrar : functionRegistrar) {
+ functionRegistrar.registerFunction(functions);
+ }
+ } catch (BazaarException bazaarEx) {
+ resultJSON = ExceptionHandler.getInstance().toJSON(bazaarEx);
+ } catch (Exception ex) {
+ BazaarException bazaarException = ExceptionHandler.getInstance().convert(ex, ExceptionLocation.BAZAARSERVICE, ErrorCode.UNKNOWN, Localization.getInstance().getResourceBundle().getString("error.registrars"));
+ resultJSON = ExceptionHandler.getInstance().toJSON(bazaarException);
+ }
+ return resultJSON;
+ }
+
+ public Set> validate(Object entity) {
+ Validator validator = validatorFactory.getValidator();
+ // Take Object for generic error handling
+ return validator.validate(entity);
+ }
+
+ public Set> validateCreate(Object entity) {
+ Validator validator = validatorFactory.getValidator();
+ // Take Object for generic error handling
+ return validator.validate(entity, CreateValidation.class);
+ }
+
+ public NotificationDispatcher getNotificationDispatcher() {
+ return notificationDispatcher;
+ }
+
+ private void registerUserAtFirstLogin() throws Exception {
+ Agent agent = Context.getCurrent().getMainAgent();
+
+ String loginName = null;
+ String email = null;
+ String profileImage = "https://api.learning-layers.eu/profile.png";
+
+ if (agent instanceof AnonymousAgent) {
+ loginName = ((AnonymousAgent) agent).LOGIN_NAME;
+ email = "NO.EMAIL@WARNING.COM";
+ } else if (agent instanceof UserAgent) {
+ loginName = ((UserAgent) agent).getLoginName();
+ if (((UserAgent) agent).getEmail() == null) {
+ email = "NO.EMAIL@WARNING.COM";
+ } else {
+ email = ((UserAgent) agent).getEmail();
+ }
+ }
+
+ DALFacade dalFacade = null;
+ try {
+ dalFacade = getDBConnection();
+ Integer userIdByLAS2PeerId = dalFacade.getUserIdByLAS2PeerId(agent.getIdentifier());
+ if (userIdByLAS2PeerId == null) {
+ // create user
+ User user = User.builder()
+ .eMail(email)
+ .las2peerId(agent.getIdentifier())
+ .userName(loginName)
+ .profileImage(profileImage)
+ .emailLeadSubscription(true)
+ .emailFollowSubscription(true)
+ .personalizationEnabled(false)
+ .build();
+ user = dalFacade.createUser(user);
+ int userId = user.getId();
+ // this.getNotificationDispatcher().dispatchNotification(user.getCreationDate(), Activity.ActivityAction.CREATE, MonitoringEvent.SERVICE_CUSTOM_MESSAGE_55, userId, Activity.DataType.USER, userId);
+ dalFacade.addUserToRole(userId, "LoggedInUser", null);
+ } else {
+ // update lastLoginDate
+ dalFacade.updateLastLoginDate(userIdByLAS2PeerId);
+ }
+ } catch (Exception ex) {
+ ExceptionHandler.getInstance().convertAndThrowException(ex, ExceptionLocation.BAZAARSERVICE, ErrorCode.UNKNOWN, Localization.getInstance().getResourceBundle().getString("error.first_login"));
+ logger.warning(ex.getMessage());
+ } finally {
+ closeDBConnection(dalFacade);
+ }
+ }
+
+ public DALFacade getDBConnection() throws Exception { // TODO: Specify Exception
+ return new DALFacadeImpl(dataSource, SQLDialect.POSTGRES);
+ }
+
+ public void closeDBConnection(DALFacade dalFacade) {
+ if (dalFacade == null) {
+ return;
+ }
+ dalFacade.close();
+ }
+
+ public ObjectMapper getMapper() {
+ return mapper;
+ }
+
+ public Response.ResponseBuilder paginationLinks(Response.ResponseBuilder responseBuilder, PaginationResult paginationResult,
+ String path, Map> httpParameter) throws URISyntaxException {
+ List links = new ArrayList<>();
+ URIBuilder uriBuilder = new URIBuilder(baseURL + path);
+ for (Map.Entry> entry : httpParameter.entrySet()) {
+ for (String parameter : entry.getValue()) {
+ uriBuilder.addParameter(entry.getKey(), parameter);
+ }
+ }
+ if (paginationResult.getPrevPage() != -1) {
+ links.add(Link.fromUri(uriBuilder.setParameter("page", String.valueOf(paginationResult.getPrevPage())).build()).rel("prev").build());
+ }
+ if (paginationResult.getNextPage() != -1) {
+ links.add(Link.fromUri(uriBuilder.setParameter("page", String.valueOf(paginationResult.getNextPage())).build()).rel("next").build());
+ }
+ links.add(Link.fromUri(uriBuilder.setParameter("page", "0").build()).rel("first").build());
+ links.add(Link.fromUri(uriBuilder.setParameter("page", String.valueOf(paginationResult.getTotalPages())).build()).rel("last").build());
+ responseBuilder = responseBuilder.links(links.toArray(new Link[links.size()]));
+ return responseBuilder;
+ }
+
+ public Response.ResponseBuilder xHeaderFields(Response.ResponseBuilder responseBuilder, PaginationResult paginationResult) {
+ responseBuilder = responseBuilder.header("X-Page", String.valueOf(paginationResult.getPageable().getPageNumber()));
+ responseBuilder = responseBuilder.header("X-Per-Page", String.valueOf(paginationResult.getPageable().getPageSize()));
+ if (paginationResult.getPrevPage() != -1) {
+ responseBuilder = responseBuilder.header("X-Prev-Page", String.valueOf(paginationResult.getPrevPage()));
+ }
+ if (paginationResult.getNextPage() != -1) {
+ responseBuilder = responseBuilder.header("X-Next-Page", String.valueOf(paginationResult.getNextPage()));
+ }
+ responseBuilder = responseBuilder.header("X-Total-Pages", String.valueOf(paginationResult.getTotalPages()));
+ responseBuilder = responseBuilder.header("X-Total", String.valueOf(paginationResult.getTotal()));
+ return responseBuilder;
+ }
+
@Api(value = "/", description = "Bazaar service")
@SwaggerDefinition(
info = @Info(
title = "Requirements Bazaar",
- version = "0.8.0",
+ version = "0.9.0",
description = "Requirements Bazaar project",
termsOfService = "http://requirements-bazaar.org",
contact = @Contact(
@@ -248,7 +396,7 @@ public Response getStatistics(
Integer internalUserId = dalFacade.getUserIdByLAS2PeerId(userId);
Calendar sinceCal = since == null ? null : DatatypeConverter.parseDateTime(since);
Statistic platformStatistics = dalFacade.getStatisticsForAllProjects(internalUserId, sinceCal);
- bazaarService.getNotificationDispatcher().dispatchNotification(new Date(), Activity.ActivityAction.RETRIEVE, MonitoringEvent.SERVICE_CUSTOM_MESSAGE_2,
+ bazaarService.getNotificationDispatcher().dispatchNotification(OffsetDateTime.now(), Activity.ActivityAction.RETRIEVE, MonitoringEvent.SERVICE_CUSTOM_MESSAGE_2,
0, Activity.DataType.STATISTIC, internalUserId);
return Response.ok(platformStatistics.toJSON()).build();
} catch (BazaarException bex) {
@@ -279,15 +427,35 @@ public Response getStatistics(
@Path("/notifications")
@ApiOperation(value = "This method sends all notifications (emails) in the waiting queue. Run this method before shutting down Requirements Bazaar.")
@ApiResponses(value = {
- @ApiResponse(code = HttpURLConnection.HTTP_CREATED, message = "Notifications send"),
+ @ApiResponse(code = HttpURLConnection.HTTP_OK, message = "Notifications send"),
@ApiResponse(code = HttpURLConnection.HTTP_UNAUTHORIZED, message = "Unauthorized"),
@ApiResponse(code = HttpURLConnection.HTTP_INTERNAL_ERROR, message = "Internal server problems")
})
public Response sendNotifications() {
- // TODO: Use authorization scopes to limit users who can run this method to admins
+ DALFacade dalFacade = null;
try {
+ Agent agent = Context.getCurrent().getMainAgent();
+ String userId = agent.getIdentifier();
+ String registrarErrors = bazaarService.notifyRegistrars(EnumSet.of(BazaarFunction.VALIDATION, BazaarFunction.USER_FIRST_LOGIN_HANDLING));
+ if (registrarErrors != null) {
+ ExceptionHandler.getInstance().throwException(ExceptionLocation.BAZAARSERVICE, ErrorCode.UNKNOWN, registrarErrors);
+ }
+ dalFacade = bazaarService.getDBConnection();
+ Integer internalUserId = dalFacade.getUserIdByLAS2PeerId(userId);
+
+ boolean authorized = new AuthorizationManager().isAuthorized(internalUserId, dalFacade.getRoleByName(SystemRole.SystemAdmin.name()), dalFacade);
+ if (!authorized) {
+ ExceptionHandler.getInstance().throwException(ExceptionLocation.BAZAARSERVICE, ErrorCode.AUTHORIZATION, Localization.getInstance().getResourceBundle().getString("error.authorization.notifications"));
+ }
bazaarService.notificationDispatcher.run();
- return Response.status(Response.Status.CREATED).build();
+ return Response.status(Response.Status.OK).build();
+ } catch (BazaarException bex) {
+ if (bex.getErrorCode() == ErrorCode.AUTHORIZATION) {
+ return Response.status(Response.Status.UNAUTHORIZED).entity(ExceptionHandler.getInstance().toJSON(bex)).build();
+ } else {
+ Context.get().monitorEvent(MonitoringEvent.SERVICE_ERROR, "Flushing notifications");
+ return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(ExceptionHandler.getInstance().toJSON(bex)).build();
+ }
} catch (Exception ex) {
BazaarException bex = ExceptionHandler.getInstance().convert(ex, ExceptionLocation.BAZAARSERVICE, ErrorCode.UNKNOWN, ex.getMessage());
Context.get().monitorEvent(MonitoringEvent.SERVICE_ERROR, "Send Notifications failed");
@@ -297,132 +465,4 @@ public Response sendNotifications() {
}
}
- public String notifyRegistrars(EnumSet functions) {
- String resultJSON = null;
- try {
- for (BazaarFunctionRegistrar functionRegistrar : functionRegistrar) {
- functionRegistrar.registerFunction(functions);
- }
- } catch (BazaarException bazaarEx) {
- resultJSON = ExceptionHandler.getInstance().toJSON(bazaarEx);
- } catch (Exception ex) {
- BazaarException bazaarException = ExceptionHandler.getInstance().convert(ex, ExceptionLocation.BAZAARSERVICE, ErrorCode.UNKNOWN, Localization.getInstance().getResourceBundle().getString("error.registrars"));
- resultJSON = ExceptionHandler.getInstance().toJSON(bazaarException);
- }
- return resultJSON;
- }
-
- private void createValidators() {
- vtor = new Vtor();
- }
-
- public Vtor getValidators() {
- return vtor;
- }
-
- public NotificationDispatcher getNotificationDispatcher() {
- return notificationDispatcher;
- }
-
- private void registerUserAtFirstLogin() throws Exception {
- Agent agent = Context.getCurrent().getMainAgent();
-
- String loginName = null;
- String email = null;
- String profileImage = "https://api.learning-layers.eu/profile.png";
-
- if (agent instanceof AnonymousAgent) {
- loginName = ((AnonymousAgent) agent).LOGIN_NAME;
- email = "NO.EMAIL@WARNING.COM";
- } else if (agent instanceof UserAgent) {
- loginName = ((UserAgent) agent).getLoginName();
- if (((UserAgent) agent).getEmail() == null) {
- email = "NO.EMAIL@WARNING.COM";
- } else {
- email = ((UserAgent) agent).getEmail();
- }
- }
-
- DALFacade dalFacade = null;
- try {
- dalFacade = getDBConnection();
- Integer userIdByLAS2PeerId = dalFacade.getUserIdByLAS2PeerId(agent.getIdentifier());
- if (userIdByLAS2PeerId == null) {
- // create user
- User.Builder userBuilder = User.geBuilder(email);
- User user = userBuilder.admin(false).las2peerId(agent.getIdentifier()).userName(loginName).profileImage(profileImage)
- .emailLeadSubscription(true).emailFollowSubscription(true).build();
- user = dalFacade.createUser(user);
- int userId = user.getId();
- this.getNotificationDispatcher().dispatchNotification(user.getCreationDate(), Activity.ActivityAction.CREATE, MonitoringEvent.SERVICE_CUSTOM_MESSAGE_55,
- userId, Activity.DataType.USER, userId);
- dalFacade.addUserToRole(userId, "SystemAdmin", null);
- } else {
- // update lastLoginDate
- dalFacade.updateLastLoginDate(userIdByLAS2PeerId);
- }
- } catch (Exception ex) {
- ExceptionHandler.getInstance().convertAndThrowException(ex, ExceptionLocation.BAZAARSERVICE, ErrorCode.UNKNOWN, Localization.getInstance().getResourceBundle().getString("error.first_login"));
- logger.warning(ex.getMessage());
- } finally {
- closeDBConnection(dalFacade);
- }
- }
-
- public static DataSource setupDataSource(String dbUrl, String dbUserName, String dbPassword) {
- BasicDataSource dataSource = new BasicDataSource();
- dataSource.setDriverClassName("com.mysql.jdbc.Driver");
- dataSource.setUrl(dbUrl + "?useSSL=false&serverTimezone=UTC");
- dataSource.setUsername(dbUserName);
- dataSource.setPassword(dbPassword);
- dataSource.setValidationQuery("SELECT 1;");
- dataSource.setTestOnBorrow(true); // test each connection when borrowing from the pool with the validation query
- dataSource.setMaxConnLifetimeMillis(1000 * 60 * 60); // max connection life time 1h. mysql drops connection after 8h.
- return dataSource;
- }
-
- public DALFacade getDBConnection() throws Exception { // TODO: Specify Exception
- return new DALFacadeImpl(dataSource, SQLDialect.MYSQL);
- }
-
- public void closeDBConnection(DALFacade dalFacade) {
- if (dalFacade == null) return;
- dalFacade.close();
- }
-
- public Response.ResponseBuilder paginationLinks(Response.ResponseBuilder responseBuilder, PaginationResult paginationResult,
- String path, Map> httpParameter) throws URISyntaxException {
- List links = new ArrayList<>();
- URIBuilder uriBuilder = new URIBuilder(baseURL + path);
- for (Map.Entry> entry : httpParameter.entrySet()) {
- for (String parameter : entry.getValue()) {
- uriBuilder.addParameter(entry.getKey(), parameter);
- }
- }
- if (paginationResult.getPrevPage() != -1) {
- links.add(Link.fromUri(uriBuilder.setParameter("page", String.valueOf(paginationResult.getPrevPage())).build()).rel("prev").build());
- }
- if (paginationResult.getNextPage() != -1) {
- links.add(Link.fromUri(uriBuilder.setParameter("page", String.valueOf(paginationResult.getNextPage())).build()).rel("next").build());
- }
- links.add(Link.fromUri(uriBuilder.setParameter("page", "0").build()).rel("first").build());
- links.add(Link.fromUri(uriBuilder.setParameter("page", String.valueOf(paginationResult.getTotalPages())).build()).rel("last").build());
- responseBuilder = responseBuilder.links(links.toArray(new Link[links.size()]));
- return responseBuilder;
- }
-
- public Response.ResponseBuilder xHeaderFields(Response.ResponseBuilder responseBuilder, PaginationResult paginationResult) {
- responseBuilder = responseBuilder.header("X-Page", String.valueOf(paginationResult.getPageable().getPageNumber()));
- responseBuilder = responseBuilder.header("X-Per-Page", String.valueOf(paginationResult.getPageable().getPageSize()));
- if (paginationResult.getPrevPage() != -1) {
- responseBuilder = responseBuilder.header("X-Prev-Page", String.valueOf(paginationResult.getPrevPage()));
- }
- if (paginationResult.getNextPage() != -1) {
- responseBuilder = responseBuilder.header("X-Next-Page", String.valueOf(paginationResult.getNextPage()));
- }
- responseBuilder = responseBuilder.header("X-Total-Pages", String.valueOf(paginationResult.getTotalPages()));
- responseBuilder = responseBuilder.header("X-Total", String.valueOf(paginationResult.getTotal()));
- return responseBuilder;
- }
-
}
diff --git a/src/main/de/rwth/dbis/acis/bazaar/service/dal/DALFacade.java b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/DALFacade.java
similarity index 74%
rename from src/main/de/rwth/dbis/acis/bazaar/service/dal/DALFacade.java
rename to reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/DALFacade.java
index d88dcc65..c4ba83be 100644
--- a/src/main/de/rwth/dbis/acis/bazaar/service/dal/DALFacade.java
+++ b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/DALFacade.java
@@ -135,6 +135,15 @@ public interface DALFacade {
* @return list of users to receive email notification
*/
List getRecipientListForRequirement(int requirementId) throws BazaarException;
+
+ /**
+ * Search for users with a given search term
+ *
+ * @param pageInfo
+ * @return
+ */
+ PaginationResult searchUsers(PageInfo pageInfo) throws BazaarException;
+
//endregion
//region Project
@@ -175,6 +184,14 @@ public interface DALFacade {
*/
Project modifyProject(Project modifiedProject) throws Exception;
+ /**
+ * Deletes a given project
+ *
+ * @param projectId id of the project to delete
+ * @param userId id of the user
+ */
+ Project deleteProjectById(int projectId, Integer userId) throws Exception;
+
/**
* Returns if a project is public or not
*
@@ -193,6 +210,33 @@ public interface DALFacade {
Statistic getStatisticsForAllProjects(int userId, Calendar since) throws BazaarException;
Statistic getStatisticsForProject(int userId, int projectId, Calendar since) throws BazaarException;
+
+ /**
+ * Get the members of a project and their according role
+ *
+ * @param projectId
+ * @return List of projectmembers in the project
+ */
+ PaginationResult getProjectMembers(int projectId, Pageable pageable) throws BazaarException;
+
+ /**
+ * Allows to remove a role from a user
+ *
+ * @param userId
+ * @param context
+ * @throws BazaarException
+ */
+ void removeUserFromProject(int userId, Integer context) throws BazaarException;
+
+ /**
+ * Returns the count most recent active projects followed by the user
+ *
+ * @param userId id of the follower
+ * @param count how many should be returned
+ * @return Followed projects ordered by last activity
+ */
+ List getFollowedProjects(int userId, int count) throws BazaarException;
+
//endregion
//region ProjectFollower
@@ -248,6 +292,13 @@ public interface DALFacade {
*/
PaginationResult listRequirementsByCategory(int categoryId, Pageable pageable, int userId) throws BazaarException;
+ /**
+ * @param pageable pagination information
+ * @param userId
+ * @return the requirements filtered by pageable
+ */
+ PaginationResult listAllRequirements(Pageable pageable, int userId) throws BazaarException;
+
/**
* @param requirementId the identifier of the requirement should be returned
* @return the requirement identified by the given id and all of its assets: comments,attachments,followers,developers,creator
@@ -324,6 +375,15 @@ public interface DALFacade {
boolean isRequirementPublic(int requirementId) throws BazaarException;
Statistic getStatisticsForRequirement(int userId, int requirementId, Calendar timestamp) throws BazaarException;
+
+ /**
+ * Returns the count most recent active requirements followed by the user
+ *
+ * @param userId id of the follower
+ * @param count how many should be returned
+ * @return Followed requirements ordered by last activity
+ */
+ List getFollowedRequirements(int userId, int count) throws BazaarException;
//endregion
//region Category
@@ -337,10 +397,9 @@ public interface DALFacade {
/**
* @param requirementId the id of the requirement we are looking in
- * @param pageable pagination information
* @return the categories under the given project in a paginated way
*/
- PaginationResult listCategoriesByRequirementId(int requirementId, Pageable pageable, int userId) throws BazaarException;
+ List listCategoriesByRequirementId(int requirementId, int userId) throws BazaarException;
/**
* @param category to be added to the database.
@@ -378,6 +437,15 @@ public interface DALFacade {
boolean isCategoryPublic(int categoryId) throws BazaarException;
Statistic getStatisticsForCategory(int userId, int categoryId, Calendar timestamp) throws BazaarException;
+
+ /**
+ * Returns the count most recent active categories followed by the user
+ *
+ * @param userId id of the follower
+ * @param count how many should be returned
+ * @return Followed categories ordered by last activity
+ */
+ List getFollowedCategories(int userId, int count) throws BazaarException;
//endregion
//region CategoryFollower
@@ -439,10 +507,22 @@ public interface DALFacade {
/**
* @param requirementId the identifier of the requirement we are looking in
- * @param pageable pagination information
* @return the comments for a given requirement
*/
- PaginationResult listCommentsByRequirementId(int requirementId, Pageable pageable) throws BazaarException;
+ List listCommentsByRequirementId(int requirementId) throws BazaarException;
+
+ /**
+ * @param pageable pagination information
+ * @return the set of comments
+ */
+ PaginationResult listAllComments(Pageable pageable) throws BazaarException;
+
+ /**
+ * @param userId the identifier of user we are looking at
+ * @param pageable pagination information
+ * @return the answers for a given user
+ */
+ PaginationResult listAllAnswers(Pageable pageable, int userId) throws BazaarException;
/**
* @param commentId
@@ -455,6 +535,15 @@ public interface DALFacade {
*/
Comment createComment(Comment comment) throws Exception;
+ /**
+ * Updates a comment
+ *
+ * @param comment comment to persist
+ * @return the updated comment
+ * @throws Exception
+ */
+ Comment updateComment(Comment comment) throws Exception;
+
/**
* @param commentId to identify the comment to be deleted
*/
@@ -556,12 +645,129 @@ public interface DALFacade {
* @param userId the identifier of the user
* @return all the roles filled up with parents and permissions
*/
- List getRolesByUserId(int userId, String context) throws BazaarException;
+ List getRolesByUserId(int userId, Integer context) throws BazaarException;
List getParentsForRole(int roleId) throws BazaarException;
void createPrivilegeIfNotExists(PrivilegeEnum privilege) throws BazaarException;
- void addUserToRole(int userId, String roleName, String context) throws BazaarException;
+ void addUserToRole(int userId, String roleName, Integer context) throws BazaarException;
+
+ Role getRoleByName(String role) throws BazaarException;
//endregion
+
+
+ /**
+ * Receives the PersonalisationData for a given userid, key and version
+ *
+ * @param userId which owns the personalisationData.
+ * @param key which identifies the personalisationData.
+ * @param version of the key's plugin
+ */
+ PersonalisationData getPersonalisationData(int userId, String key, int version) throws BazaarException;
+
+ /**
+ * Creates a new record or alters the existing record to save a given personalisationData
+ *
+ * @param personalisationData which holds the data to be saved
+ */
+ void setPersonalisationData(PersonalisationData personalisationData) throws BazaarException;
+
+ /**
+ * Creates an Entity-Overview for a given user
+ *
+ * @param includes List of entities to include values: [projects, categories, requirements]
+ * @param pageable Used for search-term, filters and sorting
+ * @param userId userId for privilege-check
+ */
+ EntityOverview getEntitiesForUser(List includes, Pageable pageable, int userId) throws BazaarException;
+
+ // region feedback
+
+ /**
+ * Creates a new feedback item
+ *
+ * @param feedback the feedback to create (as submitted by the api)
+ * @return the created feedback item
+ * @throws BazaarException
+ */
+ Feedback createFeedback(Feedback feedback) throws Exception;
+
+ /**
+ * Returns the feedback for a project
+ *
+ * @param projectId Project to look for
+ * @param pageable a pageable
+ * @return Pageable with the feedback for this project
+ * @throws BazaarException
+ */
+ PaginationResult getFeedbackByProject(int projectId, Pageable pageable) throws BazaarException;
+
+ /**
+ * Allows to retrieve a single feedback item
+ *
+ * @param feedbackId ID of the feedback item
+ * @return the requested feedback item
+ * @throws Exception
+ */
+ Feedback getFeedbackById(int feedbackId) throws Exception;
+
+ // endregion feedback
+
+ /**
+ * Aggregates the data for the dashboard
+ *
+ * @param userId Id of the user for their individual dashboard
+ * @param count Number of items per group
+ * @return
+ * @throws BazaarException
+ */
+ Dashboard getDashboardData(int userId, int count) throws BazaarException;
+
+ // region Tags
+
+ /**
+ * Allows to retrieve a tag by its id
+ *
+ * @param id
+ * @return
+ */
+ Tag getTagById(int id) throws Exception;
+
+ /**
+ * Returns all tags associated with a project.
+ *
+ * @param projectId
+ * @return
+ */
+ List getTagsByProjectId(int projectId) throws Exception;
+
+ /**
+ * Creates a new project level tag
+ *
+ * @param tag
+ * @return
+ * @throws BazaarException
+ */
+ Tag createTag(Tag tag) throws BazaarException;
+
+ /**
+ * Link a tag to a requirement
+ *
+ * @param tagId id of the tag (project scoped)
+ * @param requirementId id of the requirement
+ * @return
+ * @throws BazaarException
+ */
+ CreationStatus tagRequirement(int tagId, int requirementId) throws BazaarException;
+
+ /**
+ * Removes a tag from a requirement
+ *
+ * @param tagId
+ * @param requirementId
+ * @throws Exception
+ */
+ void untagRequirement(int tagId, int requirementId) throws Exception;
+ // endregion tags
}
diff --git a/src/main/de/rwth/dbis/acis/bazaar/service/dal/DALFacadeImpl.java b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/DALFacadeImpl.java
similarity index 64%
rename from src/main/de/rwth/dbis/acis/bazaar/service/dal/DALFacadeImpl.java
rename to reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/DALFacadeImpl.java
index ba8f7346..04a8d641 100644
--- a/src/main/de/rwth/dbis/acis/bazaar/service/dal/DALFacadeImpl.java
+++ b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/DALFacadeImpl.java
@@ -28,9 +28,10 @@
import de.rwth.dbis.acis.bazaar.service.dal.repositories.*;
import de.rwth.dbis.acis.bazaar.service.dal.transform.PrivilegeEnumConverter;
import de.rwth.dbis.acis.bazaar.service.exception.BazaarException;
+import de.rwth.dbis.acis.bazaar.service.exception.ErrorCode;
+import de.rwth.dbis.acis.bazaar.service.exception.ExceptionHandler;
import de.rwth.dbis.acis.bazaar.service.internalization.Localization;
import i5.las2peer.api.Context;
-import i5.las2peer.api.security.Agent;
import i5.las2peer.security.PassphraseAgentImpl;
import org.jooq.DSLContext;
import org.jooq.SQLDialect;
@@ -38,12 +39,13 @@
import javax.sql.DataSource;
import java.sql.Timestamp;
+import java.time.OffsetDateTime;
+import java.time.ZoneOffset;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
/**
- * @author Adam Gavronek
* @since 6/14/2014
*/
public class DALFacadeImpl implements DALFacade {
@@ -59,11 +61,15 @@ public class DALFacadeImpl implements DALFacade {
private RequirementFollowerRepository requirementFollowerRepository;
private ProjectRepository projectRepository;
private RequirementRepository requirementRepository;
- private RequirementCategoryRepository tagRepository;
+ private RequirementCategoryRepository requirementCategoryRepository;
private UserRepository userRepository;
private VoteRepository voteRepository;
private RoleRepository roleRepository;
private PrivilegeRepository privilegeRepository;
+ private PersonalisationDataRepository personalisationDataRepository;
+ private FeedbackRepository feedbackRepository;
+ private TagRepository tagRepository;
+ private RequirementTagRepository requirementTagRepository;
public DALFacadeImpl(DataSource dataSource, SQLDialect dialect) {
dslContext = DSL.using(dataSource, dialect);
@@ -86,7 +92,8 @@ public DSLContext getDslContext() {
@Override
public void close() {
- dslContext.close();
+ // No longer necessary, jooq claims gc will take care of it
+ // dslContext.close();
}
@Override
@@ -179,6 +186,13 @@ public List getRecipientListForRequirement(int requirementId) throws Bazaa
return userRepository.getEmailReceiverForRequirement(requirementId);
}
+ @Override
+ public PaginationResult searchUsers(PageInfo pageInfo) throws BazaarException {
+ userRepository = (userRepository != null) ? userRepository : new UserRepositoryImpl(dslContext);
+ List users = userRepository.search(pageInfo);
+ return new PaginationResult<>(users.size(), pageInfo, users);
+ }
+
@Override
public PaginationResult listPublicProjects(Pageable pageable, int userId) throws BazaarException {
projectRepository = (projectRepository != null) ? projectRepository : new ProjectRepositoryImpl(dslContext);
@@ -202,15 +216,24 @@ public Project createProject(Project project, int userId) throws Exception {
projectRepository = (projectRepository != null) ? projectRepository : new ProjectRepositoryImpl(dslContext);
project.setDefaultCategoryId(null);
Project newProject = projectRepository.add(project);
- Category uncategorizedCategory = Category.getBuilder(Localization.getInstance().getResourceBundle().getString("category.uncategorized.Name"))
- .description(Localization.getInstance().getResourceBundle().getString("category.uncategorized.Description"))
+
+ String categoryName = Localization.getInstance().getResourceBundle().getString("category.uncategorized.Name");
+ String categoryDescription = Localization.getInstance().getResourceBundle().getString("category.uncategorized.Description");
+ Category uncategorizedCategory = Category.builder()
+ .name(categoryName)
+ .description(categoryDescription)
.projectId(newProject.getId())
.build();
- uncategorizedCategory.setLeader(project.getLeader());
+ uncategorizedCategory.setCreator(project.getLeader());
Category defaultCategory = createCategory(uncategorizedCategory, userId);
newProject.setDefaultCategoryId(defaultCategory.getId());
- //TODO concurrency transaction -> https://www.jooq.org/doc/3.9/manual/sql-execution/transaction-management/
- return projectRepository.update(newProject);
+ // TODO: concurrency transaction -> https://www.jooq.org/doc/3.9/manual/sql-execution/transaction-management/
+ addUserToRole(userId, "ProjectAdmin", newProject.getId());
+
+ // This is stupid, but the return value of update is inclomplete (dependent objects won't be resolved, since only the top level get by id method is called.
+ // Call repository get separately
+ projectRepository.update(newProject);
+ return projectRepository.findById(newProject.getId(), userId);
}
@Override
@@ -219,6 +242,14 @@ public Project modifyProject(Project modifiedProject) throws Exception {
return projectRepository.update(modifiedProject);
}
+ @Override
+ public Project deleteProjectById(int projectId, Integer userId) throws Exception {
+ projectRepository = (projectRepository != null) ? projectRepository : new ProjectRepositoryImpl(dslContext);
+ Project project = projectRepository.findById(projectId, userId);
+ projectRepository.delete(projectId);
+ return project;
+ }
+
@Override
public boolean isProjectPublic(int projectId) throws BazaarException {
projectRepository = (projectRepository != null) ? projectRepository : new ProjectRepositoryImpl(dslContext);
@@ -229,14 +260,14 @@ public boolean isProjectPublic(int projectId) throws BazaarException {
public Statistic getStatisticsForAllProjects(int userId, Calendar since) throws BazaarException {
projectRepository = (projectRepository != null) ? projectRepository : new ProjectRepositoryImpl(dslContext);
Timestamp timestamp = since == null ? new java.sql.Timestamp(0) : new java.sql.Timestamp(since.getTimeInMillis());
- return projectRepository.getStatisticsForVisibleProjects(userId, timestamp);
+ return projectRepository.getStatisticsForVisibleProjects(userId, timestamp.toLocalDateTime().atOffset(ZoneOffset.UTC));
}
@Override
public Statistic getStatisticsForProject(int userId, int projectId, Calendar since) throws BazaarException {
projectRepository = (projectRepository != null) ? projectRepository : new ProjectRepositoryImpl(dslContext);
Timestamp timestamp = since == null ? new java.sql.Timestamp(0) : new java.sql.Timestamp(since.getTimeInMillis());
- return projectRepository.getStatisticsForProject(userId, projectId, timestamp);
+ return projectRepository.getStatisticsForProject(userId, projectId, timestamp.toLocalDateTime().atOffset(ZoneOffset.UTC));
}
@Override
@@ -245,6 +276,13 @@ public PaginationResult listFollowersForProject(int projectId, Pageable pa
return userRepository.findAllByFollowing(projectId, 0, 0, pageable);
}
+ @Override
+ public List getFollowedProjects(int userId, int count) throws BazaarException {
+ projectRepository = (projectRepository != null) ? projectRepository : new ProjectRepositoryImpl(dslContext);
+ return projectRepository.getFollowedProjects(userId, count);
+ }
+
+
@Override
public List listRequirements(Pageable pageable) throws BazaarException {
requirementRepository = (requirementRepository != null) ? requirementRepository : new RequirementRepositoryImpl(dslContext);
@@ -263,6 +301,12 @@ public PaginationResult listRequirementsByCategory(int categoryId,
return requirementRepository.findAllByCategory(categoryId, pageable, userId);
}
+ @Override
+ public PaginationResult listAllRequirements(Pageable pageable, int userId) throws BazaarException {
+ requirementRepository = (requirementRepository != null) ? requirementRepository : new RequirementRepositoryImpl(dslContext);
+ return requirementRepository.findAll(pageable, userId);
+ }
+
@Override
public Requirement getRequirementById(int requirementId, int userId) throws Exception {
requirementRepository = (requirementRepository != null) ? requirementRepository : new RequirementRepositoryImpl(dslContext);
@@ -273,8 +317,11 @@ public Requirement getRequirementById(int requirementId, int userId) throws Exce
public Requirement createRequirement(Requirement requirement, int userId) throws Exception {
requirementRepository = (requirementRepository != null) ? requirementRepository : new RequirementRepositoryImpl(dslContext);
Requirement newRequirement = requirementRepository.add(requirement);
- for (Category category : requirement.getCategories()) {
- addCategoryTag(newRequirement.getId(), category.getId());
+ for (Integer category : requirement.getCategories()) {
+ addCategoryTag(newRequirement.getId(), category);
+ }
+ for (Tag tag : requirement.getTags()) {
+ tagRequirement(tag.getId(), newRequirement.getId());
}
return getRequirementById(newRequirement.getId(), userId);
}
@@ -282,14 +329,15 @@ public Requirement createRequirement(Requirement requirement, int userId) throws
@Override
public Requirement modifyRequirement(Requirement modifiedRequirement, int userId) throws Exception {
requirementRepository = (requirementRepository != null) ? requirementRepository : new RequirementRepositoryImpl(dslContext);
+ Requirement oldRequirement = getRequirementById(modifiedRequirement.getId(), userId);
requirementRepository.update(modifiedRequirement);
if (modifiedRequirement.getCategories() != null) {
- PaginationResult oldCategories = listCategoriesByRequirementId(modifiedRequirement.getId(), new PageInfo(0, 1000, new HashMap<>()), userId);
- for (Category oldCategory : oldCategories.getElements()) {
+ List oldCategories = listCategoriesByRequirementId(modifiedRequirement.getId(), userId);
+ for (Category oldCategory : oldCategories) {
boolean containCategory = false;
- for (Category newCategory : modifiedRequirement.getCategories()) {
- if (oldCategory.getId() == newCategory.getId()) {
+ for (Integer newCategory : modifiedRequirement.getCategories()) {
+ if (oldCategory.getId() == newCategory) {
containCategory = true;
break;
}
@@ -298,19 +346,79 @@ public Requirement modifyRequirement(Requirement modifiedRequirement, int userId
deleteCategoryTag(modifiedRequirement.getId(), oldCategory.getId());
}
}
- for (Category newCategory : modifiedRequirement.getCategories()) {
+ for (Integer newCategory : modifiedRequirement.getCategories()) {
boolean containCategory = false;
- for (Category oldCategory : oldCategories.getElements()) {
- if (oldCategory.getId() == newCategory.getId()) {
+ for (Category oldCategory : oldCategories) {
+ if (oldCategory.getId() == newCategory) {
containCategory = true;
break;
}
}
if (!containCategory) {
- addCategoryTag(modifiedRequirement.getId(), newCategory.getId());
+ addCategoryTag(modifiedRequirement.getId(), newCategory);
+ }
+ }
+ }
+
+ // Synchronize tags
+ if (modifiedRequirement.getTags() != null) {
+ // Check if tags have changed
+ for (Tag tag : modifiedRequirement.getTags()) {
+ try {
+ Tag internalTag = getTagById(tag.getId());
+
+ // Check if tag exists (in project)
+ if (internalTag == null || modifiedRequirement.getProjectId() != internalTag.getProjectId()) {
+ tag.setProjectId(modifiedRequirement.getProjectId());
+ tag = createTag(tag);
+ }
+ tagRequirement(tag.getId(), modifiedRequirement.getId());
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ // Remove tags no longer present
+ oldRequirement.getTags().stream().filter(tag -> modifiedRequirement.getTags().contains(tag)).forEach(tag -> {
+ try {
+ untagRequirement(tag.getId(), oldRequirement.getId());
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ });
+ }
+
+ // Synchronize attachments
+ if (modifiedRequirement.getAttachments() != null) {
+ // Check if tags have changed
+ for (Attachment attachment : modifiedRequirement.getAttachments()) {
+ try {
+ Attachment internalAttachment = null;
+ if (attachment.getId() != 0) {
+ internalAttachment = getAttachmentById(attachment.getId());
+ }
+
+ // Check if attachment exists, otherwise create
+ if (internalAttachment == null) {
+ attachment.setRequirementId(modifiedRequirement.getId());
+ attachment.setCreator(getUserById(userId));
+ createAttachment(attachment);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
}
}
+
+ // Remove tags no longer present
+ oldRequirement.getAttachments().stream().filter(attachment -> modifiedRequirement.getAttachments().contains(attachment)).forEach(attachment -> {
+ try {
+ deleteAttachmentById(attachment.getId());
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ });
}
+
return getRequirementById(modifiedRequirement.getId(), userId);
}
@@ -325,7 +433,7 @@ public Requirement deleteRequirementById(int requirementId, int userId) throws E
@Override
public Requirement setRequirementToRealized(int requirementId, int userId) throws Exception {
requirementRepository = (requirementRepository != null) ? requirementRepository : new RequirementRepositoryImpl(dslContext);
- requirementRepository.setRealized(requirementId, new java.sql.Timestamp(Calendar.getInstance().getTime().getTime()));
+ requirementRepository.setRealized(requirementId, OffsetDateTime.now());
return getRequirementById(requirementId, userId);
}
@@ -360,7 +468,13 @@ public boolean isRequirementPublic(int requirementId) throws BazaarException {
public Statistic getStatisticsForRequirement(int userId, int requirementId, Calendar since) throws BazaarException {
requirementRepository = (requirementRepository != null) ? requirementRepository : new RequirementRepositoryImpl(dslContext);
Timestamp timestamp = since == null ? new java.sql.Timestamp(0) : new java.sql.Timestamp(since.getTimeInMillis());
- return requirementRepository.getStatisticsForRequirement(userId, requirementId, timestamp);
+ return requirementRepository.getStatisticsForRequirement(userId, requirementId, timestamp.toLocalDateTime().atOffset(ZoneOffset.UTC));
+ }
+
+ @Override
+ public List getFollowedRequirements(int userId, int count) throws BazaarException {
+ requirementRepository = (requirementRepository != null) ? requirementRepository : new RequirementRepositoryImpl(dslContext);
+ return requirementRepository.getFollowedRequirements(userId, count);
}
@Override
@@ -370,9 +484,9 @@ public PaginationResult listCategoriesByProjectId(int projectId, Pagea
}
@Override
- public PaginationResult listCategoriesByRequirementId(int requirementId, Pageable pageable, int userId) throws BazaarException {
+ public List listCategoriesByRequirementId(int requirementId, int userId) throws BazaarException {
categoryRepository = (categoryRepository != null) ? categoryRepository : new CategoryRepositoryImpl(dslContext);
- return categoryRepository.findByRequirementId(requirementId, pageable, userId);
+ return categoryRepository.findByRequirementId(requirementId, userId);
}
@Override
@@ -427,7 +541,13 @@ public boolean isCategoryPublic(int categoryId) throws BazaarException {
public Statistic getStatisticsForCategory(int userId, int categoryId, Calendar since) throws BazaarException {
categoryRepository = (categoryRepository != null) ? categoryRepository : new CategoryRepositoryImpl(dslContext);
Timestamp timestamp = since == null ? new java.sql.Timestamp(0) : new java.sql.Timestamp(since.getTimeInMillis());
- return categoryRepository.getStatisticsForCategory(userId, categoryId, timestamp);
+ return categoryRepository.getStatisticsForCategory(userId, categoryId, timestamp.toLocalDateTime().atOffset(ZoneOffset.UTC));
+ }
+
+ @Override
+ public List getFollowedCategories(int userId, int count) throws BazaarException {
+ categoryRepository = (categoryRepository != null) ? categoryRepository : new CategoryRepositoryImpl(dslContext);
+ return categoryRepository.getFollowedCategories(userId, count);
}
@Override
@@ -464,9 +584,21 @@ public Attachment deleteAttachmentById(int attachmentId) throws Exception {
}
@Override
- public PaginationResult listCommentsByRequirementId(int requirementId, Pageable pageable) throws BazaarException {
+ public List listCommentsByRequirementId(int requirementId) throws BazaarException {
commentRepository = (commentRepository != null) ? commentRepository : new CommentRepositoryImpl(dslContext);
- return commentRepository.findAllByRequirementId(requirementId, pageable);
+ return commentRepository.findAllByRequirementId(requirementId);
+ }
+
+ @Override
+ public PaginationResult listAllComments(Pageable pageable) throws BazaarException {
+ commentRepository = (commentRepository != null) ? commentRepository : new CommentRepositoryImpl(dslContext);
+ return commentRepository.findAllComments(pageable);
+ }
+
+ @Override
+ public PaginationResult listAllAnswers(Pageable pageable, int userId) throws BazaarException {
+ commentRepository = (commentRepository != null) ? commentRepository : new CommentRepositoryImpl(dslContext);
+ return commentRepository.findAllAnswers(pageable, userId);
}
@Override
@@ -482,18 +614,31 @@ public Comment createComment(Comment comment) throws Exception {
return commentRepository.findById(newComment.getId());
}
+ @Override
+ public Comment updateComment(Comment comment) throws Exception {
+ commentRepository = (commentRepository != null) ? commentRepository : new CommentRepositoryImpl(dslContext);
+ commentRepository.update(comment);
+ return commentRepository.findById(comment.getId());
+ }
+
@Override
public Comment deleteCommentById(int commentId) throws Exception {
commentRepository = (commentRepository != null) ? commentRepository : new CommentRepositoryImpl(dslContext);
Comment comment = commentRepository.findById(commentId);
- commentRepository.delete(commentId);
+ if (commentRepository.hasAnswers(commentId)) {
+ comment.setDeleted(true);
+ comment.setMessage("[This message has been deleted]");
+ commentRepository.update(comment);
+ } else {
+ commentRepository.delete(commentId);
+ }
return comment;
}
@Override
public CreationStatus followProject(int userId, int projectId) throws BazaarException {
projectFollowerRepository = (projectFollowerRepository != null) ? projectFollowerRepository : new ProjectFollowerRepositoryImpl(dslContext);
- return projectFollowerRepository.addOrUpdate(ProjectFollower.getBuilder()
+ return projectFollowerRepository.addOrUpdate(ProjectFollower.builder()
.projectId(projectId)
.userId(userId)
.build()
@@ -509,7 +654,7 @@ public void unFollowProject(int userId, int projectId) throws BazaarException {
@Override
public CreationStatus followCategory(int userId, int categoryId) throws BazaarException {
categoryFollowerRepository = (categoryFollowerRepository != null) ? categoryFollowerRepository : new CategoryFollowerRepositoryImpl(dslContext);
- return categoryFollowerRepository.addOrUpdate(CategoryFollower.getBuilder()
+ return categoryFollowerRepository.addOrUpdate(CategoryFollower.builder()
.categoryId(categoryId)
.userId(userId)
.build()
@@ -525,7 +670,7 @@ public void unFollowCategory(int userId, int categoryId) throws BazaarException
@Override
public CreationStatus followRequirement(int userId, int requirementId) throws BazaarException {
requirementFollowerRepository = (requirementFollowerRepository != null) ? requirementFollowerRepository : new RequirementFollowerRepositoryImpl(dslContext);
- return requirementFollowerRepository.addOrUpdate(RequirementFollower.getBuilder()
+ return requirementFollowerRepository.addOrUpdate(RequirementFollower.builder()
.requirementId(requirementId)
.userId(userId)
.build()
@@ -541,7 +686,7 @@ public void unFollowRequirement(int userId, int requirementId) throws BazaarExce
@Override
public CreationStatus wantToDevelop(int userId, int requirementId) throws BazaarException {
developerRepository = (developerRepository != null) ? developerRepository : new RequirementDeveloperRepositoryImpl(dslContext);
- return developerRepository.addOrUpdate(RequirementDeveloper.getBuilder()
+ return developerRepository.addOrUpdate(RequirementDeveloper.builder()
.requirementId(requirementId)
.userId(userId)
.build()
@@ -557,8 +702,9 @@ public void notWantToDevelop(int userId, int requirementId) throws BazaarExcepti
@Override
public void addCategoryTag(int requirementId, int categoryId) throws BazaarException {
- tagRepository = (tagRepository != null) ? tagRepository : new RequirementCategoryRepositoryImpl(dslContext);
- tagRepository.add(RequirementCategory.getBuilder(categoryId)
+ requirementCategoryRepository = (requirementCategoryRepository != null) ? requirementCategoryRepository : new RequirementCategoryRepositoryImpl(dslContext);
+ requirementCategoryRepository.add(RequirementCategory.builder()
+ .categoryId(categoryId)
.requirementId(requirementId)
.build()
);
@@ -566,14 +712,14 @@ public void addCategoryTag(int requirementId, int categoryId) throws BazaarExcep
@Override
public void deleteCategoryTag(int requirementId, int categoryId) throws BazaarException {
- tagRepository = (tagRepository != null) ? tagRepository : new RequirementCategoryRepositoryImpl(dslContext);
- tagRepository.delete(requirementId, categoryId);
+ requirementCategoryRepository = (requirementCategoryRepository != null) ? requirementCategoryRepository : new RequirementCategoryRepositoryImpl(dslContext);
+ requirementCategoryRepository.delete(requirementId, categoryId);
}
@Override
public CreationStatus vote(int userId, int requirementId, boolean isUpVote) throws BazaarException {
voteRepository = (voteRepository != null) ? voteRepository : new VoteRepositoryImpl(dslContext);
- return voteRepository.addOrUpdate(Vote.getBuilder()
+ return voteRepository.addOrUpdate(Vote.builder()
.requirementId(requirementId)
.userId(userId)
.isUpvote(isUpVote)
@@ -594,7 +740,7 @@ public boolean hasUserVotedForRequirement(int userId, int requirementId) throws
}
@Override
- public List getRolesByUserId(int userId, String context) throws BazaarException {
+ public List getRolesByUserId(int userId, Integer context) throws BazaarException {
roleRepository = (roleRepository != null) ? roleRepository : new RoleRepositoryImpl(dslContext);
return roleRepository.listRolesOfUser(userId, context);
}
@@ -611,14 +757,140 @@ public void createPrivilegeIfNotExists(PrivilegeEnum privilege) throws BazaarExc
Privilege privilegeDb = privilegeRepository.findByName(new PrivilegeEnumConverter().to(privilege));
if (privilegeDb == null) {
- privilegeRepository.add(Privilege.getBuilder(privilege).build());
+ privilegeRepository.add(Privilege.builder().name(privilege).build());
}
}
@Override
- public void addUserToRole(int userId, String roleName, String context) throws BazaarException {
+ public void addUserToRole(int userId, String roleName, Integer context) throws BazaarException {
roleRepository = (roleRepository != null) ? roleRepository : new RoleRepositoryImpl(dslContext);
roleRepository.addUserToRole(userId, roleName, context);
}
+
+ @Override
+ public Role getRoleByName(String role) throws BazaarException {
+ roleRepository = (roleRepository != null) ? roleRepository : new RoleRepositoryImpl(dslContext);
+ return roleRepository.findByRoleName(role);
+ }
+
+ @Override
+ public void removeUserFromProject(int userId, Integer context) throws BazaarException {
+ roleRepository = (roleRepository != null) ? roleRepository : new RoleRepositoryImpl(dslContext);
+ roleRepository.removeUserFromRole(userId, context);
+ }
+
+ @Override
+ public PersonalisationData getPersonalisationData(int userId, String key, int version) throws BazaarException {
+ personalisationDataRepository = (personalisationDataRepository != null) ? personalisationDataRepository : new PersonalisationDataRepositoryImpl(dslContext);
+ return personalisationDataRepository.findByKey(userId, version, key);
+ }
+
+ @Override
+ public void setPersonalisationData(PersonalisationData personalisationData) throws BazaarException {
+ personalisationDataRepository = (personalisationDataRepository != null) ? personalisationDataRepository : new PersonalisationDataRepositoryImpl(dslContext);
+ personalisationDataRepository.insertOrUpdate(personalisationData);
+
+ }
+
+ @Override
+ public EntityOverview getEntitiesForUser(List includes, Pageable pageable, int userId) throws BazaarException {
+ //categoryRepository = (categoryRepository != null) ? categoryRepository : new CategoryRepositoryImpl(dslContext);
+ EntityOverview.Builder result = EntityOverview.builder();
+ for (String include : includes) {
+ switch (include) {
+ case "projects" -> {
+ projectRepository = (projectRepository != null) ? projectRepository : new ProjectRepositoryImpl(dslContext);
+ result.projects(projectRepository.listAllProjectIds(pageable, userId));
+ }
+ case "requirements" -> {
+ requirementRepository = (requirementRepository != null) ? requirementRepository : new RequirementRepositoryImpl(dslContext);
+ result.requirements(requirementRepository.listAllRequirementIds(pageable, userId));
+ }
+ case "categories" -> {
+ categoryRepository = (categoryRepository != null) ? categoryRepository : new CategoryRepositoryImpl(dslContext);
+ result.categories(categoryRepository.listAllCategoryIds(pageable, userId));
+ }
+ }
+ //TODO Add Comments/Attachments
+ }
+ return result.build();
+ }
+
+ @Override
+ public Feedback createFeedback(Feedback feedback) throws Exception {
+ feedbackRepository = (feedbackRepository != null) ? feedbackRepository : new FeedbackRepositoryImpl(dslContext);
+ Feedback newFeedback = feedbackRepository.add(feedback);
+ return feedbackRepository.findById(newFeedback.getId());
+ }
+
+ @Override
+ public PaginationResult getFeedbackByProject(int projectId, Pageable pageable) throws BazaarException {
+ feedbackRepository = (feedbackRepository != null) ? feedbackRepository : new FeedbackRepositoryImpl(dslContext);
+ return feedbackRepository.findAllByProject(projectId, pageable);
+ }
+
+ @Override
+ public Feedback getFeedbackById(int feedbackId) throws Exception {
+ feedbackRepository = (feedbackRepository != null) ? feedbackRepository : new FeedbackRepositoryImpl(dslContext);
+ return feedbackRepository.findById(feedbackId);
+ }
+
+ @Override
+ public Dashboard getDashboardData(int userId, int count) throws BazaarException {
+ return Dashboard.builder()
+ .projects(getFollowedProjects(userId, count))
+ .categories(getFollowedCategories(userId, count))
+ .requirements(getFollowedRequirements(userId, count))
+ .build();
+ }
+
+ @Override
+ public Tag getTagById(int id) throws Exception {
+ tagRepository = (tagRepository != null) ? tagRepository : new TagRepositoryImpl(dslContext);
+ try {
+ return tagRepository.findById(id);
+ } catch (BazaarException bex) {
+ if (bex.getErrorCode() == ErrorCode.NOT_FOUND) {
+ return null;
+ }
+ ExceptionHandler.getInstance().convertAndThrowException(bex);
+ }
+ return null;
+ }
+
+ @Override
+ public List getTagsByProjectId(int projectId) throws Exception {
+ tagRepository = (tagRepository != null) ? tagRepository : new TagRepositoryImpl(dslContext);
+ return tagRepository.findByProjectId(projectId);
+ }
+
+ @Override
+ public Tag createTag(Tag tag) throws BazaarException {
+ tagRepository = (tagRepository != null) ? tagRepository : new TagRepositoryImpl(dslContext);
+ return tagRepository.add(tag);
+ }
+
+ @Override
+ public CreationStatus tagRequirement(int tagId, int requirementId) throws BazaarException {
+ requirementTagRepository = (requirementTagRepository != null) ? requirementTagRepository : new RequirementTagRepositoryImpl(dslContext);
+ return requirementTagRepository.addOrUpdate(RequirementTag.builder()
+ .requirementId(requirementId)
+ .tagId(tagId)
+ .build()
+ );
+ }
+
+ @Override
+ public void untagRequirement(int tagId, int requirementId) throws Exception {
+ requirementTagRepository = (requirementTagRepository != null) ? requirementTagRepository : new RequirementTagRepositoryImpl(dslContext);
+ requirementTagRepository.delete(tagId, requirementId);
+ }
+
+ @Override
+ public PaginationResult getProjectMembers(int projectId, Pageable pageable) throws
+ BazaarException {
+ roleRepository = (roleRepository != null) ? roleRepository : new RoleRepositoryImpl(dslContext);
+ return roleRepository.listProjectMembers(projectId, pageable);
+ }
}
diff --git a/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/Activity.java b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/Activity.java
new file mode 100644
index 00000000..3be33d10
--- /dev/null
+++ b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/Activity.java
@@ -0,0 +1,90 @@
+package de.rwth.dbis.acis.bazaar.service.dal.entities;
+
+import com.fasterxml.jackson.annotation.JsonFilter;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import lombok.Builder;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NonNull;
+import lombok.extern.jackson.Jacksonized;
+
+import java.time.OffsetDateTime;
+
+@EqualsAndHashCode(callSuper = true)
+@Data
+@Jacksonized
+@Builder(builderClassName = "Builder")
+public class Activity extends EntityBase {
+
+ private final transient int id;
+
+ @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZ", timezone = "Europe/Berlin")
+ private final OffsetDateTime creationDate;
+
+ private final ActivityAction activityAction;
+ private final String dataUrl;
+ private final DataType dataType;
+ private final String dataFrontendUrl;
+ private final String parentDataUrl;
+ private final DataType parentDataType;
+ private final String userUrl;
+ private final String origin;
+
+ private AdditionalObject additionalObject;
+
+ @Override
+ @JsonIgnore
+ public int getId() {
+ return id;
+ }
+
+ public enum DataType {
+ STATISTIC,
+ PROJECT,
+ CATEGORY,
+ REQUIREMENT,
+ COMMENT,
+ ATTACHMENT,
+ USER,
+ FEEDBACK,
+ TAG
+ }
+
+ public enum ActivityAction {
+ RETRIEVE,
+ RETRIEVE_CHILD,
+ CREATE,
+ UPDATE,
+ DELETE,
+ REALIZE,
+ UNREALIZE,
+ VOTE,
+ UNVOTE,
+ DEVELOP,
+ UNDEVELOP,
+ FOLLOW,
+ UNFOLLOW,
+ LEADDEVELOP,
+ UNLEADDEVELOP
+ }
+
+ @Data
+ public static class AdditionalObject {
+ @JsonFilter("ActivityFilter")
+ @NonNull
+ private Project project;
+
+ @JsonFilter("ActivityFilter")
+ @NonNull
+ private Category category;
+
+ @JsonFilter("ActivityFilter")
+ @NonNull
+ private Requirement requirement;
+
+ @JsonFilter("ActivityFilter")
+ @NonNull
+ private User user;
+ }
+}
diff --git a/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/Attachment.java b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/Attachment.java
new file mode 100644
index 00000000..64f867b8
--- /dev/null
+++ b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/Attachment.java
@@ -0,0 +1,82 @@
+/*
+ *
+ * Copyright (c) 2014, RWTH Aachen University.
+ * For a list of contributors see the AUTHORS file at the top-level directory
+ * of this distribution.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * /
+ */
+
+package de.rwth.dbis.acis.bazaar.service.dal.entities;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import de.rwth.dbis.acis.bazaar.service.dal.helpers.CreateValidation;
+import lombok.Builder;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.extern.jackson.Jacksonized;
+
+import javax.validation.constraints.Min;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+import java.time.OffsetDateTime;
+
+@EqualsAndHashCode(callSuper = true)
+@Data
+@Jacksonized
+@Builder(builderClassName = "Builder")
+public class Attachment extends EntityBase implements Ownable {
+
+ private int id;
+
+ @NotNull(groups = CreateValidation.class)
+ @Size(min = 1, max = 50)
+ private String name;
+
+ private String description;
+
+ @NotNull(groups = CreateValidation.class)
+ @Size(min = 1, max = 1000)
+ private String mimeType;
+
+ @NotNull(groups = CreateValidation.class)
+ @Size(min = 1, max = 1000)
+ private String identifier;
+
+ @NotNull(groups = CreateValidation.class)
+ @Size(min = 1, max = 1000)
+ private String fileUrl;
+
+ @NotNull
+ @Min(value = 0)
+ private int requirementId;
+
+ private User creator;
+
+ @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZ", timezone = "Europe/Berlin")
+ private OffsetDateTime creationDate;
+
+ @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZ", timezone = "Europe/Berlin")
+ private OffsetDateTime lastUpdatedDate;
+
+ @Override
+ public boolean isOwner(User user) {
+ return creator == user;
+ }
+
+ @Override
+ public boolean isOwner(Integer userId) {
+ return creator.getId() == userId;
+ }
+}
diff --git a/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/Category.java b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/Category.java
new file mode 100644
index 00000000..4a03b112
--- /dev/null
+++ b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/Category.java
@@ -0,0 +1,90 @@
+/*
+ *
+ * Copyright (c) 2014, RWTH Aachen University.
+ * For a list of contributors see the AUTHORS file at the top-level directory
+ * of this distribution.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * /
+ */
+
+package de.rwth.dbis.acis.bazaar.service.dal.entities;
+
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.databind.JsonNode;
+import de.rwth.dbis.acis.bazaar.service.dal.helpers.CreateValidation;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Builder;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.extern.jackson.Jacksonized;
+
+import javax.validation.constraints.Min;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+import java.time.OffsetDateTime;
+
+/**
+ * @since 6/9/2014
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+@Jacksonized
+@Builder(builderClassName = "Builder")
+public class Category extends EntityBase implements Ownable {
+
+ private int id;
+
+ @NotNull(message = "name can't be null", groups = CreateValidation.class)
+ @Size(min = 1, max = 50, message = "name must have between 1 and 50 characters")
+ private String name;
+
+ @NotNull(groups = CreateValidation.class)
+ @Size(min = 1)
+ private String description;
+
+ @Min(value = 0, groups = CreateValidation.class)
+ private int projectId;
+
+ private User creator;
+
+ @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZ", timezone = "Europe/Berlin")
+ private OffsetDateTime creationDate;
+
+ @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZ", timezone = "Europe/Berlin")
+ private OffsetDateTime lastUpdatedDate;
+
+ @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZ", timezone = "Europe/Berlin")
+ private OffsetDateTime lastActivity;
+
+ private Integer numberOfRequirements;
+ private Integer numberOfFollowers;
+
+ private UserContext userContext;
+
+ @ApiModelProperty(
+ dataType = "java.util.Map"
+ )
+ private JsonNode additionalProperties;
+
+ @Override
+ public boolean isOwner(User user) {
+ return creator == user;
+ }
+
+ @Override
+ public boolean isOwner(Integer userId) {
+ return creator.getId() == userId;
+ }
+}
diff --git a/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/CategoryContributors.java b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/CategoryContributors.java
new file mode 100644
index 00000000..91a34dfd
--- /dev/null
+++ b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/CategoryContributors.java
@@ -0,0 +1,34 @@
+package de.rwth.dbis.acis.bazaar.service.dal.entities;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import lombok.Builder;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.extern.jackson.Jacksonized;
+
+import java.util.List;
+
+/**
+ * Created by Martin on 15.06.2017.
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+@Jacksonized
+@Builder(builderClassName = "Builder")
+public class CategoryContributors extends EntityBase {
+
+ private final int id;
+
+ private User leader;
+ private List requirementCreator;
+ private List leadDeveloper;
+ private List developers;
+ private List commentCreator;
+ private List attachmentCreator;
+
+ @Override
+ @JsonIgnore
+ public int getId() {
+ return id;
+ }
+}
diff --git a/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/CategoryFollower.java b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/CategoryFollower.java
new file mode 100644
index 00000000..eac84c31
--- /dev/null
+++ b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/CategoryFollower.java
@@ -0,0 +1,16 @@
+package de.rwth.dbis.acis.bazaar.service.dal.entities;
+
+import lombok.Builder;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.extern.jackson.Jacksonized;
+
+@EqualsAndHashCode(callSuper = true)
+@Data
+@Jacksonized
+@Builder(builderClassName = "Builder")
+public class CategoryFollower extends EntityBase {
+ private final int id;
+ private final int categoryId;
+ private final int userId;
+}
diff --git a/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/Comment.java b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/Comment.java
new file mode 100644
index 00000000..562d26e3
--- /dev/null
+++ b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/Comment.java
@@ -0,0 +1,81 @@
+/*
+ *
+ * Copyright (c) 2014, RWTH Aachen University.
+ * For a list of contributors see the AUTHORS file at the top-level directory
+ * of this distribution.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * /
+ */
+
+package de.rwth.dbis.acis.bazaar.service.dal.entities;
+
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import de.rwth.dbis.acis.bazaar.service.dal.helpers.CreateValidation;
+import lombok.Builder;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.extern.jackson.Jacksonized;
+
+import javax.validation.constraints.Min;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+import java.time.OffsetDateTime;
+
+/**
+ * @since 6/11/2014
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+@Jacksonized
+@Builder(builderClassName = "Builder")
+public class Comment extends EntityBase implements Ownable {
+
+ private int id;
+
+ @NotNull(groups = CreateValidation.class)
+ @Size(min = 1)
+ private String message;
+
+ @Min(value = 0, groups = CreateValidation.class)
+ private Integer replyToComment;
+
+ @Min(value = 0, groups = CreateValidation.class)
+ private int requirementId;
+
+ private User creator;
+
+ @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZ", timezone = "Europe/Berlin")
+ private OffsetDateTime creationDate;
+
+ @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZ", timezone = "Europe/Berlin")
+ private OffsetDateTime lastUpdatedDate;
+
+ @lombok.Builder.Default
+ private Boolean deleted = false;
+
+ @JsonProperty("_context")
+ private EntityContext context;
+
+ @Override
+ public boolean isOwner(User user) {
+ return creator == user;
+ }
+
+ @Override
+ public boolean isOwner(Integer userId) {
+ return creator.getId() == userId;
+ }
+}
diff --git a/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/Dashboard.java b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/Dashboard.java
new file mode 100644
index 00000000..40ba5361
--- /dev/null
+++ b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/Dashboard.java
@@ -0,0 +1,34 @@
+package de.rwth.dbis.acis.bazaar.service.dal.entities;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import de.rwth.dbis.acis.bazaar.service.dal.helpers.UserVote;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Builder;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.extern.jackson.Jacksonized;
+
+import javax.validation.constraints.NotNull;
+import java.util.List;
+
+@EqualsAndHashCode(callSuper = true)
+@Data
+@Jacksonized
+@Builder(builderClassName = "Builder")
+public class Dashboard extends EntityBase {
+
+ @NotNull
+ private List projects;
+
+ @NotNull
+ private List categories;
+
+ @NotNull
+ private List requirements;
+
+ @JsonIgnore
+ @Override
+ public int getId() {
+ return 0;
+ }
+}
diff --git a/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/Direction.java b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/Direction.java
new file mode 100644
index 00000000..634fce71
--- /dev/null
+++ b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/Direction.java
@@ -0,0 +1,16 @@
+package de.rwth.dbis.acis.bazaar.service.dal.entities;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import lombok.Getter;
+import lombok.Setter;
+
+public class Direction {
+ @Getter
+ @Setter
+ private VoteDirection direction;
+
+ @JsonIgnore
+ public boolean isUpVote() {
+ return direction == VoteDirection.up;
+ }
+}
diff --git a/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/Email.java b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/Email.java
new file mode 100644
index 00000000..fa1b0cbd
--- /dev/null
+++ b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/Email.java
@@ -0,0 +1,35 @@
+package de.rwth.dbis.acis.bazaar.service.dal.entities;
+
+
+import lombok.Builder;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.extern.jackson.Jacksonized;
+
+import java.time.OffsetDateTime;
+import java.util.Set;
+
+@EqualsAndHashCode(callSuper = true)
+@Data
+@Jacksonized
+@Builder(builderClassName = "Builder", toBuilder = true)
+public class Email extends EntityBase {
+
+ private final transient int id;
+
+ private final Set recipients;
+ private final String subject;
+ private final String starting;
+ private final String message;
+ private final String closing;
+ private final String footer;
+ private final OffsetDateTime creationDate;
+
+ public static Activity.Builder getBuilder() {
+ return new Activity.Builder();
+ }
+
+ public void removeRecipient(User user) {
+ recipients.remove(user);
+ }
+}
diff --git a/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/EntityBase.java b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/EntityBase.java
new file mode 100644
index 00000000..df72d589
--- /dev/null
+++ b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/EntityBase.java
@@ -0,0 +1,48 @@
+/*
+ *
+ * Copyright (c) 2014, RWTH Aachen University.
+ * For a list of contributors see the AUTHORS file at the top-level directory
+ * of this distribution.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * /
+ */
+
+package de.rwth.dbis.acis.bazaar.service.dal.entities;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
+import de.rwth.dbis.acis.bazaar.service.dal.helpers.SerializerViews;
+
+/**
+ * @since 9/16/2014
+ */
+public abstract class EntityBase implements IdentifiedById {
+
+ public String toJSON() throws JsonProcessingException {
+ return new ObjectMapper().registerModule(new JavaTimeModule())
+ .setSerializationInclusion(JsonInclude.Include.NON_NULL)
+ .writerWithView(SerializerViews.Public.class)
+ .writeValueAsString(this);
+ }
+
+ public String toPrivateJSON() throws JsonProcessingException {
+ return new ObjectMapper()
+ .registerModule(new JavaTimeModule())
+ .setSerializationInclusion(JsonInclude.Include.NON_NULL)
+ .writerWithView(SerializerViews.Private.class)
+ .writeValueAsString(this);
+ }
+}
diff --git a/src/main/de/rwth/dbis/acis/bazaar/service/dal/entities/EntityBase.java b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/EntityContext.java
similarity index 64%
rename from src/main/de/rwth/dbis/acis/bazaar/service/dal/entities/EntityBase.java
rename to reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/EntityContext.java
index f7027257..5c5b1a35 100644
--- a/src/main/de/rwth/dbis/acis/bazaar/service/dal/entities/EntityBase.java
+++ b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/EntityContext.java
@@ -20,17 +20,29 @@
package de.rwth.dbis.acis.bazaar.service.dal.entities;
+
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
+import lombok.Builder;
+import lombok.Data;
+import lombok.extern.jackson.Jacksonized;
/**
- * @author Adam Gavronek
- * @since 9/16/2014
+ * @since 30/01/2020
*/
-public abstract class EntityBase implements IdentifiedById {
+@Data
+@Jacksonized
+@Builder(builderClassName = "Builder")
+public class EntityContext {
+ private User user;
+ private Project project;
+ private Category[] categories;
+ private Requirement requirement;
+ private Comment comment;
public String toJSON() throws JsonProcessingException {
- return new ObjectMapper().setSerializationInclusion(JsonInclude.Include.NON_NULL).writeValueAsString(this);
+ return new ObjectMapper().registerModule(new JavaTimeModule()).setSerializationInclusion(JsonInclude.Include.NON_NULL).writeValueAsString(this);
}
}
diff --git a/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/EntityOverview.java b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/EntityOverview.java
new file mode 100644
index 00000000..7e49d702
--- /dev/null
+++ b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/EntityOverview.java
@@ -0,0 +1,51 @@
+/*
+ *
+ * Copyright (c) 2014, RWTH Aachen University.
+ * For a list of contributors see the AUTHORS file at the top-level directory
+ * of this distribution.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * /
+ */
+
+package de.rwth.dbis.acis.bazaar.service.dal.entities;
+
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
+import lombok.Builder;
+import lombok.Data;
+import lombok.extern.jackson.Jacksonized;
+
+import java.util.List;
+
+/**
+ * @since 22/01/2020
+ */
+@Data
+@Jacksonized
+@Builder(builderClassName = "Builder")
+public class EntityOverview {
+
+ private List projects;
+ private List categories;
+ private List requirements;
+ private List comments;
+ private List attachments;
+
+ public String toJSON() throws JsonProcessingException {
+ return new ObjectMapper().registerModule(new JavaTimeModule()).setSerializationInclusion(JsonInclude.Include.NON_NULL).writeValueAsString(this);
+ }
+}
diff --git a/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/Feedback.java b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/Feedback.java
new file mode 100644
index 00000000..e5b9ee00
--- /dev/null
+++ b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/Feedback.java
@@ -0,0 +1,36 @@
+package de.rwth.dbis.acis.bazaar.service.dal.entities;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import de.rwth.dbis.acis.bazaar.service.dal.helpers.CreateValidation;
+import lombok.Builder;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.extern.jackson.Jacksonized;
+
+import javax.validation.constraints.Min;
+import javax.validation.constraints.NotNull;
+import java.time.OffsetDateTime;
+
+@EqualsAndHashCode(callSuper = true)
+@Data
+@Jacksonized
+@Builder(builderClassName = "Builder")
+public class Feedback extends EntityBase {
+ private int id;
+
+ @NotNull(message = "feedback needs an associated project", groups = CreateValidation.class)
+ @Min(value = 0, groups = CreateValidation.class)
+ private int projectId;
+
+ @NotNull(message = "feedback can not be null", groups = CreateValidation.class)
+ private String feedback;
+
+ @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZ", timezone = "Europe/Berlin")
+ private OffsetDateTime creationDate;
+
+ @JsonProperty("email")
+ private String eMail;
+
+ private Integer requirementId;
+}
diff --git a/src/main/de/rwth/dbis/acis/bazaar/service/dal/entities/IdentifiedById.java b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/IdentifiedById.java
similarity index 94%
rename from src/main/de/rwth/dbis/acis/bazaar/service/dal/entities/IdentifiedById.java
rename to reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/IdentifiedById.java
index 70e65d51..077b68d9 100644
--- a/src/main/de/rwth/dbis/acis/bazaar/service/dal/entities/IdentifiedById.java
+++ b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/IdentifiedById.java
@@ -21,7 +21,6 @@
package de.rwth.dbis.acis.bazaar.service.dal.entities;
/**
- * @author Adam Gavronek
* @since 6/9/2014
*/
public interface IdentifiedById {
diff --git a/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/Ownable.java b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/Ownable.java
new file mode 100644
index 00000000..388b03b7
--- /dev/null
+++ b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/Ownable.java
@@ -0,0 +1,11 @@
+package de.rwth.dbis.acis.bazaar.service.dal.entities;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+
+public interface Ownable {
+ @JsonIgnore
+ boolean isOwner(User user);
+
+ @JsonIgnore
+ boolean isOwner(Integer userId);
+}
diff --git a/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/PersonalisationData.java b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/PersonalisationData.java
new file mode 100644
index 00000000..1aa8833a
--- /dev/null
+++ b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/PersonalisationData.java
@@ -0,0 +1,54 @@
+/*
+ *
+ * Copyright (c) 2014, RWTH Aachen University.
+ * For a list of contributors see the AUTHORS file at the top-level directory
+ * of this distribution.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * /
+ */
+
+package de.rwth.dbis.acis.bazaar.service.dal.entities;
+
+import lombok.Builder;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.extern.jackson.Jacksonized;
+
+import javax.validation.constraints.Min;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+
+/**
+ * @since 26/11/2019
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+@Jacksonized
+@Builder(builderClassName = "Builder")
+public class PersonalisationData extends EntityBase {
+ private int id;
+
+ @NotNull
+ @Size(min = 1, max = 50, message = "Key must have between 1 and 50 characters")
+ private String key;
+
+ @Min(value = 0)
+ private int version;
+
+ private int userId;
+
+ @NotNull
+ @Size(min = 1, max = 10000)
+ private String value;
+}
diff --git a/src/test/de/rwth/dbis/acis/bazaar/service/security/SpecialRightsTest.java b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/Privilege.java
similarity index 65%
rename from src/test/de/rwth/dbis/acis/bazaar/service/security/SpecialRightsTest.java
rename to reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/Privilege.java
index 60fc2354..0a81ba33 100644
--- a/src/test/de/rwth/dbis/acis/bazaar/service/security/SpecialRightsTest.java
+++ b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/Privilege.java
@@ -18,12 +18,23 @@
* /
*/
-package de.rwth.dbis.acis.bazaar.service.security;
+package de.rwth.dbis.acis.bazaar.service.dal.entities;
+
+import lombok.Builder;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.extern.jackson.Jacksonized;
/**
- * @author Adam Gavronek
- * @since 2/25/2015
+ * @since 2/17/2015
*/
-public class SpecialRightsTest {
+@EqualsAndHashCode(callSuper = true)
+@Data
+@Jacksonized
+@Builder(builderClassName = "Builder")
+public class Privilege extends EntityBase {
+
+ private final int id;
+ private final PrivilegeEnum name;
}
diff --git a/src/main/de/rwth/dbis/acis/bazaar/service/dal/entities/PrivilegeEnum.java b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/PrivilegeEnum.java
similarity index 86%
rename from src/main/de/rwth/dbis/acis/bazaar/service/dal/entities/PrivilegeEnum.java
rename to reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/PrivilegeEnum.java
index 68e4b9ae..14987d37 100644
--- a/src/main/de/rwth/dbis/acis/bazaar/service/dal/entities/PrivilegeEnum.java
+++ b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/PrivilegeEnum.java
@@ -21,7 +21,6 @@
package de.rwth.dbis.acis.bazaar.service.dal.entities;
/**
- * @author Adam Gavronek
* @since 2/17/2015
*/
public enum PrivilegeEnum {
@@ -29,6 +28,7 @@ public enum PrivilegeEnum {
Read_PROJECT,
Read_PUBLIC_PROJECT,
Modify_PROJECT,
+ Delete_PROJECT,
Create_CATEGORY,
Read_CATEGORY,
@@ -39,6 +39,7 @@ public enum PrivilegeEnum {
Read_REQUIREMENT,
Read_PUBLIC_REQUIREMENT,
Modify_REQUIREMENT,
+ Realize_REQUIREMENT,
Create_COMMENT,
Read_COMMENT,
@@ -54,4 +55,11 @@ public enum PrivilegeEnum {
Create_FOLLOW, Delete_FOLLOW,
Create_DEVELOP, Delete_DEVELOP,
+ Read_PERSONALISATION_DATA, Create_PERSONALISATION_DATA, //Create covers "PUT" Operation
+
+ Read_FEEDBACK,
+ Modify_MEMBERS,
+ Modify_ADMIN_MEMBERS,
+
+ Read_USERS,
}
diff --git a/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/Project.java b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/Project.java
new file mode 100644
index 00000000..093736dc
--- /dev/null
+++ b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/Project.java
@@ -0,0 +1,90 @@
+/*
+ *
+ * Copyright (c) 2014, RWTH Aachen University.
+ * For a list of contributors see the AUTHORS file at the top-level directory
+ * of this distribution.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * /
+ */
+
+package de.rwth.dbis.acis.bazaar.service.dal.entities;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.databind.JsonNode;
+import de.rwth.dbis.acis.bazaar.service.dal.helpers.CreateValidation;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Builder;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.extern.jackson.Jacksonized;
+
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+import java.time.OffsetDateTime;
+
+/**
+ * @since 6/9/2014
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+@Jacksonized
+@Builder(builderClassName = "Builder")
+public class Project extends EntityBase implements Ownable {
+
+ private int id;
+
+ @NotNull(message = "name can not be null", groups = CreateValidation.class)
+ @Size(min = 1, max = 50, message = "name can't have more than 50 characters")
+ private String name;
+
+ @NotNull(message = "description can not be null", groups = CreateValidation.class)
+ private String description;
+
+ @lombok.Builder.Default
+ private Boolean visibility = true;
+
+ private Integer defaultCategoryId;
+
+ private User leader;
+
+ @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZ", timezone = "Europe/Berlin")
+ private OffsetDateTime creationDate;
+
+ @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZ", timezone = "Europe/Berlin")
+ private OffsetDateTime lastUpdatedDate;
+
+ @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZ", timezone = "Europe/Berlin")
+ private OffsetDateTime lastActivity;
+
+ private Integer numberOfCategories;
+ private Integer numberOfRequirements;
+ private Integer numberOfFollowers;
+
+ private UserContext userContext;
+
+ @ApiModelProperty(
+ dataType = "java.util.Map"
+ )
+ private JsonNode additionalProperties;
+
+ @Override
+ public boolean isOwner(User user) {
+ return leader.equals(user);
+ }
+
+ @Override
+ public boolean isOwner(Integer userId) {
+ return leader.getId() == userId;
+ }
+}
diff --git a/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/ProjectContributors.java b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/ProjectContributors.java
new file mode 100644
index 00000000..8c574408
--- /dev/null
+++ b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/ProjectContributors.java
@@ -0,0 +1,35 @@
+package de.rwth.dbis.acis.bazaar.service.dal.entities;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import lombok.Builder;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.extern.jackson.Jacksonized;
+
+import java.util.List;
+
+/**
+ * Created by Martin on 15.06.2017.
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+@Jacksonized
+@Builder(builderClassName = "Builder")
+public class ProjectContributors extends EntityBase {
+
+ private final int id;
+
+ private User leader;
+ private List categoryLeader;
+ private List requirementCreator;
+ private List leadDeveloper;
+ private List developers;
+ private List commentCreator;
+ private List attachmentCreator;
+
+ @Override
+ @JsonIgnore
+ public int getId() {
+ return id;
+ }
+}
diff --git a/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/ProjectFollower.java b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/ProjectFollower.java
new file mode 100644
index 00000000..7df040ca
--- /dev/null
+++ b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/ProjectFollower.java
@@ -0,0 +1,16 @@
+package de.rwth.dbis.acis.bazaar.service.dal.entities;
+
+import lombok.Builder;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.extern.jackson.Jacksonized;
+
+@EqualsAndHashCode(callSuper = true)
+@Data
+@Jacksonized
+@Builder(builderClassName = "Builder")
+public class ProjectFollower extends EntityBase {
+ private final int id;
+ private final int projectId;
+ private final int userId;
+}
diff --git a/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/ProjectMember.java b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/ProjectMember.java
new file mode 100644
index 00000000..e10a19b3
--- /dev/null
+++ b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/ProjectMember.java
@@ -0,0 +1,41 @@
+package de.rwth.dbis.acis.bazaar.service.dal.entities;
+
+import com.fasterxml.jackson.annotation.JsonGetter;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import lombok.Builder;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.extern.jackson.Jacksonized;
+
+import javax.validation.constraints.NotNull;
+
+/**
+ * Abstracts the project membership data
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+@Jacksonized
+@Builder(builderClassName = "Builder")
+public class ProjectMember extends EntityBase {
+
+ private int id;
+
+ @NotNull
+ private int userId;
+
+ @NotNull
+ private ProjectRole role;
+
+ @JsonIgnore
+ private User user;
+
+ @JsonGetter("userProfileImage")
+ public String getProfileImage() {
+ return user.getProfileImage();
+ }
+
+ @JsonGetter("userName")
+ public String getUserName() {
+ return user.getUserName();
+ }
+}
diff --git a/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/ProjectRole.java b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/ProjectRole.java
new file mode 100644
index 00000000..b5add1ad
--- /dev/null
+++ b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/ProjectRole.java
@@ -0,0 +1,7 @@
+package de.rwth.dbis.acis.bazaar.service.dal.entities;
+
+public enum ProjectRole {
+ ProjectMember,
+ ProjectManager,
+ ProjectAdmin
+}
diff --git a/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/Requirement.java b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/Requirement.java
new file mode 100644
index 00000000..d97ed23d
--- /dev/null
+++ b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/Requirement.java
@@ -0,0 +1,96 @@
+package de.rwth.dbis.acis.bazaar.service.dal.entities;
+
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.JsonNode;
+import de.rwth.dbis.acis.bazaar.service.dal.helpers.CreateValidation;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Builder;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.extern.jackson.Jacksonized;
+
+import javax.validation.constraints.Min;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+import java.time.OffsetDateTime;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Requirement entity
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+@Jacksonized
+@Builder(builderClassName = "Builder")
+public class Requirement extends EntityBase implements Ownable {
+
+ private int id;
+
+ @NotNull
+ @Size(min = 1, max = 50, message = "name must be between 1 and 50 characters")
+ private String name;
+
+ @NotNull(message = "description should not be null", groups = CreateValidation.class)
+ @Size(min = 1, message = "Description can't be empty")
+ private String description;
+
+ @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZ", timezone = "Europe/Berlin")
+ private OffsetDateTime realized;
+
+ @Min(value = 0)
+ @NotNull(message = "A project id must be provided", groups = CreateValidation.class)
+ private int projectId;
+
+ private User creator;
+ private User leadDeveloper;
+
+ @NotNull(message = "categories should not be null", groups = CreateValidation.class)
+ @Size(min = 1, groups = CreateValidation.class)
+ private List categories;
+
+ // This field is not filled because attachments should be not included in requirements response.
+ // But the API still allows to create a requirement with attachments at the same time.
+ private List attachments;
+
+ @lombok.Builder.Default
+ private List tags = new ArrayList<>();
+
+ @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZ", timezone = "Europe/Berlin")
+ private OffsetDateTime creationDate;
+
+ @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZ", timezone = "Europe/Berlin")
+ private OffsetDateTime lastUpdatedDate;
+
+ @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZ", timezone = "Europe/Berlin")
+ private OffsetDateTime lastActivity;
+
+ private Integer numberOfComments;
+ private Integer numberOfAttachments;
+ private Integer numberOfFollowers;
+
+ private int upVotes;
+ private int downVotes;
+
+ private UserContext userContext;
+
+ @ApiModelProperty(
+ dataType = "java.util.Map"
+ )
+ private JsonNode additionalProperties;
+
+ @JsonProperty("_context")
+ private EntityContext context;
+
+ @Override
+ public boolean isOwner(User user) {
+ return creator == user;
+ }
+
+ @Override
+ public boolean isOwner(Integer userId) {
+ return creator.getId() == userId;
+ }
+}
diff --git a/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/RequirementCategory.java b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/RequirementCategory.java
new file mode 100644
index 00000000..4de6229a
--- /dev/null
+++ b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/RequirementCategory.java
@@ -0,0 +1,40 @@
+/*
+ *
+ * Copyright (c) 2014, RWTH Aachen University.
+ * For a list of contributors see the AUTHORS file at the top-level directory
+ * of this distribution.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * /
+ */
+
+package de.rwth.dbis.acis.bazaar.service.dal.entities;
+
+import lombok.Builder;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.extern.jackson.Jacksonized;
+
+/**
+ * @since 6/11/2014
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+@Jacksonized
+@Builder(builderClassName = "Builder")
+public class RequirementCategory extends EntityBase {
+
+ private final int id;
+ private final int categoryId;
+ private final int requirementId;
+}
diff --git a/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/RequirementContributors.java b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/RequirementContributors.java
new file mode 100644
index 00000000..c07e0166
--- /dev/null
+++ b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/RequirementContributors.java
@@ -0,0 +1,28 @@
+package de.rwth.dbis.acis.bazaar.service.dal.entities;
+
+import lombok.Builder;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.extern.jackson.Jacksonized;
+
+import java.util.List;
+
+// import javax.ws.rs.core.Link;
+
+/**
+ * Created by Martin on 12.06.2017.
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+@Jacksonized
+@Builder(builderClassName = "Builder")
+public class RequirementContributors extends EntityBase {
+
+ private final int id;
+
+ private User creator;
+ private User leadDeveloper;
+ private List developers;
+ private List commentCreator;
+ private List attachmentCreator;
+}
diff --git a/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/RequirementDeveloper.java b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/RequirementDeveloper.java
new file mode 100644
index 00000000..10e8bf36
--- /dev/null
+++ b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/RequirementDeveloper.java
@@ -0,0 +1,39 @@
+/*
+ *
+ * Copyright (c) 2014, RWTH Aachen University.
+ * For a list of contributors see the AUTHORS file at the top-level directory
+ * of this distribution.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * /
+ */
+
+package de.rwth.dbis.acis.bazaar.service.dal.entities;
+
+import lombok.Builder;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.extern.jackson.Jacksonized;
+
+/**
+ * @since 6/11/2014
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+@Jacksonized
+@Builder(builderClassName = "Builder")
+public class RequirementDeveloper extends EntityBase {
+ private final int id;
+ private final int requirementId;
+ private final int userId;
+}
diff --git a/src/test/de/rwth/dbis/acis/bazaar/service/TestBase.java b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/RequirementFollower.java
similarity index 62%
rename from src/test/de/rwth/dbis/acis/bazaar/service/TestBase.java
rename to reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/RequirementFollower.java
index 3726e2b5..14e4210a 100644
--- a/src/test/de/rwth/dbis/acis/bazaar/service/TestBase.java
+++ b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/RequirementFollower.java
@@ -1,4 +1,4 @@
- /*
+/*
*
* Copyright (c) 2014, RWTH Aachen University.
* For a list of contributors see the AUTHORS file at the top-level directory
@@ -18,10 +18,22 @@
* /
*/
-package de.rwth.dbis.acis.bazaar.service;
+package de.rwth.dbis.acis.bazaar.service.dal.entities;
+import lombok.Builder;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.extern.jackson.Jacksonized;
-// TODO: update tests, see older revision
-public abstract class TestBase {
-
+/**
+ * @since 6/11/2014
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+@Jacksonized
+@Builder(builderClassName = "Builder")
+public class RequirementFollower extends EntityBase {
+ private final int id;
+ private final int requirementId;
+ private final int userId;
}
diff --git a/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/RequirementTag.java b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/RequirementTag.java
new file mode 100644
index 00000000..e9b0e1c7
--- /dev/null
+++ b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/RequirementTag.java
@@ -0,0 +1,17 @@
+package de.rwth.dbis.acis.bazaar.service.dal.entities;
+
+import lombok.Builder;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.extern.jackson.Jacksonized;
+
+@EqualsAndHashCode(callSuper = true)
+@Data
+@Jacksonized
+@Builder(builderClassName = "Builder")
+public class RequirementTag extends EntityBase {
+ private final int id;
+ private final int requirementId;
+
+ private int tagId;
+}
diff --git a/src/main/de/rwth/dbis/acis/bazaar/service/dal/entities/Role.java b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/Role.java
similarity index 51%
rename from src/main/de/rwth/dbis/acis/bazaar/service/dal/entities/Role.java
rename to reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/Role.java
index 1c97c8ce..603acf0d 100644
--- a/src/main/de/rwth/dbis/acis/bazaar/service/dal/entities/Role.java
+++ b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/Role.java
@@ -20,68 +20,53 @@
package de.rwth.dbis.acis.bazaar.service.dal.entities;
+import lombok.Builder;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.extern.jackson.Jacksonized;
+import net.minidev.json.annotate.JsonIgnore;
+
import java.util.List;
/**
- * @author Adam Gavronek
* @since 2/17/2015
*/
+@EqualsAndHashCode(callSuper = true)
+@Data
+@Jacksonized
+@Builder(builderClassName = "Builder")
public class Role extends EntityBase {
- private final int Id;
+ private final int id;
private final List privileges;
-
private final String name;
- private Role(Builder builder) {
- Id = builder.id;
- this.privileges = builder.privileges;
-
- this.name = builder.name;
+ @JsonIgnore
+ public boolean isProjectScoped() {
+ try {
+ ProjectRole.valueOf(name);
+ return true;
+ } catch (IllegalArgumentException e) {
+ return false;
+ }
}
@Override
- public int getId() {
- return Id;
- }
-
- public List getPrivileges() {
- return privileges;
- }
-
-
- public String getName() {
- return name;
- }
-
- public static Builder getBuilder(String name) {
- return new Builder(name);
- }
-
- public static class Builder {
- private String name;
- private int id;
- private List privileges;
-
-
- public Builder(String name) {
- this.name = name;
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
}
-
- public Builder id(int id) {
- this.id = id;
- return this;
- }
-
- public Builder privileges(List privileges) {
- this.privileges = privileges;
- return this;
+ if (!(o instanceof Role)) {
+ return false;
}
+ Role other = (Role) o;
- public Role build() {
- return new Role(this);
+ if (isProjectScoped()) {
+ return id == other.id;
}
+ return name.equals(other.name);
}
+
}
diff --git a/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/Statistic.java b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/Statistic.java
new file mode 100644
index 00000000..0382e495
--- /dev/null
+++ b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/Statistic.java
@@ -0,0 +1,29 @@
+package de.rwth.dbis.acis.bazaar.service.dal.entities;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
+import lombok.Builder;
+import lombok.Data;
+import lombok.extern.jackson.Jacksonized;
+
+/**
+ * Created by hugif on 26.12.2016.
+ */
+@Data
+@Jacksonized
+@Builder(builderClassName = "Builder")
+public class Statistic {
+
+ private int numberOfProjects;
+ private int numberOfCategories;
+ private int numberOfRequirements;
+ private int numberOfComments;
+ private int numberOfAttachments;
+ private int numberOfVotes;
+
+ public String toJSON() throws JsonProcessingException {
+ return new ObjectMapper().registerModule(new JavaTimeModule()).setSerializationInclusion(JsonInclude.Include.NON_NULL).writeValueAsString(this);
+ }
+}
diff --git a/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/SystemRole.java b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/SystemRole.java
new file mode 100644
index 00000000..1dcaa357
--- /dev/null
+++ b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/SystemRole.java
@@ -0,0 +1,7 @@
+package de.rwth.dbis.acis.bazaar.service.dal.entities;
+
+public enum SystemRole {
+ Anonymous,
+ LoggedInUser,
+ SystemAdmin,
+}
diff --git a/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/Tag.java b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/Tag.java
new file mode 100644
index 00000000..d1beabb2
--- /dev/null
+++ b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/Tag.java
@@ -0,0 +1,27 @@
+package de.rwth.dbis.acis.bazaar.service.dal.entities;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import lombok.Builder;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.extern.jackson.Jacksonized;
+
+import javax.validation.constraints.NotNull;
+
+@EqualsAndHashCode(callSuper = true)
+@Data
+@Jacksonized
+@Builder(builderClassName = "Builder")
+public class Tag extends EntityBase {
+
+ private final int id;
+
+ @NotNull
+ private String name;
+
+ @NotNull
+ private String colour;
+
+ @JsonIgnore
+ private Integer projectId;
+}
diff --git a/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/User.java b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/User.java
new file mode 100644
index 00000000..d2091d73
--- /dev/null
+++ b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/User.java
@@ -0,0 +1,126 @@
+/*
+ *
+ * Copyright (c) 2014, RWTH Aachen University.
+ * For a list of contributors see the AUTHORS file at the top-level directory
+ * of this distribution.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * /
+ */
+
+package de.rwth.dbis.acis.bazaar.service.dal.entities;
+
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonView;
+import de.rwth.dbis.acis.bazaar.service.dal.helpers.SerializerViews;
+import lombok.Builder;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.extern.jackson.Jacksonized;
+
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+import java.time.OffsetDateTime;
+import java.util.Objects;
+
+@EqualsAndHashCode(callSuper = true)
+@Data
+@Jacksonized
+@Builder(builderClassName = "Builder")
+public class User extends EntityBase {
+
+ private int id;
+
+ @NotNull(message = "Username can't be null")
+ @Size(min = 1, max = 1000, message = "Username must have between 1 and 1000 characters")
+ private String userName;
+
+ @Size(min = 1, max = 1000, message = "first name must have between 1 and 1000 characters")
+ @JsonView(SerializerViews.Private.class)
+ private String firstName;
+
+ @Size(min = 1, max = 1000, message = "last name must have between 1 and 1000 characters")
+ @JsonView(SerializerViews.Private.class)
+ private String lastName;
+
+ @NotNull(message = "eMail can't be null")
+ @Size(min = 1, max = 1000, message = "eMail must have between 1 and 1000 characters")
+ private transient String eMail;
+
+ @NotNull(message = "las2peerId can't be null")
+ @Size(min = 1, max = 1000, message = "las2peerId must have between 1 and 1000 characters")
+ @JsonView(SerializerViews.Private.class)
+ private String las2peerId;
+
+ private String profileImage;
+
+ @JsonView(SerializerViews.Private.class)
+ private Boolean emailLeadSubscription;
+
+ @JsonView(SerializerViews.Private.class)
+ private Boolean emailFollowSubscription;
+
+ @JsonView(SerializerViews.Private.class)
+ private Boolean personalizationEnabled;
+
+ @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZ", timezone = "Europe/Berlin")
+ @JsonView(SerializerViews.Private.class)
+ private OffsetDateTime creationDate;
+
+ @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZ", timezone = "Europe/Berlin")
+ @JsonView(SerializerViews.Private.class)
+ private OffsetDateTime lastUpdatedDate;
+
+ @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZ", timezone = "Europe/Berlin")
+ @JsonView(SerializerViews.Private.class)
+ private OffsetDateTime lastLoginDate;
+
+ @JsonView(SerializerViews.Private.class)
+ public String getEMail() {
+ return eMail;
+ }
+
+ @JsonView(SerializerViews.Private.class)
+ public Boolean isEmailLeadSubscription() {
+ return emailLeadSubscription != null && emailLeadSubscription;
+ }
+
+ @JsonView(SerializerViews.Private.class)
+ public Boolean isEmailFollowSubscription() {
+ return emailFollowSubscription != null && emailFollowSubscription;
+ }
+
+ @JsonView(SerializerViews.Private.class)
+ public Boolean isPersonalizationEnabled() {
+ return personalizationEnabled != null && personalizationEnabled;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (!(o instanceof User)) {
+ return false;
+ }
+ User other = (User) o;
+
+ return las2peerId.equals(other.las2peerId);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(id, userName, firstName, lastName, eMail, las2peerId, profileImage, emailLeadSubscription, emailFollowSubscription, personalizationEnabled, creationDate, lastUpdatedDate, lastLoginDate);
+ }
+}
diff --git a/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/UserContext.java b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/UserContext.java
new file mode 100644
index 00000000..e5bc98f2
--- /dev/null
+++ b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/UserContext.java
@@ -0,0 +1,50 @@
+package de.rwth.dbis.acis.bazaar.service.dal.entities;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import de.rwth.dbis.acis.bazaar.service.dal.helpers.UserVote;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Builder;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.extern.jackson.Jacksonized;
+
+import javax.validation.constraints.NotNull;
+
+/**
+ * Dynamic object to provide the context the currently logged in user has provided to the related object
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+@Jacksonized
+@Builder(builderClassName = "Builder")
+public class UserContext extends EntityBase {
+
+ @ApiModelProperty(
+ value = "The role the user has within the project. Only returned when requesting project resources."
+ )
+ private ProjectRole userRole;
+
+ @ApiModelProperty(
+ value = "Only returned when requesting requirement resources."
+ )
+ private UserVote userVoted;
+
+ @NotNull
+ private Boolean isFollower;
+
+ @ApiModelProperty(
+ value = "Only returned when requesting requirement resources."
+ )
+ private Boolean isDeveloper;
+
+ @ApiModelProperty(
+ value = "Only returned when requesting requirement resources."
+ )
+ private Boolean isContributor;
+
+ @JsonIgnore
+ @Override
+ public int getId() {
+ return 0;
+ }
+}
diff --git a/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/Vote.java b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/Vote.java
new file mode 100644
index 00000000..95d43730
--- /dev/null
+++ b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/Vote.java
@@ -0,0 +1,40 @@
+/*
+ *
+ * Copyright (c) 2014, RWTH Aachen University.
+ * For a list of contributors see the AUTHORS file at the top-level directory
+ * of this distribution.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * /
+ */
+
+package de.rwth.dbis.acis.bazaar.service.dal.entities;
+
+import lombok.Builder;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.extern.jackson.Jacksonized;
+
+/**
+ * @since 6/11/2014
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+@Jacksonized
+@Builder(builderClassName = "Builder")
+public class Vote extends EntityBase {
+ private final int id;
+ private final boolean isUpvote;
+ private final int requirementId;
+ private final int userId;
+}
diff --git a/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/VoteDirection.java b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/VoteDirection.java
new file mode 100644
index 00000000..07f5c86b
--- /dev/null
+++ b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/entities/VoteDirection.java
@@ -0,0 +1,6 @@
+package de.rwth.dbis.acis.bazaar.service.dal.entities;
+
+public enum VoteDirection {
+ up,
+ down,
+}
diff --git a/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/helpers/CreateValidation.java b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/helpers/CreateValidation.java
new file mode 100644
index 00000000..f62624ab
--- /dev/null
+++ b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/helpers/CreateValidation.java
@@ -0,0 +1,11 @@
+package de.rwth.dbis.acis.bazaar.service.dal.helpers;
+
+import javax.validation.GroupSequence;
+import javax.validation.groups.Default;
+
+/**
+ * Does nothing but acts as a group for hibernate validation
+ */
+@GroupSequence(Default.class)
+public interface CreateValidation {
+}
diff --git a/src/main/de/rwth/dbis/acis/bazaar/service/dal/helpers/CreationStatus.java b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/helpers/CreationStatus.java
similarity index 94%
rename from src/main/de/rwth/dbis/acis/bazaar/service/dal/helpers/CreationStatus.java
rename to reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/helpers/CreationStatus.java
index 804ae8fe..c170f1fc 100644
--- a/src/main/de/rwth/dbis/acis/bazaar/service/dal/helpers/CreationStatus.java
+++ b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/helpers/CreationStatus.java
@@ -21,7 +21,6 @@
package de.rwth.dbis.acis.bazaar.service.dal.helpers;
/**
- * @author Adam Gavronek
* @since 2/27/2015
*/
public enum CreationStatus {
diff --git a/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/helpers/EntityContextFactory.java b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/helpers/EntityContextFactory.java
new file mode 100644
index 00000000..a8df9ff7
--- /dev/null
+++ b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/helpers/EntityContextFactory.java
@@ -0,0 +1,62 @@
+package de.rwth.dbis.acis.bazaar.service.dal.helpers;
+
+
+import de.rwth.dbis.acis.bazaar.dal.jooq.tables.records.CategoryRecord;
+import de.rwth.dbis.acis.bazaar.dal.jooq.tables.records.ProjectRecord;
+import de.rwth.dbis.acis.bazaar.dal.jooq.tables.records.RequirementRecord;
+import de.rwth.dbis.acis.bazaar.service.dal.entities.Category;
+import de.rwth.dbis.acis.bazaar.service.dal.entities.EntityContext;
+import de.rwth.dbis.acis.bazaar.service.dal.entities.Project;
+import de.rwth.dbis.acis.bazaar.service.dal.entities.Requirement;
+import de.rwth.dbis.acis.bazaar.service.dal.transform.CategoryTransformer;
+import de.rwth.dbis.acis.bazaar.service.dal.transform.ProjectTransformer;
+import de.rwth.dbis.acis.bazaar.service.dal.transform.RequirementTransformer;
+import org.jooq.Record;
+
+import java.util.List;
+
+public class EntityContextFactory{
+ private static ProjectTransformer projectTransformer;
+ private static CategoryTransformer categoryTransformer;
+ private static RequirementTransformer requirementTransformer;
+
+
+ public static EntityContext create(List embed, Record record){
+ EntityContext.Builder contextBuilder = EntityContext.builder();
+ if(embed != null) {
+ for (String entry : embed) {
+ if (entry.equalsIgnoreCase("project")) {
+ contextBuilder.project(transformToProject(record));
+
+ } else if (entry.equalsIgnoreCase("category")) {
+ //TODO Need to handle multiple Categories
+ //context.category(transformToCategory(record));
+
+ } else if (entry.equalsIgnoreCase("requirement")) {
+ contextBuilder.requirement(transformToRequirement(record));
+ }
+ }
+ }
+ return contextBuilder.build();
+ }
+
+ private static Project transformToProject(Record record){
+ projectTransformer = (projectTransformer != null) ? projectTransformer : new ProjectTransformer();
+ ProjectRecord projectRecord = record.into(ProjectRecord.class);
+ return projectTransformer.getEntityFromTableRecord(projectRecord);
+ }
+ private static Category transformToCategory(Record record){
+ categoryTransformer = (categoryTransformer != null) ? categoryTransformer : new CategoryTransformer();
+ CategoryRecord categoryRecord = record.into(CategoryRecord.class);
+ return categoryTransformer.getEntityFromTableRecord(categoryRecord);
+ }
+ private static Requirement transformToRequirement(Record record){
+ requirementTransformer = (requirementTransformer != null) ? requirementTransformer : new RequirementTransformer();
+ RequirementRecord requirementRecord= record.into(RequirementRecord.class);
+ return requirementTransformer.getEntityFromTableRecord(requirementRecord);
+ }
+
+
+
+
+}
diff --git a/src/main/de/rwth/dbis/acis/bazaar/service/dal/helpers/PageInfo.java b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/helpers/PageInfo.java
similarity index 61%
rename from src/main/de/rwth/dbis/acis/bazaar/service/dal/helpers/PageInfo.java
rename to reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/helpers/PageInfo.java
index 1fa917b1..518339a3 100644
--- a/src/main/de/rwth/dbis/acis/bazaar/service/dal/helpers/PageInfo.java
+++ b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/helpers/PageInfo.java
@@ -20,8 +20,7 @@
package de.rwth.dbis.acis.bazaar.service.dal.helpers;
-import jodd.vtor.constraint.Min;
-
+import javax.validation.constraints.Min;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -32,6 +31,7 @@
* @since 6/15/2014
*/
public class PageInfo implements Pageable {
+
@Min(-1)
private final int pageNumber;
@Min(0)
@@ -39,21 +39,45 @@ public class PageInfo implements Pageable {
private final Map filters;
private final List sorts;
private final String search;
+ private final List ids;
+ private final List embed;
+ private final Map options;
+
public PageInfo(int pageNumber, int pageSize) {
- this(pageNumber, pageSize, new HashMap<>(), new ArrayList<>(), null);
+ this(pageNumber, pageSize, new HashMap<>(), new ArrayList<>(), null, null);
}
+
public PageInfo(int pageNumber, int pageSize, Map filters) {
this(pageNumber, pageSize, filters, new ArrayList<>(), null);
}
+ public PageInfo(int pageNumber, int pageSize, Map filters, List sorts) {
+ this(pageNumber, pageSize, filters, sorts, null);
+ }
+
public PageInfo(int pageNumber, int pageSize, Map filters, List sorts, String search) {
+ this(pageNumber, pageSize, filters, sorts, search, null);
+ }
+
+ public PageInfo(int pageNumber, int pageSize, Map filters, List sorts, String search, List ids) {
+ this(pageNumber, pageSize, filters, sorts, search, ids, null);
+ }
+
+ public PageInfo(int pageNumber, int pageSize, Map filters, List sorts, String search, List ids, List embed) {
+ this(pageNumber, pageSize, filters, sorts, search, ids, embed, new HashMap<>());
+ }
+
+ public PageInfo(int pageNumber, int pageSize, Map filters, List sorts, String search, List ids, List embed, Map options) {
this.pageNumber = pageNumber;
this.pageSize = pageSize;
this.filters = filters;
this.sorts = sorts;
this.search = search != null ? search : "";
+ this.ids = ids != null ? ids : new ArrayList<>();
+ this.embed = embed;
+ this.options = options;
}
@Override
@@ -81,6 +105,21 @@ public List getSorts() {
return sorts;
}
+ @Override
+ public List getIds() {
+ return ids;
+ }
+
+ @Override
+ public List getEmbed() {
+ return embed;
+ }
+
+ @Override
+ public Map getOptions() {
+ return options;
+ }
+
@Override
public String getSearch() {
return search;
diff --git a/src/main/de/rwth/dbis/acis/bazaar/service/dal/helpers/Pageable.java b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/helpers/Pageable.java
similarity index 70%
rename from src/main/de/rwth/dbis/acis/bazaar/service/dal/helpers/Pageable.java
rename to reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/helpers/Pageable.java
index a7d66eb2..b2ea68d9 100644
--- a/src/main/de/rwth/dbis/acis/bazaar/service/dal/helpers/Pageable.java
+++ b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/helpers/Pageable.java
@@ -22,9 +22,9 @@
import java.util.List;
import java.util.Map;
+import java.util.Objects;
/**
- * @author Adam Gavronek
* @since 6/12/2014
*/
public interface Pageable {
@@ -40,13 +40,31 @@ public interface Pageable {
String getSearch();
+ List getIds();
+
+ List getEmbed();
+
+ Map getOptions();
+
+ enum SortDirection {
+ DEFAULT, ASC, DESC
+ }
+
class SortField {
String field;
SortDirection sortDirection;
- public SortField(String field, SortDirection sortDirection) {
+ public SortField(String field, String sortDirection) {
this.field = field;
- this.sortDirection = sortDirection;
+
+ // Use Object.equals here for no extra null check (else should cover this)
+ if (Objects.equals(sortDirection, "ASC")) {
+ this.sortDirection = SortDirection.ASC;
+ } else if (Objects.equals(sortDirection, "DESC")) {
+ this.sortDirection = SortDirection.DESC;
+ } else {
+ this.sortDirection = SortDirection.DEFAULT;
+ }
}
public String getField() {
@@ -57,8 +75,4 @@ public SortDirection getSortDirection() {
return sortDirection;
}
}
-
- enum SortDirection {
- DEFAULT, ASC, DESC
- }
}
diff --git a/src/main/de/rwth/dbis/acis/bazaar/service/dal/helpers/PaginationResult.java b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/helpers/PaginationResult.java
similarity index 81%
rename from src/main/de/rwth/dbis/acis/bazaar/service/dal/helpers/PaginationResult.java
rename to reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/helpers/PaginationResult.java
index 7d0d26ac..22dc545e 100644
--- a/src/main/de/rwth/dbis/acis/bazaar/service/dal/helpers/PaginationResult.java
+++ b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/helpers/PaginationResult.java
@@ -4,6 +4,7 @@
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import java.util.List;
@@ -52,6 +53,9 @@ public int getNextPage() {
}
public String toJSON() throws JsonProcessingException {
- return new ObjectMapper().setSerializationInclusion(JsonInclude.Include.NON_NULL).writeValueAsString(this.getElements());
+ return new ObjectMapper().registerModule(new JavaTimeModule())
+ .setSerializationInclusion(JsonInclude.Include.NON_NULL)
+ .writerWithView(SerializerViews.Public.class)
+ .writeValueAsString(this.getElements());
}
}
diff --git a/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/helpers/SerializerViews.java b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/helpers/SerializerViews.java
new file mode 100644
index 00000000..f3b0a5f7
--- /dev/null
+++ b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/helpers/SerializerViews.java
@@ -0,0 +1,8 @@
+package de.rwth.dbis.acis.bazaar.service.dal.helpers;
+
+public class SerializerViews {
+ public static class Public {
+ }
+ public static class Private extends Public {
+ }
+}
diff --git a/src/main/de/rwth/dbis/acis/bazaar/service/dal/helpers/UserVote.java b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/helpers/UserVote.java
similarity index 93%
rename from src/main/de/rwth/dbis/acis/bazaar/service/dal/helpers/UserVote.java
rename to reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/helpers/UserVote.java
index 71313e21..5be4c2e2 100644
--- a/src/main/de/rwth/dbis/acis/bazaar/service/dal/helpers/UserVote.java
+++ b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/helpers/UserVote.java
@@ -21,7 +21,6 @@
package de.rwth.dbis.acis.bazaar.service.dal.helpers;
/**
- * @author Adam Gavronek
* @since 2/27/2015
*/
public enum UserVote {
@@ -29,4 +28,3 @@ public enum UserVote {
DOWN_VOTE,
NO_VOTE
}
-
diff --git a/src/main/de/rwth/dbis/acis/bazaar/service/dal/repositories/AttachmentRepository.java b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/repositories/AttachmentRepository.java
similarity index 100%
rename from src/main/de/rwth/dbis/acis/bazaar/service/dal/repositories/AttachmentRepository.java
rename to reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/repositories/AttachmentRepository.java
diff --git a/src/main/de/rwth/dbis/acis/bazaar/service/dal/repositories/AttachmentRepositoryImpl.java b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/repositories/AttachmentRepositoryImpl.java
similarity index 92%
rename from src/main/de/rwth/dbis/acis/bazaar/service/dal/repositories/AttachmentRepositoryImpl.java
rename to reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/repositories/AttachmentRepositoryImpl.java
index 276a149c..b4af8b59 100644
--- a/src/main/de/rwth/dbis/acis/bazaar/service/dal/repositories/AttachmentRepositoryImpl.java
+++ b/reqbaz/src/main/java/de/rwth/dbis/acis/bazaar/service/dal/repositories/AttachmentRepositoryImpl.java
@@ -20,11 +20,11 @@
package de.rwth.dbis.acis.bazaar.service.dal.repositories;
+import de.rwth.dbis.acis.bazaar.dal.jooq.tables.records.AttachmentRecord;
+import de.rwth.dbis.acis.bazaar.dal.jooq.tables.records.UserRecord;
import de.rwth.dbis.acis.bazaar.service.dal.entities.Attachment;
import de.rwth.dbis.acis.bazaar.service.dal.helpers.Pageable;
import de.rwth.dbis.acis.bazaar.service.dal.helpers.PaginationResult;
-import de.rwth.dbis.acis.bazaar.service.dal.jooq.tables.records.AttachmentRecord;
-import de.rwth.dbis.acis.bazaar.service.dal.jooq.tables.records.UserRecord;
import de.rwth.dbis.acis.bazaar.service.dal.transform.AttachmentTransformer;
import de.rwth.dbis.acis.bazaar.service.dal.transform.UserTransformer;
import de.rwth.dbis.acis.bazaar.service.exception.BazaarException;
@@ -39,10 +39,9 @@
import java.util.ArrayList;
import java.util.List;
-import static de.rwth.dbis.acis.bazaar.service.dal.jooq.Tables.*;
+import static de.rwth.dbis.acis.bazaar.dal.jooq.Tables.*;
/**
- * @author Adam Gavronek
* @since 6/22/2014
*/
public class AttachmentRepositoryImpl extends RepositoryImpl implements AttachmentRepository {
@@ -58,7 +57,7 @@ public AttachmentRepositoryImpl(DSLContext jooq) {
public Attachment findById(int id) throws Exception {
Attachment attachment = null;
try {
- de.rwth.dbis.acis.bazaar.service.dal.jooq.tables.User creatorUser = USER.as("creatorUser");
+ de.rwth.dbis.acis.bazaar.dal.jooq.tables.User creatorUser = USER.as("creatorUser");
Record record = jooq.selectFrom(ATTACHMENT
.join(creatorUser).on(creatorUser.ID.equal(ATTACHMENT.USER_ID)))
.where(transformer.getTableId().equal(id))
@@ -84,7 +83,7 @@ public PaginationResult findAllByRequirementId(int requirementId, Pa
List attachments;
try {
attachments = new ArrayList<>();
- de.rwth.dbis.acis.bazaar.service.dal.jooq.tables.User creatorUser = USER.as("creatorUser");
+ de.rwth.dbis.acis.bazaar.dal.jooq.tables.User creatorUser = USER.as("creatorUser");
Field