getNotFoundUsersIds() {
47 | return notFoundUsersIds;
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/backend/src/main/java/com/github/joumenharzli/surveypoc/repository/dao/DaoException.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 Joumen Harzli
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 | * in compliance with the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License
10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 | * or implied. See the License for the specific language governing permissions and limitations under
12 | * the License.
13 | *
14 | */
15 |
16 | package com.github.joumenharzli.surveypoc.repository.dao;
17 |
18 | /**
19 | * This exception encapsulates SQLs related exceptions
20 | *
21 | * @author Joumen Harzli
22 | */
23 | public class DaoException extends RuntimeException {
24 | /**
25 | * Constructs a new runtime exception with the specified detail message and
26 | * cause. Note that the detail message associated with
27 | * {@code cause} is not automatically incorporated in
28 | * this runtime exception's detail message.
29 | *
30 | * @param message the detail message (which is saved for later retrieval
31 | * by the {@link #getMessage()} method).
32 | * @param cause the cause (which is saved for later retrieval by the
33 | * {@link #getCause()} method). (A null value is
34 | * permitted, and indicates that the cause is nonexistent or
35 | * unknown.)
36 | * @since 1.4
37 | */
38 | public DaoException(String message, Throwable cause) {
39 | super(message, cause);
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/backend/src/main/java/com/github/joumenharzli/surveypoc/repository/dao/JdbcQuestionDao.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 Joumen Harzli
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 | * in compliance with the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License
10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 | * or implied. See the License for the specific language governing permissions and limitations under
12 | * the License.
13 | *
14 | */
15 |
16 | package com.github.joumenharzli.surveypoc.repository.dao;
17 |
18 | import java.util.List;
19 | import java.util.stream.Collectors;
20 |
21 | import org.simpleflatmapper.jdbc.spring.JdbcTemplateMapperFactory;
22 | import org.springframework.jdbc.core.JdbcTemplate;
23 | import org.springframework.jdbc.core.ResultSetExtractor;
24 | import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
25 | import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
26 | import org.springframework.stereotype.Repository;
27 | import org.springframework.util.Assert;
28 |
29 | import com.github.joumenharzli.surveypoc.domain.Question;
30 |
31 | /**
32 | * JDBC implementation for {@link QuestionDao}
33 | *
34 | * @author Joumen Harzli
35 | */
36 | @Repository
37 | public class JdbcQuestionDao implements QuestionDao {
38 |
39 | private static final String SELECT_QUESTIONS_AND_SUBJECTS = "SELECT q.id AS id, q.label AS label, s.id AS subject_id, " +
40 | " s.label AS subject_label FROM questions q" +
41 | " LEFT OUTER JOIN subjects s ON q.subject_id = s.id" +
42 | " ORDER BY id";
43 |
44 | private static final String FIND_QUESTIONS_BY_IDS = "SELECT q.id AS id FROM questions q WHERE id IN (:questions_ids)";
45 |
46 | private final NamedParameterJdbcTemplate parameterJdbcTemplate;
47 | private final JdbcTemplate jdbcTemplate;
48 |
49 | private final ResultSetExtractor> selectQuestionAndSubjectResultSetExtractor =
50 | JdbcTemplateMapperFactory
51 | .newInstance()
52 | .addKeys("id", "subject_id")
53 | .newResultSetExtractor(Question.class);
54 |
55 | public JdbcQuestionDao(JdbcTemplate jdbcTemplate, NamedParameterJdbcTemplate parameterJdbcTemplate) {
56 | this.jdbcTemplate = jdbcTemplate;
57 | this.parameterJdbcTemplate = parameterJdbcTemplate;
58 | }
59 |
60 | /**
61 | * find all questions with their subjects
62 | *
63 | * @return a list of the questions with subjects
64 | * @throws DaoException if there is an sql exception
65 | */
66 | @Override
67 | public List findAllQuestionsAndSubjects() {
68 | try {
69 | return jdbcTemplate.query(SELECT_QUESTIONS_AND_SUBJECTS, selectQuestionAndSubjectResultSetExtractor);
70 | } catch (Exception exception) {
71 | throw new DaoException("Unable to find subjects and questions", exception);
72 | }
73 | }
74 |
75 | /**
76 | * Returns the list of ids of the not found questions using ids
77 | *
78 | * @param questionsIds ids of the questions to check
79 | * @return a list of the ids of the not found questions
80 | * @throws DaoException if there is an sql exception
81 | * @throws IllegalArgumentException if any given argument is invalid
82 | */
83 | @Override
84 | public List findNonExistingQuestionsByQuestionsIds(List questionsIds) {
85 |
86 | Assert.notEmpty(questionsIds, "Ids of the questions cannot be null or empty");
87 | questionsIds.forEach(questionId -> Assert.notNull(questionId, "Id of the question cannot be null"));
88 |
89 | MapSqlParameterSource parameters = new MapSqlParameterSource();
90 | parameters.addValue("questions_ids", questionsIds);
91 |
92 | try {
93 | List foundQuestionsId = parameterJdbcTemplate.query(FIND_QUESTIONS_BY_IDS, parameters,
94 | (rs, rowNum) -> rs.getLong(1));
95 |
96 | //@formatter:off
97 | return questionsIds.stream()
98 | .filter(id -> !foundQuestionsId.contains(id))
99 | .collect(Collectors.toList());
100 | //@formatter:on
101 | } catch (Exception exception) {
102 | throw new DaoException("Unable to find questions by ids", exception);
103 | }
104 |
105 | }
106 |
107 | }
108 |
--------------------------------------------------------------------------------
/backend/src/main/java/com/github/joumenharzli/surveypoc/repository/dao/JdbcUserDao.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 Joumen Harzli
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 | * in compliance with the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License
10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 | * or implied. See the License for the specific language governing permissions and limitations under
12 | * the License.
13 | *
14 | */
15 |
16 | package com.github.joumenharzli.surveypoc.repository.dao;
17 |
18 | import java.util.List;
19 | import java.util.stream.Collectors;
20 |
21 | import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
22 | import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
23 | import org.springframework.stereotype.Repository;
24 | import org.springframework.util.Assert;
25 |
26 | /**
27 | * JDBC implementation for {@link UserDao}
28 | *
29 | * @author Joumen Harzli
30 | */
31 | @Repository
32 | public class JdbcUserDao implements UserDao {
33 |
34 | private static final String FIND_USERS_BY_IDS = "SELECT u.id AS id FROM users u WHERE id IN (:users_ids)";
35 |
36 | private final NamedParameterJdbcTemplate parameterJdbcTemplate;
37 |
38 | public JdbcUserDao(NamedParameterJdbcTemplate parameterJdbcTemplate) {
39 | this.parameterJdbcTemplate = parameterJdbcTemplate;
40 | }
41 |
42 | /**
43 | * Returns the list of ids of the not found users using ids
44 | *
45 | * @param usersIds ids of the users to check
46 | * @return a list of the ids of the not found users
47 | * @throws DaoException if there is an sql exception
48 | * @throws IllegalArgumentException if any given argument is invalid
49 | */
50 | @Override
51 | public List findNonExistingUsersByUsersIds(List usersIds) {
52 |
53 | Assert.notEmpty(usersIds, "Ids of the users cannot be null or empty");
54 | usersIds.forEach(userId -> Assert.notNull(userId, "Id of the user cannot be null"));
55 |
56 | MapSqlParameterSource parameters = new MapSqlParameterSource();
57 | parameters.addValue("users_ids", usersIds);
58 |
59 | try {
60 | List foundUsersId = parameterJdbcTemplate.query(FIND_USERS_BY_IDS, parameters,
61 | (rs, rowNum) -> rs.getLong(1));
62 |
63 | //@formatter:off
64 | return usersIds.stream()
65 | .filter(id -> !foundUsersId.contains(id))
66 | .collect(Collectors.toList());
67 | //@formatter:on
68 |
69 | } catch (Exception exception) {
70 | throw new DaoException("Unable to find users by ids", exception);
71 | }
72 |
73 | }
74 |
75 | }
76 |
--------------------------------------------------------------------------------
/backend/src/main/java/com/github/joumenharzli/surveypoc/repository/dao/JdbcUserResponseDao.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 Joumen Harzli
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 | * in compliance with the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License
10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 | * or implied. See the License for the specific language governing permissions and limitations under
12 | * the License.
13 | *
14 | */
15 |
16 | package com.github.joumenharzli.surveypoc.repository.dao;
17 |
18 | import java.sql.PreparedStatement;
19 | import java.sql.SQLException;
20 | import java.util.List;
21 |
22 | import org.simpleflatmapper.jdbc.spring.JdbcTemplateMapperFactory;
23 | import org.springframework.jdbc.core.BatchPreparedStatementSetter;
24 | import org.springframework.jdbc.core.JdbcTemplate;
25 | import org.springframework.jdbc.core.RowMapper;
26 | import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
27 | import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
28 | import org.springframework.jdbc.core.namedparam.SqlParameterSource;
29 | import org.springframework.jdbc.core.namedparam.SqlParameterSourceUtils;
30 | import org.springframework.stereotype.Repository;
31 | import org.springframework.util.Assert;
32 |
33 | import com.github.joumenharzli.surveypoc.domain.UserResponse;
34 |
35 | /**
36 | * JDBC implementation for {@link UserResponseDao}
37 | *
38 | * @author Joumen Harzli
39 | */
40 | @Repository
41 | public class JdbcUserResponseDao implements UserResponseDao {
42 |
43 | private static final String INSERT_USER_RESPONSE = "INSERT INTO user_responses (content,user_id,question_id) " +
44 | "VALUES (?, ?, ?)";
45 |
46 | private static final String SELECT_USER_RESPONSES_FOR_QUESTIONS = "SELECT ur.content AS content, " +
47 | "ur.question_id AS question_id, ur.user_id AS user_id FROM user_responses AS ur WHERE ur.user_id = :user_id " +
48 | "AND ur.question_id IN (:question_ids) ORDER BY ur.question_id,ur.user_id";
49 |
50 | private static final String UPDATE_USER_RESPONSE = "UPDATE user_responses SET content = :content " +
51 | "WHERE user_id = :user.id AND question_id = :question.id";
52 |
53 | private final JdbcTemplate jdbcTemplate;
54 | private final NamedParameterJdbcTemplate parameterJdbcTemplate;
55 |
56 | private final RowMapper mapper = JdbcTemplateMapperFactory
57 | .newInstance()
58 | .addKeys("id", "question_id", "user_id")
59 | .newRowMapper(UserResponse.class);
60 |
61 | public JdbcUserResponseDao(JdbcTemplate jdbcTemplate, NamedParameterJdbcTemplate parameterJdbcTemplate) {
62 | this.jdbcTemplate = jdbcTemplate;
63 | this.parameterJdbcTemplate = parameterJdbcTemplate;
64 | }
65 |
66 | /**
67 | * Add a new responses of the user
68 | *
69 | * @param userResponses entities to save
70 | * @return an array of the number of rows affected by each statement
71 | * @throws DaoException if there is an sql exception
72 | * @throws IllegalArgumentException if any given argument is invalid
73 | */
74 | @Override
75 | public int[] addUserResponses(List userResponses) {
76 |
77 | Assert.notEmpty(userResponses, "User responses cannot be null or empty");
78 |
79 | try {
80 | return jdbcTemplate.batchUpdate(INSERT_USER_RESPONSE, userResponseBatchPreparedStatementSetter(userResponses));
81 | } catch (Exception exception) {
82 | throw new DaoException("Unable to add responses of the questions for the user", exception);
83 | }
84 |
85 | }
86 |
87 | /**
88 | * Update responses of the user
89 | *
90 | * @param userResponses entities to save
91 | * @return an array of the number of rows affected by each statement
92 | * @throws DaoException if there is an sql exception
93 | * @throws IllegalArgumentException if any given argument is invalid
94 | */
95 | @Override
96 | public int[] updateUserResponses(List userResponses) {
97 |
98 | Assert.notEmpty(userResponses, "User responses cannot be null or empty");
99 |
100 | SqlParameterSource[] batchParams = SqlParameterSourceUtils.createBatch(userResponses.toArray());
101 |
102 | try {
103 | return parameterJdbcTemplate.batchUpdate(UPDATE_USER_RESPONSE, batchParams);
104 | } catch (Exception exception) {
105 | throw new DaoException("Unable to update responses of the questions for the user", exception);
106 | }
107 | }
108 |
109 | /**
110 | * Find the responses for the provided questions and user
111 | *
112 | * @param userId user who responded
113 | * @param questionsIds questions that the user may responded
114 | * @return list of responses
115 | * @throws DaoException if there is an sql exception
116 | * @throws IllegalArgumentException if any given argument is invalid
117 | */
118 | @Override
119 | public List findResponsesOfUserByUserIdAndQuestionIds(Long userId, List questionsIds) {
120 |
121 | Assert.notNull(userId, "User id cannot be null");
122 | Assert.notEmpty(questionsIds, "Questions ids cannot be null or empty");
123 | questionsIds.forEach(questionId -> Assert.notNull(questionId, "Id of the question cannot be null"));
124 |
125 | MapSqlParameterSource parameters = new MapSqlParameterSource();
126 | parameters.addValue("question_ids", questionsIds);
127 | parameters.addValue("user_id", userId);
128 |
129 | try {
130 | return parameterJdbcTemplate.query(SELECT_USER_RESPONSES_FOR_QUESTIONS, parameters, mapper);
131 | } catch (Exception exception) {
132 | throw new DaoException("Unable to find responses of the user for the questions", exception);
133 | }
134 | }
135 |
136 | /**
137 | * Batch update callback defines the way that the batch insertion
138 | * of the user responses will be executed
139 | *
140 | * @param userResponses list of the user responses
141 | * @return an instance of {@link BatchPreparedStatementSetter}
142 | */
143 | private BatchPreparedStatementSetter userResponseBatchPreparedStatementSetter(List userResponses) {
144 | return new BatchPreparedStatementSetter() {
145 | @Override
146 | public void setValues(PreparedStatement ps, int i) throws SQLException {
147 | UserResponse userResponse = userResponses.get(i);
148 | Assert.notNull(userResponse, "User response cannot be null");
149 |
150 | Long questionId = userResponse.getQuestionId();
151 | Long userId = userResponse.getUserId();
152 |
153 | Assert.notNull(questionId, "Question id in the user response entity cannot be null");
154 | Assert.notNull(userId, "User id in the user response entity cannot be null");
155 |
156 | ps.setString(1, userResponse.getContent());
157 | ps.setLong(2, userId);
158 | ps.setLong(3, questionId);
159 | }
160 |
161 | @Override
162 | public int getBatchSize() {
163 | return userResponses.size();
164 | }
165 | };
166 | }
167 |
168 | }
169 |
--------------------------------------------------------------------------------
/backend/src/main/java/com/github/joumenharzli/surveypoc/repository/dao/QuestionDao.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 Joumen Harzli
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 | * in compliance with the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License
10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 | * or implied. See the License for the specific language governing permissions and limitations under
12 | * the License.
13 | *
14 | */
15 |
16 | package com.github.joumenharzli.surveypoc.repository.dao;
17 |
18 | import java.util.List;
19 |
20 | import com.github.joumenharzli.surveypoc.domain.Question;
21 |
22 | /**
23 | * Question dao
24 | *
25 | * @author Joumen Harzli
26 | */
27 | public interface QuestionDao {
28 |
29 | /**
30 | * find all questions with their subjects
31 | *
32 | * @return a list of the questions with subjects
33 | * @throws DaoException if there is an sql exception
34 | */
35 | List findAllQuestionsAndSubjects();
36 |
37 | /**
38 | * Returns the list of ids of the not found questions using ids
39 | *
40 | * @param questionsIds ids of the questions to check
41 | * @return a list of the ids of the not found questions
42 | * @throws DaoException if there is an sql exception
43 | * @throws IllegalArgumentException if any given argument is invalid
44 | */
45 | List findNonExistingQuestionsByQuestionsIds(List questionsIds);
46 | }
47 |
--------------------------------------------------------------------------------
/backend/src/main/java/com/github/joumenharzli/surveypoc/repository/dao/UserDao.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 Joumen Harzli
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 | * in compliance with the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License
10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 | * or implied. See the License for the specific language governing permissions and limitations under
12 | * the License.
13 | *
14 | */
15 |
16 | package com.github.joumenharzli.surveypoc.repository.dao;
17 |
18 | import java.util.List;
19 |
20 | /**
21 | * User Dao
22 | *
23 | * @author Joumen Harzli
24 | */
25 | public interface UserDao {
26 |
27 | /**
28 | * Returns the list of ids of the not found users using ids
29 | *
30 | * @param usersIds ids of the users to check
31 | * @return a list of the ids of the not found users
32 | * @throws DaoException if there is an sql exception
33 | * @throws IllegalArgumentException if any given argument is invalid
34 | */
35 | List findNonExistingUsersByUsersIds(List usersIds);
36 | }
37 |
--------------------------------------------------------------------------------
/backend/src/main/java/com/github/joumenharzli/surveypoc/repository/dao/UserResponseDao.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 Joumen Harzli
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 | * in compliance with the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License
10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 | * or implied. See the License for the specific language governing permissions and limitations under
12 | * the License.
13 | *
14 | */
15 |
16 | package com.github.joumenharzli.surveypoc.repository.dao;
17 |
18 | import java.util.List;
19 |
20 | import com.github.joumenharzli.surveypoc.domain.UserResponse;
21 |
22 | /**
23 | * User Response Dao
24 | *
25 | * @author Joumen Harzli
26 | */
27 | public interface UserResponseDao {
28 |
29 | /**
30 | * Add a new responses of the user
31 | *
32 | * @param userResponses entities to save
33 | * @return an array of the number of rows affected by each statement
34 | * @throws DaoException if there is an sql exception
35 | * @throws IllegalArgumentException if any given argument is invalid
36 | */
37 | int[] addUserResponses(List userResponses);
38 |
39 | /**
40 | * Update responses of the user
41 | *
42 | * @param userResponses entities to save
43 | * @return an array of the number of rows affected by each statement
44 | * @throws DaoException if there is an sql exception
45 | * @throws IllegalArgumentException if any given argument is invalid
46 | */
47 | int[] updateUserResponses(List userResponses);
48 |
49 | /**
50 | * Find the responses for the provided questions and user
51 | *
52 | * @param userId user who responded
53 | * @param questionsIds questions that the user may responded
54 | * @return list of responses
55 | * @throws DaoException if there is an sql exception
56 | * @throws IllegalArgumentException if any given argument is invalid
57 | */
58 | List findResponsesOfUserByUserIdAndQuestionIds(Long userId, List questionsIds);
59 | }
60 |
--------------------------------------------------------------------------------
/backend/src/main/java/com/github/joumenharzli/surveypoc/service/SimpleSubjectService.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 Joumen Harzli
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 | * in compliance with the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License
10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 | * or implied. See the License for the specific language governing permissions and limitations under
12 | * the License.
13 | *
14 | */
15 |
16 | package com.github.joumenharzli.surveypoc.service;
17 |
18 | import java.util.Collections;
19 | import java.util.List;
20 |
21 | import org.slf4j.Logger;
22 | import org.slf4j.LoggerFactory;
23 | import org.springframework.stereotype.Service;
24 |
25 | import com.github.joumenharzli.surveypoc.domain.Question;
26 | import com.github.joumenharzli.surveypoc.repository.dao.QuestionDao;
27 | import com.github.joumenharzli.surveypoc.service.dto.SubjectDto;
28 | import com.github.joumenharzli.surveypoc.service.mapper.SubjectMapper;
29 |
30 | /**
31 | * A simple implementation for {@link SubjectService}
32 | *
33 | * @author Joumen Harzli
34 | */
35 | @Service
36 | public class SimpleSubjectService implements SubjectService {
37 |
38 | private static final Logger LOGGER = LoggerFactory.getLogger(SimpleSubjectService.class);
39 |
40 | private final QuestionDao questionDao;
41 | private final SubjectMapper subjectMapper;
42 |
43 | public SimpleSubjectService(QuestionDao questionDao, SubjectMapper subjectMapper) {
44 | this.questionDao = questionDao;
45 | this.subjectMapper = subjectMapper;
46 | }
47 |
48 | /**
49 | * find all the subjects and their questions
50 | *
51 | * @return a list of the questions with subjects
52 | */
53 | @Override
54 | public List findAllSubjectsAndQuestions() {
55 | LOGGER.debug("Request to get all the subjects and the questions");
56 |
57 | List questions = questionDao.findAllQuestionsAndSubjects();
58 |
59 | if (questions != null) {
60 | return subjectMapper.questionsToSubjectsDto(questions);
61 | }
62 |
63 | return Collections.emptyList();
64 | }
65 |
66 | }
67 |
--------------------------------------------------------------------------------
/backend/src/main/java/com/github/joumenharzli/surveypoc/service/SimpleUserResponseService.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 Joumen Harzli
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 | * in compliance with the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License
10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 | * or implied. See the License for the specific language governing permissions and limitations under
12 | * the License.
13 | *
14 | */
15 |
16 | package com.github.joumenharzli.surveypoc.service;
17 |
18 | import java.util.ArrayList;
19 | import java.util.Collections;
20 | import java.util.List;
21 |
22 | import org.slf4j.Logger;
23 | import org.slf4j.LoggerFactory;
24 | import org.springframework.stereotype.Service;
25 | import org.springframework.util.Assert;
26 | import org.springframework.util.CollectionUtils;
27 |
28 | import com.github.joumenharzli.surveypoc.domain.User;
29 | import com.github.joumenharzli.surveypoc.domain.UserResponse;
30 | import com.github.joumenharzli.surveypoc.exception.QuestionNotFoundException;
31 | import com.github.joumenharzli.surveypoc.exception.UserNotFoundException;
32 | import com.github.joumenharzli.surveypoc.repository.dao.QuestionDao;
33 | import com.github.joumenharzli.surveypoc.repository.dao.UserDao;
34 | import com.github.joumenharzli.surveypoc.repository.dao.UserResponseDao;
35 | import com.github.joumenharzli.surveypoc.service.dto.UserResponseForQuestionDto;
36 | import com.github.joumenharzli.surveypoc.service.mapper.QuestionMapper;
37 | import com.github.joumenharzli.surveypoc.service.mapper.UserMapper;
38 | import com.github.joumenharzli.surveypoc.service.mapper.UserResponseMapper;
39 |
40 | /**
41 | * A simple implementation for {@link UserResponseService}
42 | *
43 | * @author Joumen Harzli
44 | */
45 | @Service
46 | public class SimpleUserResponseService implements UserResponseService {
47 |
48 | private static final Logger LOGGER = LoggerFactory.getLogger(SimpleUserResponseService.class);
49 |
50 | private final UserResponseDao userResponseDao;
51 | private final UserMapper userMapper;
52 | private final QuestionMapper questionMapper;
53 | private final UserResponseMapper userResponseMapper;
54 | private final QuestionDao questionDao;
55 | private final UserDao userDao;
56 |
57 | public SimpleUserResponseService(UserResponseDao userResponseDao, UserMapper userMapper,
58 | QuestionMapper questionMapper, UserResponseMapper userResponseMapper,
59 | QuestionDao questionDao, UserDao userDao) {
60 | this.userResponseDao = userResponseDao;
61 | this.userMapper = userMapper;
62 | this.questionMapper = questionMapper;
63 | this.userResponseMapper = userResponseMapper;
64 | this.questionDao = questionDao;
65 | this.userDao = userDao;
66 | }
67 |
68 | /**
69 | * Find the responses for the provided questions and user
70 | *
71 | * @param userId id of the user who responded
72 | * @param questionsIds ids of the questions that the user may responded
73 | * @return list of responses of the user
74 | * @throws UserNotFoundException if no user was found
75 | * @throws QuestionNotFoundException if no question was found
76 | * @throws IllegalArgumentException if any given argument is invalid
77 | */
78 | @Override
79 | public List findResponsesOfUserForQuestions(Long userId, List questionsIds) {
80 | LOGGER.debug("Request to get the responses of the user {} for the questions with ids {}", userId, questionsIds);
81 |
82 | Assert.notNull(userId, "Id of the user cannot be null");
83 | Assert.notEmpty(questionsIds, "Ids of the questions cannot be null or empty");
84 | questionsIds.forEach(questionId -> Assert.notNull(questionId, "Id of the question cannot be null"));
85 |
86 | verifyUserAndQuestionsExists(userId, questionsIds);
87 |
88 | return findResponsesOfUserByUserIdAndQuestionIds(userId, questionsIds);
89 | }
90 |
91 | /**
92 | * Save the responses of the connected user for the provided questions
93 | *
94 | * @param userId id of the user who responded
95 | * @param userResponsesForQuestions questions ids and contents that the connected user entered
96 | * @return List of the saved responses of the user
97 | * @throws UserNotFoundException if no user was found
98 | * @throws QuestionNotFoundException if no question was found
99 | * @throws IllegalArgumentException if any given argument is invalid
100 | */
101 | @Override
102 | public List saveResponsesOfUserForQuestions(Long userId,
103 | List userResponsesForQuestions) {
104 | LOGGER.debug("Request to save the responses of the user {} for the questions {}", userId, userResponsesForQuestions);
105 |
106 | Assert.notNull(userId, "Id of the user cannot be null");
107 |
108 | if (CollectionUtils.isEmpty(userResponsesForQuestions)) {
109 | return Collections.emptyList();
110 | }
111 |
112 | List questionsIds = userResponseMapper.userResponsesForQuestionsToQuestionsIdsList(userResponsesForQuestions);
113 |
114 | verifyUserAndQuestionsExists(userId, questionsIds);
115 |
116 | User user = this.userMapper.toEntityFromId(userId);
117 |
118 | //@formatter:off
119 | List userResponses = userResponseMapper
120 | .userResponsesForQuestionsDtoToUserResponsesList(userResponsesForQuestions, user);
121 | //@formatter:on
122 |
123 | List existingUserResponses = userResponseDao
124 | .findResponsesOfUserByUserIdAndQuestionIds(userId, questionsIds);
125 |
126 | saveResponsesOfUserForQuestions(userResponses, existingUserResponses);
127 |
128 | return findResponsesOfUserByUserIdAndQuestionIds(userId, questionsIds);
129 | }
130 |
131 | /**
132 | * Update the existing user responses and save the new ones extracted from the provided user reponses
133 | *
134 | * @param userResponses the provided user responses
135 | * @param existingUserResponses the existing user responses
136 | */
137 | private void saveResponsesOfUserForQuestions(List userResponses, List existingUserResponses) {
138 |
139 | List userResponsesToAdd = new ArrayList<>();
140 | List userResponsesToUpdate = new ArrayList<>();
141 |
142 | userResponses.forEach((userResponse -> {
143 | if (existingUserResponses.contains(userResponse)) {
144 | userResponsesToUpdate.add(userResponse);
145 | } else {
146 | userResponsesToAdd.add(userResponse);
147 | }
148 | }));
149 |
150 | if (!CollectionUtils.isEmpty(userResponsesToAdd)) {
151 | userResponseDao.addUserResponses(userResponsesToAdd);
152 | }
153 |
154 | if (!CollectionUtils.isEmpty(userResponsesToUpdate)) {
155 | userResponseDao.updateUserResponses(userResponsesToUpdate);
156 | }
157 | }
158 |
159 | /**
160 | * Retrieve the response of the provided user for the questions from the database and map results to a list
161 | * of DTO
162 | *
163 | * @param userId user who responded to the question
164 | * @param questionsIds ids of the questions
165 | * @return list of dto of the response of the user
166 | */
167 | private List findResponsesOfUserByUserIdAndQuestionIds(Long userId, List questionsIds) {
168 | List responses = userResponseDao.findResponsesOfUserByUserIdAndQuestionIds(userId, questionsIds);
169 | return userResponseMapper.userResponseListToUserResponseForQuestionDtoList(responses);
170 | }
171 |
172 | /**
173 | * Verify that the user and the questions exist in the database
174 | *
175 | * @param userId id of the user
176 | * @param questionsIds ids of the questions
177 | * @throws UserNotFoundException if no user was found
178 | * @throws QuestionNotFoundException if no question was found
179 | */
180 | private void verifyUserAndQuestionsExists(Long userId, List questionsIds) {
181 | verifyUserExist(userId);
182 | verifyQuestionsExist(questionsIds);
183 | }
184 |
185 | /**
186 | * Verify that the user exist in the database
187 | *
188 | * @param userId id of the user
189 | * @throws UserNotFoundException if no user was found
190 | */
191 | private void verifyUserExist(Long userId) {
192 | List nonExistingUsersIds = userDao.findNonExistingUsersByUsersIds(Collections.singletonList(userId));
193 | if (!CollectionUtils.isEmpty(nonExistingUsersIds)) {
194 | throw new UserNotFoundException(nonExistingUsersIds);
195 | }
196 | }
197 |
198 | /**
199 | * Verify that the questions exists in the database
200 | *
201 | * @param questionsIds ids of the questions
202 | * @throws QuestionNotFoundException if no question was found
203 | */
204 | private void verifyQuestionsExist(List questionsIds) {
205 | List nonExistingQuestionsIds = questionDao.findNonExistingQuestionsByQuestionsIds(questionsIds);
206 | if (!CollectionUtils.isEmpty(nonExistingQuestionsIds)) {
207 | throw new QuestionNotFoundException(nonExistingQuestionsIds);
208 | }
209 | }
210 |
211 | }
212 |
--------------------------------------------------------------------------------
/backend/src/main/java/com/github/joumenharzli/surveypoc/service/SubjectService.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 Joumen Harzli
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 | * in compliance with the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License
10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 | * or implied. See the License for the specific language governing permissions and limitations under
12 | * the License.
13 | *
14 | */
15 |
16 | package com.github.joumenharzli.surveypoc.service;
17 |
18 | import java.util.List;
19 |
20 | import com.github.joumenharzli.surveypoc.service.dto.SubjectDto;
21 |
22 | /**
23 | * Subject service
24 | *
25 | * @author Joumen Harzli
26 | */
27 | public interface SubjectService {
28 |
29 | /**
30 | * find all the subjects and their questions
31 | *
32 | * @return a list of the questions with subjects
33 | */
34 | List findAllSubjectsAndQuestions();
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/backend/src/main/java/com/github/joumenharzli/surveypoc/service/UserResponseService.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 Joumen Harzli
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 | * in compliance with the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License
10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 | * or implied. See the License for the specific language governing permissions and limitations under
12 | * the License.
13 | *
14 | */
15 |
16 | package com.github.joumenharzli.surveypoc.service;
17 |
18 | import java.util.List;
19 |
20 | import com.github.joumenharzli.surveypoc.exception.QuestionNotFoundException;
21 | import com.github.joumenharzli.surveypoc.exception.UserNotFoundException;
22 | import com.github.joumenharzli.surveypoc.service.dto.UserResponseForQuestionDto;
23 |
24 | /**
25 | * User Response Service
26 | *
27 | * @author Joumen Harzli
28 | */
29 | public interface UserResponseService {
30 |
31 | /**
32 | * Find the responses for the provided questions and user
33 | *
34 | * @param userId id of the user who responded
35 | * @param questionsIds ids of the questions that the user may responded
36 | * @return list of responses of the user
37 | * @throws UserNotFoundException if no user was found
38 | * @throws QuestionNotFoundException if no question was found
39 | * @throws IllegalArgumentException if any given argument is invalid
40 | */
41 | List findResponsesOfUserForQuestions(Long userId, List questionsIds);
42 |
43 | /**
44 | * Save the responses of the connected user for the provided questions
45 | *
46 | * @param userId id of the user who responded
47 | * @param userResponsesForQuestions questions ids and contents that the connected user entered
48 | * @return List of the saved responses of the user
49 | * @throws UserNotFoundException if no user was found
50 | * @throws QuestionNotFoundException if no question was found
51 | * @throws IllegalArgumentException if any given argument is invalid
52 | */
53 | List saveResponsesOfUserForQuestions(Long userId,
54 | List userResponsesForQuestions);
55 | }
56 |
--------------------------------------------------------------------------------
/backend/src/main/java/com/github/joumenharzli/surveypoc/service/dto/QuestionDto.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 Joumen Harzli
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 | * in compliance with the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License
10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 | * or implied. See the License for the specific language governing permissions and limitations under
12 | * the License.
13 | *
14 | */
15 |
16 | package com.github.joumenharzli.surveypoc.service.dto;
17 |
18 | import org.apache.commons.lang3.builder.EqualsBuilder;
19 | import org.apache.commons.lang3.builder.HashCodeBuilder;
20 | import org.apache.commons.lang3.builder.ToStringBuilder;
21 |
22 | /**
23 | * Question Dto
24 | *
25 | * @author Joumen Harzli
26 | */
27 | public class QuestionDto {
28 |
29 | private Long id;
30 | private String label;
31 |
32 | public Long getId() {
33 | return id;
34 | }
35 |
36 | public void setId(Long id) {
37 | this.id = id;
38 | }
39 |
40 | public String getLabel() {
41 | return label;
42 | }
43 |
44 | public void setLabel(String label) {
45 | this.label = label;
46 | }
47 |
48 | @Override
49 | public boolean equals(Object o) {
50 | if (this == o) {
51 | return true;
52 | }
53 |
54 | if (o == null || getClass() != o.getClass()) {
55 | return false;
56 | }
57 |
58 | QuestionDto that = (QuestionDto) o;
59 |
60 | return new EqualsBuilder()
61 | .append(id, that.id)
62 | .isEquals();
63 | }
64 |
65 | @Override
66 | public int hashCode() {
67 | return new HashCodeBuilder(17, 37)
68 | .append(id)
69 | .toHashCode();
70 | }
71 |
72 | @Override
73 | public String toString() {
74 | return new ToStringBuilder(this)
75 | .append("id", id)
76 | .append("label", label)
77 | .toString();
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/backend/src/main/java/com/github/joumenharzli/surveypoc/service/dto/SubjectDto.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 Joumen Harzli
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 | * in compliance with the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License
10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 | * or implied. See the License for the specific language governing permissions and limitations under
12 | * the License.
13 | *
14 | */
15 |
16 | package com.github.joumenharzli.surveypoc.service.dto;
17 |
18 | import java.util.ArrayList;
19 | import java.util.List;
20 |
21 | import org.apache.commons.lang3.builder.EqualsBuilder;
22 | import org.apache.commons.lang3.builder.HashCodeBuilder;
23 | import org.apache.commons.lang3.builder.ToStringBuilder;
24 | import org.springframework.util.Assert;
25 |
26 | /**
27 | * Subject Dto
28 | *
29 | * @author Joumen Harzli
30 | */
31 | public class SubjectDto {
32 | private Long id;
33 | private String label;
34 | private List questions;
35 |
36 | public Long getId() {
37 | return id;
38 | }
39 |
40 | public void setId(Long id) {
41 | this.id = id;
42 | }
43 |
44 | public String getLabel() {
45 | return label;
46 | }
47 |
48 | public void setLabel(String label) {
49 | this.label = label;
50 | }
51 |
52 | public void addQuestions(List questions) {
53 | Assert.notNull(questions, String.format("Cannot add null questions to subject %s", this.toString()));
54 | if (!questions.isEmpty()) {
55 | questions.forEach(this::addQuestion);
56 | }
57 | }
58 |
59 | public void addQuestion(QuestionDto question) {
60 | Assert.notNull(question, String.format("Cannot add null question to the list of questions in the subject %s",
61 | this.toString()));
62 |
63 | if (questions == null) {
64 | questions = new ArrayList<>();
65 | }
66 | questions.add(question);
67 | }
68 |
69 | public List getQuestions() {
70 | return questions;
71 | }
72 |
73 | public void setQuestions(List questions) {
74 | this.questions = questions;
75 | }
76 |
77 | @Override
78 | public boolean equals(Object o) {
79 | if (this == o) {
80 | return true;
81 | }
82 |
83 | if (o == null || getClass() != o.getClass()) {
84 | return false;
85 | }
86 |
87 | SubjectDto that = (SubjectDto) o;
88 |
89 | return new EqualsBuilder()
90 | .append(id, that.id)
91 | .isEquals();
92 | }
93 |
94 | @Override
95 | public int hashCode() {
96 | return new HashCodeBuilder(17, 37)
97 | .append(id)
98 | .toHashCode();
99 | }
100 |
101 | @Override
102 | public String toString() {
103 | return new ToStringBuilder(this)
104 | .append("id", id)
105 | .append("label", label)
106 | .append("questions", questions)
107 | .toString();
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/backend/src/main/java/com/github/joumenharzli/surveypoc/service/dto/UserResponseForQuestionDto.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 Joumen Harzli
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 | * in compliance with the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License
10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 | * or implied. See the License for the specific language governing permissions and limitations under
12 | * the License.
13 | *
14 | */
15 |
16 | package com.github.joumenharzli.surveypoc.service.dto;
17 |
18 | import javax.validation.constraints.NotNull;
19 |
20 | import org.apache.commons.lang3.builder.EqualsBuilder;
21 | import org.apache.commons.lang3.builder.HashCodeBuilder;
22 | import org.apache.commons.lang3.builder.ToStringBuilder;
23 | import org.hibernate.validator.constraints.NotBlank;
24 |
25 | /**
26 | * User Response For Question Dto
27 | *
28 | * @author Joumen Harzli
29 | */
30 | public class UserResponseForQuestionDto {
31 |
32 |
33 | @NotNull
34 | private Long questionId;
35 |
36 | @NotBlank
37 | private String content;
38 |
39 | public Long getQuestionId() {
40 | return questionId;
41 | }
42 |
43 | public void setQuestionId(Long questionId) {
44 | this.questionId = questionId;
45 | }
46 |
47 | public String getContent() {
48 | return content;
49 | }
50 |
51 | public void setContent(String content) {
52 | this.content = content;
53 | }
54 |
55 | @Override
56 | public boolean equals(Object o) {
57 | if (this == o) {
58 | return true;
59 | }
60 |
61 | if (o == null || getClass() != o.getClass()) {
62 | return false;
63 | }
64 |
65 | UserResponseForQuestionDto that = (UserResponseForQuestionDto) o;
66 |
67 | return new EqualsBuilder()
68 | .append(questionId, that.questionId)
69 | .isEquals();
70 | }
71 |
72 | @Override
73 | public int hashCode() {
74 | return new HashCodeBuilder(17, 37)
75 | .append(questionId)
76 | .toHashCode();
77 | }
78 |
79 | @Override
80 | public String toString() {
81 | return new ToStringBuilder(this)
82 | .append("questionId", questionId)
83 | .append("content", content)
84 | .toString();
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/backend/src/main/java/com/github/joumenharzli/surveypoc/service/dto/UserResponsesForQuestionListDto.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 Joumen Harzli
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 | * in compliance with the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License
10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 | * or implied. See the License for the specific language governing permissions and limitations under
12 | * the License.
13 | *
14 | */
15 |
16 | package com.github.joumenharzli.surveypoc.service.dto;
17 |
18 | import java.util.ArrayList;
19 | import java.util.List;
20 | import javax.validation.Valid;
21 |
22 | import org.apache.commons.lang3.builder.EqualsBuilder;
23 | import org.apache.commons.lang3.builder.HashCodeBuilder;
24 | import org.apache.commons.lang3.builder.ToStringBuilder;
25 | import org.hibernate.validator.constraints.NotEmpty;
26 |
27 | /**
28 | * User Responses List
29 | * A wrapper for a list of user response for question dto
30 | * This useful to apply {@code Bean validations}
31 | *
32 | * @author Joumen Harzli
33 | */
34 | public class UserResponsesForQuestionListDto {
35 |
36 | @Valid
37 | @NotEmpty
38 | private List responses;
39 |
40 | public UserResponsesForQuestionListDto() {
41 | this.responses = new ArrayList<>();
42 | }
43 |
44 | public List getResponses() {
45 | return responses;
46 | }
47 |
48 | public void setResponses(List responses) {
49 | this.responses = responses;
50 | }
51 |
52 | @Override
53 | public boolean equals(Object o) {
54 | if (this == o) {
55 | return true;
56 | }
57 |
58 | if (o == null || getClass() != o.getClass()) {
59 | return false;
60 | }
61 |
62 | UserResponsesForQuestionListDto that = (UserResponsesForQuestionListDto) o;
63 |
64 | return new EqualsBuilder()
65 | .append(responses, that.responses)
66 | .isEquals();
67 | }
68 |
69 | @Override
70 | public int hashCode() {
71 | return new HashCodeBuilder(17, 37)
72 | .append(responses)
73 | .toHashCode();
74 | }
75 |
76 | @Override
77 | public String toString() {
78 | return new ToStringBuilder(this)
79 | .append("responses", responses)
80 | .toString();
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/backend/src/main/java/com/github/joumenharzli/surveypoc/service/mapper/QuestionMapper.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 Joumen Harzli
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 | * in compliance with the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License
10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 | * or implied. See the License for the specific language governing permissions and limitations under
12 | * the License.
13 | *
14 | */
15 |
16 | package com.github.joumenharzli.surveypoc.service.mapper;
17 |
18 | import java.util.List;
19 |
20 | import org.mapstruct.Mapper;
21 | import org.mapstruct.Mapping;
22 | import org.springframework.stereotype.Service;
23 |
24 | import com.github.joumenharzli.surveypoc.domain.Question;
25 | import com.github.joumenharzli.surveypoc.service.dto.QuestionDto;
26 |
27 | /**
28 | * Mapper for {@link Question} and {@link QuestionDto}
29 | *
30 | * @author Joumen Harzli
31 | */
32 | @Mapper(componentModel = "spring")
33 | @Service
34 | public interface QuestionMapper {
35 |
36 | QuestionDto questionToQuestionDto(Question question);
37 |
38 | @Mapping(source = "questionId", target = "id")
39 | Question questionIdToQuestion(Long questionId);
40 |
41 | List questionsIdsListToQuestionList(List questionsIds);
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/backend/src/main/java/com/github/joumenharzli/surveypoc/service/mapper/SubjectMapper.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 Joumen Harzli
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 | * in compliance with the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License
10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 | * or implied. See the License for the specific language governing permissions and limitations under
12 | * the License.
13 | *
14 | */
15 |
16 | package com.github.joumenharzli.surveypoc.service.mapper;
17 |
18 | import java.util.List;
19 |
20 | import org.mapstruct.DecoratedWith;
21 | import org.mapstruct.Mapper;
22 | import org.springframework.stereotype.Service;
23 |
24 | import com.github.joumenharzli.surveypoc.domain.Question;
25 | import com.github.joumenharzli.surveypoc.domain.Subject;
26 | import com.github.joumenharzli.surveypoc.service.dto.SubjectDto;
27 |
28 | /**
29 | * Mapper for {@link Subject} and {@link SubjectDto}
30 | *
31 | * @author Joumen Harzli
32 | */
33 | @Mapper(componentModel = "spring")
34 | @DecoratedWith(SubjectMapperDecorator.class)
35 | @Service
36 | public interface SubjectMapper {
37 |
38 | List questionsToSubjectsDto(List questions);
39 |
40 | SubjectDto toDto(Subject subject);
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/backend/src/main/java/com/github/joumenharzli/surveypoc/service/mapper/SubjectMapperDecorator.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 Joumen Harzli
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 | * in compliance with the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License
10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 | * or implied. See the License for the specific language governing permissions and limitations under
12 | * the License.
13 | *
14 | */
15 |
16 | package com.github.joumenharzli.surveypoc.service.mapper;
17 |
18 | import java.util.List;
19 | import java.util.stream.Collectors;
20 |
21 | import org.springframework.beans.factory.annotation.Autowired;
22 | import org.springframework.beans.factory.annotation.Qualifier;
23 | import org.springframework.util.Assert;
24 |
25 | import com.github.joumenharzli.surveypoc.domain.Question;
26 | import com.github.joumenharzli.surveypoc.domain.Subject;
27 | import com.github.joumenharzli.surveypoc.service.dto.QuestionDto;
28 | import com.github.joumenharzli.surveypoc.service.dto.SubjectDto;
29 | import com.google.common.collect.Multimap;
30 | import com.google.common.collect.Multimaps;
31 |
32 | /**
33 | * A decorator for the mapper for {@link Subject} and {@link SubjectDto}
34 | * for custom methods
35 | *
36 | * @author Joumen Harzli
37 | */
38 | public abstract class SubjectMapperDecorator implements SubjectMapper {
39 |
40 | @Autowired
41 | @Qualifier("delegate")
42 | private SubjectMapper delegate;
43 |
44 | @Autowired
45 | private QuestionMapper questionMapper;
46 |
47 | @Override
48 | public List questionsToSubjectsDto(List questions) {
49 | Assert.notNull(questions, "Cannot map a null list of questions to a list of subject dtos");
50 |
51 | Multimap multimap = Multimaps.index(questions, Question::getSubject);
52 |
53 | return multimap.keySet().stream().map(subject -> {
54 |
55 | SubjectDto subjectDto = delegate.toDto(subject);
56 |
57 | List questionsDtos = multimap.get(subject).stream()
58 | .map(question -> questionMapper.questionToQuestionDto(question)).collect(Collectors.toList());
59 |
60 | subjectDto.addQuestions(questionsDtos);
61 | return subjectDto;
62 |
63 | }).collect(Collectors.toList());
64 | }
65 |
66 | }
67 |
--------------------------------------------------------------------------------
/backend/src/main/java/com/github/joumenharzli/surveypoc/service/mapper/UserMapper.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 Joumen Harzli
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 | * in compliance with the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License
10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 | * or implied. See the License for the specific language governing permissions and limitations under
12 | * the License.
13 | *
14 | */
15 |
16 | package com.github.joumenharzli.surveypoc.service.mapper;
17 |
18 | import org.mapstruct.Mapper;
19 | import org.springframework.stereotype.Service;
20 |
21 | import com.github.joumenharzli.surveypoc.domain.User;
22 |
23 | /**
24 | * Mapper for {@link User}
25 | *
26 | * @author Joumen Harzli
27 | */
28 | @Mapper(componentModel = "spring")
29 | @Service
30 | public interface UserMapper {
31 |
32 | User toEntityFromId(Long id);
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/backend/src/main/java/com/github/joumenharzli/surveypoc/service/mapper/UserResponseMapper.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 Joumen Harzli
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 | * in compliance with the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License
10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 | * or implied. See the License for the specific language governing permissions and limitations under
12 | * the License.
13 | *
14 | */
15 |
16 | package com.github.joumenharzli.surveypoc.service.mapper;
17 |
18 | import java.util.List;
19 | import java.util.stream.Collectors;
20 |
21 | import org.mapstruct.Mapper;
22 | import org.mapstruct.Mapping;
23 | import org.springframework.stereotype.Service;
24 |
25 | import com.github.joumenharzli.surveypoc.domain.User;
26 | import com.github.joumenharzli.surveypoc.domain.UserResponse;
27 | import com.github.joumenharzli.surveypoc.service.dto.UserResponseForQuestionDto;
28 |
29 | /**
30 | * Mapper for {@link UserResponse} and {@link UserResponseForQuestionDto}
31 | *
32 | * @author Joumen Harzli
33 | */
34 | @Mapper(componentModel = "spring")
35 | @Service
36 | public interface UserResponseMapper {
37 |
38 | @Mapping(source = "question.id", target = "questionId")
39 | UserResponseForQuestionDto userResponseToUserResponseForQuestionDto(UserResponse entity);
40 |
41 | List userResponseListToUserResponseForQuestionDtoList(List entity);
42 |
43 |
44 | @Mapping(source = "userResponseForQuestion.questionId", target = "question.id")
45 | UserResponse userResponseForQuestionDtoToUserResponse(UserResponseForQuestionDto userResponseForQuestion);
46 |
47 | default List userResponsesForQuestionsDtoToUserResponsesList(List userResponseForQuestions,
48 | User user) {
49 |
50 | return userResponseForQuestions.stream()
51 | .map(
52 | userResponseForQuestionDto -> {
53 | UserResponse userResponse = userResponseForQuestionDtoToUserResponse(userResponseForQuestionDto);
54 | userResponse.setUser(user);
55 | return userResponse;
56 | })
57 | .collect(Collectors.toList());
58 | }
59 |
60 | default List userResponsesForQuestionsToQuestionsIdsList(List userResponsesForQuestions) {
61 | return userResponsesForQuestions.stream()
62 | .map(UserResponseForQuestionDto::getQuestionId)
63 | .collect(Collectors.toList());
64 | }
65 |
66 | }
67 |
--------------------------------------------------------------------------------
/backend/src/main/java/com/github/joumenharzli/surveypoc/web/QuestionResponseResource.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 Joumen Harzli
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 | * in compliance with the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License
10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 | * or implied. See the License for the specific language governing permissions and limitations under
12 | * the License.
13 | *
14 | */
15 |
16 | package com.github.joumenharzli.surveypoc.web;
17 |
18 | import java.util.List;
19 | import javax.validation.Valid;
20 |
21 | import org.slf4j.Logger;
22 | import org.slf4j.LoggerFactory;
23 | import org.springframework.web.bind.annotation.*;
24 |
25 | import com.codahale.metrics.annotation.Timed;
26 | import com.github.joumenharzli.surveypoc.domain.Question;
27 | import com.github.joumenharzli.surveypoc.domain.UserResponse;
28 | import com.github.joumenharzli.surveypoc.service.UserResponseService;
29 | import com.github.joumenharzli.surveypoc.service.dto.UserResponseForQuestionDto;
30 | import com.github.joumenharzli.surveypoc.service.dto.UserResponsesForQuestionListDto;
31 | import com.github.joumenharzli.surveypoc.web.error.RestErrorDto;
32 |
33 | import static com.github.joumenharzli.surveypoc.web.util.RestUtils.commaDelimitedListToLongList;
34 | import io.swagger.annotations.ApiOperation;
35 | import io.swagger.annotations.ApiParam;
36 | import io.swagger.annotations.ApiResponse;
37 | import io.swagger.annotations.ApiResponses;
38 |
39 | /**
40 | * Rest Resource for the entities {@link UserResponse} and {@link Question}
41 | *
42 | * @author Joumen Harzli
43 | */
44 | @RestController
45 | @RequestMapping("/api/v1/questions")
46 | public class QuestionResponseResource {
47 |
48 | /* because this example is not using Spring Security yet
49 | * the user id is simply hardcoded */
50 | private static final Long USER_ID = 1L;
51 |
52 | private static final Logger LOGGER = LoggerFactory.getLogger(QuestionResponseResource.class);
53 |
54 | private final UserResponseService userResponseService;
55 |
56 | public QuestionResponseResource(UserResponseService userResponseService) {
57 | this.userResponseService = userResponseService;
58 | }
59 |
60 | /**
61 | * GET /:questionsId/responses/me : Get the responses of the connected user for the provided questions
62 | *
63 | * @param questionsId a comma separated ids of the questions that the user may responded
64 | * @return the ResponseEntity with status 200 (OK) and list of responses of the user
65 | * and the ResponseEntity with status 500 if the request body is invalid
66 | */
67 | @ApiOperation(notes = "Returns all the found responses of the connected user for the provided questions.",
68 | value = "Get all responses of the connected user for the questions",
69 | nickname = "getResponsesOfConnectUserForQuestions")
70 | @ApiResponses({
71 | @ApiResponse(code = 404, message = "Question or user not found", response = RestErrorDto.class),
72 | })
73 | @Timed
74 | @GetMapping("/{questionsId}/responses/me")
75 | public List getResponsesOfConnectUserForQuestions(
76 | @ApiParam(value = "A comma separated ids of the questions that the user may responded example: 1, 2, 3",
77 | required = true)
78 | @PathVariable("questionsId") String questionsId) {
79 |
80 | LOGGER.debug("REST request to get the responses of the connected user for the questions with ids {}", questionsId);
81 | return userResponseService.findResponsesOfUserForQuestions(USER_ID, commaDelimitedListToLongList(questionsId));
82 | }
83 |
84 | /**
85 | * POST /responses/me : Save the responses of the connected user for the provided questions
86 | *
87 | * @param userResponseForQuestions questions ids and contents that the connected user entered
88 | * @return the ResponseEntity with status 200 (OK) and list of the saved responses of the user
89 | * and the ResponseEntity with status 500 if the request body is invalid
90 | */
91 | @ApiOperation(notes = "Add and update the responses of the connected user for the provided questions then " +
92 | "returns all the list of the saved responses of the user.",
93 | value = "Save the responses of the connected user for the provided questions",
94 | nickname = "getResponsesOfConnectUserForQuestions")
95 | @ApiResponses({
96 | @ApiResponse(code = 404, message = "Question or user not found", response = RestErrorDto.class),
97 | })
98 | @Timed
99 | @PostMapping("/responses/me")
100 | public List saveResponsesOfConnectUserForQuestions(@Valid @RequestBody
101 | UserResponsesForQuestionListDto userResponseForQuestions) {
102 | LOGGER.debug("REST request to save the responses of the connected user for the questions {}", userResponseForQuestions);
103 | return userResponseService.saveResponsesOfUserForQuestions(USER_ID, userResponseForQuestions.getResponses());
104 | }
105 |
106 | }
107 |
--------------------------------------------------------------------------------
/backend/src/main/java/com/github/joumenharzli/surveypoc/web/SubjectResource.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 Joumen Harzli
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 | * in compliance with the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License
10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 | * or implied. See the License for the specific language governing permissions and limitations under
12 | * the License.
13 | *
14 | */
15 |
16 | package com.github.joumenharzli.surveypoc.web;
17 |
18 | import java.util.List;
19 |
20 | import org.slf4j.Logger;
21 | import org.slf4j.LoggerFactory;
22 | import org.springframework.web.bind.annotation.GetMapping;
23 | import org.springframework.web.bind.annotation.RequestMapping;
24 | import org.springframework.web.bind.annotation.RestController;
25 |
26 | import com.codahale.metrics.annotation.Timed;
27 | import com.github.joumenharzli.surveypoc.service.SubjectService;
28 | import com.github.joumenharzli.surveypoc.service.dto.SubjectDto;
29 | import com.github.joumenharzli.surveypoc.web.error.RestFieldsErrorsDto;
30 |
31 | import io.swagger.annotations.ApiOperation;
32 | import io.swagger.annotations.ApiResponse;
33 | import io.swagger.annotations.ApiResponses;
34 |
35 | /**
36 | * Rest resource for subject entity
37 | *
38 | * @author Joumen Harzli
39 | */
40 | @RestController
41 | @RequestMapping("/api/v1/subjects")
42 | public class SubjectResource {
43 |
44 | private static final Logger LOGGER = LoggerFactory.getLogger(SubjectResource.class);
45 |
46 | private final SubjectService subjectService;
47 |
48 | public SubjectResource(SubjectService subjectService) {
49 | this.subjectService = subjectService;
50 | }
51 |
52 | /**
53 | * GET /subjects : get all the find all the subjects and their questions.
54 | *
55 | * @return the ResponseEntity with status 200 (OK) and the list the subjects and their questions
56 | */
57 | @ApiOperation(notes = "Returns all the found subjects and their questions.",
58 | value = "Get all subjects and questions",
59 | nickname = "findAllSubjectsAndQuestions")
60 | @ApiResponses({
61 | /* We need to inject {@link RestFieldsErrorsDto} at least one so springfox can added it globally */
62 | @ApiResponse(code = 400, message = "Request content is invalid", response = RestFieldsErrorsDto.class)
63 | })
64 | @Timed
65 | @GetMapping
66 | public List findAllSubjectsAndQuestions() {
67 | LOGGER.debug("REST request to get all the subjects and the questions");
68 |
69 | return subjectService.findAllSubjectsAndQuestions();
70 | }
71 |
72 | }
73 |
--------------------------------------------------------------------------------
/backend/src/main/java/com/github/joumenharzli/surveypoc/web/error/RestErrorConstants.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 Joumen Harzli
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 | * in compliance with the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License
10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 | * or implied. See the License for the specific language governing permissions and limitations under
12 | * the License.
13 | *
14 | */
15 |
16 | package com.github.joumenharzli.surveypoc.web.error;
17 |
18 | /**
19 | * Constants for rest error
20 | *
21 | * @author Joumen Harzli
22 | */
23 | public final class RestErrorConstants {
24 |
25 | public static final String ERR_INTERNAL_SERVER_ERROR = "error.internal";
26 | public static final String ERR_VALIDATION_ERROR = "error.validation";
27 | public static final String ERR_QUESTIONS_NOT_FOUND_ERROR = "error.questionNotFound";
28 | public static final String ERR_USERS_NOT_FOUND_ERROR = "error.userNotFound";
29 |
30 | private RestErrorConstants() {
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/backend/src/main/java/com/github/joumenharzli/surveypoc/web/error/RestErrorDto.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 Joumen Harzli
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 | * in compliance with the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License
10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 | * or implied. See the License for the specific language governing permissions and limitations under
12 | * the License.
13 | *
14 | */
15 |
16 | package com.github.joumenharzli.surveypoc.web.error;
17 |
18 | import java.io.Serializable;
19 |
20 | import org.apache.commons.lang3.builder.EqualsBuilder;
21 | import org.apache.commons.lang3.builder.HashCodeBuilder;
22 | import org.apache.commons.lang3.builder.ToStringBuilder;
23 |
24 | import io.swagger.annotations.ApiModel;
25 |
26 | /**
27 | * A representation for the rest error
28 | *
29 | * @author Joumen Harzli
30 | */
31 | @ApiModel("RestErrorDto")
32 | public class RestErrorDto implements Serializable {
33 | private String code;
34 | private String message;
35 |
36 | public RestErrorDto(String code, String message) {
37 | this.code = code;
38 | this.message = message;
39 | }
40 |
41 | public String getCode() {
42 | return code;
43 | }
44 |
45 | public String getMessage() {
46 | return message;
47 | }
48 |
49 | @Override
50 | public boolean equals(Object o) {
51 | if (this == o) {
52 | return true;
53 | }
54 |
55 | if (o == null || getClass() != o.getClass()) {
56 | return false;
57 | }
58 |
59 | RestErrorDto that = (RestErrorDto) o;
60 |
61 | return new EqualsBuilder()
62 | .append(code, that.code)
63 | .append(message, that.message)
64 | .isEquals();
65 | }
66 |
67 | @Override
68 | public int hashCode() {
69 | return new HashCodeBuilder(17, 37)
70 | .append(code)
71 | .toHashCode();
72 | }
73 |
74 | @Override
75 | public String toString() {
76 | ToStringBuilder builder = new ToStringBuilder(this)
77 | .append("code", code)
78 | .append("message", message);
79 |
80 | return builder.toString();
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/backend/src/main/java/com/github/joumenharzli/surveypoc/web/error/RestExceptionTranslator.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 Joumen Harzli
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 | * in compliance with the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License
10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 | * or implied. See the License for the specific language governing permissions and limitations under
12 | * the License.
13 | *
14 | */
15 |
16 | package com.github.joumenharzli.surveypoc.web.error;
17 |
18 | import java.util.List;
19 |
20 | import org.springframework.context.MessageSource;
21 | import org.springframework.context.i18n.LocaleContextHolder;
22 | import org.springframework.http.HttpStatus;
23 | import org.springframework.validation.BindingResult;
24 | import org.springframework.validation.FieldError;
25 | import org.springframework.web.bind.MethodArgumentNotValidException;
26 | import org.springframework.web.bind.annotation.ControllerAdvice;
27 | import org.springframework.web.bind.annotation.ExceptionHandler;
28 | import org.springframework.web.bind.annotation.ResponseBody;
29 | import org.springframework.web.bind.annotation.ResponseStatus;
30 |
31 | import com.github.joumenharzli.surveypoc.exception.QuestionNotFoundException;
32 | import com.github.joumenharzli.surveypoc.exception.UserNotFoundException;
33 |
34 | /**
35 | * Controller advice to translate the server side exceptions to client-friendly json structures.
36 | *
37 | * @author Joumen Harzli
38 | */
39 | @ControllerAdvice
40 | public class RestExceptionTranslator {
41 |
42 | private final MessageSource messageSource;
43 |
44 | public RestExceptionTranslator(MessageSource messageSource) {
45 | this.messageSource = messageSource;
46 | }
47 |
48 | /**
49 | * Handle Question Not Found
50 | *
51 | * @return 404 status with message telling that the question not found
52 | */
53 | @ResponseStatus(value = HttpStatus.NOT_FOUND)
54 | @ExceptionHandler(value = QuestionNotFoundException.class)
55 | @ResponseBody
56 | public RestErrorDto handleQuestionNotFound(QuestionNotFoundException exception) {
57 | String errorCode = RestErrorConstants.ERR_QUESTIONS_NOT_FOUND_ERROR;
58 | return new RestErrorDto(errorCode, getLocalizedMessageFromErrorCode(errorCode, new Object[]{exception.getNotFoundQuestionsIds()}));
59 | }
60 |
61 | /**
62 | * Handle User Not Found
63 | *
64 | * @return 404 status with message telling that the user not found
65 | */
66 | @ResponseStatus(value = HttpStatus.NOT_FOUND)
67 | @ExceptionHandler(value = UserNotFoundException.class)
68 | @ResponseBody
69 | public RestErrorDto handleUserNotFound(UserNotFoundException exception) {
70 | String errorCode = RestErrorConstants.ERR_USERS_NOT_FOUND_ERROR;
71 | return new RestErrorDto(errorCode, getLocalizedMessageFromErrorCode(errorCode, new Object[]{exception.getNotFoundUsersIds()}));
72 | }
73 |
74 | /**
75 | * Handle validation errors
76 | *
77 | * @return validation error with the fields errors and a bad request
78 | */
79 | @ResponseStatus(value = HttpStatus.BAD_REQUEST)
80 | @ExceptionHandler(value = MethodArgumentNotValidException.class)
81 | @ResponseBody
82 | public RestFieldsErrorsDto handleValidationExceptions(MethodArgumentNotValidException exception) {
83 | BindingResult result = exception.getBindingResult();
84 |
85 | String errorCode = RestErrorConstants.ERR_VALIDATION_ERROR;
86 |
87 | RestFieldsErrorsDto restFieldsErrors = new RestFieldsErrorsDto(errorCode, getLocalizedMessageFromErrorCode(errorCode));
88 |
89 | List fieldErrors = result.getFieldErrors();
90 | fieldErrors.forEach(fieldError ->
91 | restFieldsErrors.addError(new RestFieldErrorDto(fieldError.getField(), fieldError.getCode(),
92 | getLocalizedMessageFromFieldError(fieldError))));
93 |
94 | return restFieldsErrors;
95 |
96 | }
97 |
98 | /**
99 | * Handle all types of errors
100 | *
101 | * @return internal server error
102 | */
103 | @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
104 | @ExceptionHandler(value = Exception.class)
105 | @ResponseBody
106 | public RestErrorDto handleAllExceptions() {
107 | String errorCode = RestErrorConstants.ERR_INTERNAL_SERVER_ERROR;
108 | return new RestErrorDto(errorCode, getLocalizedMessageFromErrorCode(errorCode));
109 | }
110 |
111 | /**
112 | * Get the correspondent localized message for a field error
113 | *
114 | * @param fieldError error that will be used for search
115 | * @return the localized message if found or the default one
116 | */
117 | private String getLocalizedMessageFromFieldError(FieldError fieldError) {
118 | return messageSource.getMessage(fieldError, LocaleContextHolder.getLocale());
119 | }
120 |
121 | /**
122 | * Get the correspondent localized message for an error code
123 | *
124 | * @param errorCode error that will be used for search
125 | * @return the localized message if found
126 | */
127 | private String getLocalizedMessageFromErrorCode(String errorCode) {
128 | return getLocalizedMessageFromErrorCode(errorCode, new Object[]{});
129 | }
130 |
131 | /**
132 | * Get the correspondent localized message for an error code
133 | *
134 | * @param errorCode error that will be used for search
135 | * @param arguments parameters that will be used when parsing the message
136 | * @return the localized message if found
137 | */
138 | private String getLocalizedMessageFromErrorCode(String errorCode, Object[] arguments) {
139 | return messageSource.getMessage(errorCode, arguments, LocaleContextHolder.getLocale());
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/backend/src/main/java/com/github/joumenharzli/surveypoc/web/error/RestFieldErrorDto.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 Joumen Harzli
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 | * in compliance with the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License
10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 | * or implied. See the License for the specific language governing permissions and limitations under
12 | * the License.
13 | *
14 | */
15 |
16 | package com.github.joumenharzli.surveypoc.web.error;
17 |
18 | import java.io.Serializable;
19 |
20 | import org.apache.commons.lang3.builder.EqualsBuilder;
21 | import org.apache.commons.lang3.builder.HashCodeBuilder;
22 | import org.apache.commons.lang3.builder.ToStringBuilder;
23 |
24 | /**
25 | * A representation for the rest field error
26 | *
27 | * @author Joumen Harzli
28 | */
29 | public class RestFieldErrorDto implements Serializable {
30 |
31 | private String field;
32 | private String code;
33 | private String message;
34 |
35 | public RestFieldErrorDto(String field, String code, String message) {
36 | this.field = field;
37 | this.code = code;
38 | this.message = message;
39 | }
40 |
41 | public String getField() {
42 | return field;
43 | }
44 |
45 | public void setField(String field) {
46 | this.field = field;
47 | }
48 |
49 | public String getCode() {
50 | return code;
51 | }
52 |
53 | public void setCode(String code) {
54 | this.code = code;
55 | }
56 |
57 | public String getMessage() {
58 | return message;
59 | }
60 |
61 | public void setMessage(String message) {
62 | this.message = message;
63 | }
64 |
65 | @Override
66 | public boolean equals(Object o) {
67 | if (this == o) {
68 | return true;
69 | }
70 |
71 | if (o == null || getClass() != o.getClass()) {
72 | return false;
73 | }
74 |
75 | RestFieldErrorDto that = (RestFieldErrorDto) o;
76 |
77 | return new EqualsBuilder()
78 | .append(field, that.field)
79 | .append(code, that.code)
80 | .append(message, that.message)
81 | .isEquals();
82 | }
83 |
84 | @Override
85 | public int hashCode() {
86 | return new HashCodeBuilder(17, 37)
87 | .append(field)
88 | .append(code)
89 | .append(message)
90 | .toHashCode();
91 | }
92 |
93 | @Override
94 | public String toString() {
95 | return new ToStringBuilder(this)
96 | .append("field", field)
97 | .append("code", code)
98 | .append("message", message)
99 | .toString();
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/backend/src/main/java/com/github/joumenharzli/surveypoc/web/error/RestFieldsErrorsDto.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 Joumen Harzli
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 | * in compliance with the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License
10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 | * or implied. See the License for the specific language governing permissions and limitations under
12 | * the License.
13 | *
14 | */
15 |
16 | package com.github.joumenharzli.surveypoc.web.error;
17 |
18 | import java.io.Serializable;
19 | import java.util.ArrayList;
20 | import java.util.List;
21 |
22 | import org.apache.commons.lang3.builder.EqualsBuilder;
23 | import org.apache.commons.lang3.builder.HashCodeBuilder;
24 | import org.apache.commons.lang3.builder.ToStringBuilder;
25 | import org.springframework.util.Assert;
26 |
27 | import io.swagger.annotations.ApiModel;
28 |
29 | /**
30 | * A representation for the rest fields errors list
31 | *
32 | * @author Joumen Harzli
33 | */
34 | @ApiModel("RestFieldsErrorsDto")
35 | public class RestFieldsErrorsDto extends RestErrorDto implements Serializable {
36 |
37 | private List fieldsErrors;
38 |
39 | /**
40 | * Constructor for the rest fields errors entity
41 | */
42 | public RestFieldsErrorsDto(String code, String message) {
43 | super(code, message);
44 | fieldsErrors = new ArrayList<>();
45 | }
46 |
47 | /**
48 | * Add a rest field error to the list of fields errors
49 | *
50 | * @param error error to add to the list
51 | */
52 | public void addError(RestFieldErrorDto error) {
53 | Assert.notNull(error, "Cannot add a null error to the list of fields errors");
54 |
55 | if (fieldsErrors == null) {
56 | fieldsErrors = new ArrayList<>();
57 | }
58 |
59 | fieldsErrors.add(error);
60 | }
61 |
62 | /**
63 | * Get the list of the fields errors
64 | *
65 | * @return list of fields errors
66 | */
67 | public List getFieldsErrors() {
68 | return fieldsErrors;
69 | }
70 |
71 | @Override
72 | public boolean equals(Object o) {
73 | if (this == o) {
74 | return true;
75 | }
76 |
77 | if (o == null || getClass() != o.getClass()) {
78 | return false;
79 | }
80 |
81 | RestFieldsErrorsDto that = (RestFieldsErrorsDto) o;
82 |
83 | return new EqualsBuilder()
84 | .appendSuper(super.equals(o))
85 | .append(fieldsErrors, that.fieldsErrors)
86 | .isEquals();
87 | }
88 |
89 | @Override
90 | public int hashCode() {
91 | return new HashCodeBuilder(17, 37)
92 | .appendSuper(super.hashCode())
93 | .append(fieldsErrors)
94 | .toHashCode();
95 | }
96 |
97 | @Override
98 | public String toString() {
99 | return new ToStringBuilder(this)
100 | .append("fieldsErrors", fieldsErrors)
101 | .toString();
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/backend/src/main/java/com/github/joumenharzli/surveypoc/web/util/RestUtils.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 Joumen Harzli
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 | * in compliance with the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License
10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 | * or implied. See the License for the specific language governing permissions and limitations under
12 | * the License.
13 | *
14 | */
15 |
16 | package com.github.joumenharzli.surveypoc.web.util;
17 |
18 | import java.util.List;
19 | import java.util.stream.Collectors;
20 |
21 | import org.springframework.util.StringUtils;
22 |
23 | /**
24 | * Utils for Rest Controllers
25 | *
26 | * @author Joumen Harzli
27 | */
28 | public final class RestUtils {
29 |
30 | private RestUtils() {
31 | }
32 |
33 | /**
34 | * Convert a comma delimited list into a list of {@code Long} values.
35 | *
36 | * @param value comma separated text
37 | * @return list of parsed elements
38 | */
39 | public static List commaDelimitedListToLongList(String value) {
40 | return StringUtils.commaDelimitedListToSet(value)
41 | .stream()
42 | .map(Long::parseLong)
43 | .collect(Collectors.toList());
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/backend/src/main/resources/application.yml:
--------------------------------------------------------------------------------
1 | spring:
2 | datasource:
3 | type: com.zaxxer.hikari.HikariDataSource
4 | url: jdbc:h2:mem:surveypoc;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
5 | hikari:
6 | data-source-properties:
7 | cachePrepStmts: true
8 | prepStmtCacheSize: 250
9 | prepStmtCacheSqlLimit: 2048
10 | useServerPrepStmts: true
11 |
12 | management:
13 | security:
14 | enabled: false
15 |
--------------------------------------------------------------------------------
/backend/src/main/resources/data.sql:
--------------------------------------------------------------------------------
1 | INSERT INTO subjects (id, label) VALUES (1, 'Personal information');
2 | INSERT INTO subjects (id, label) VALUES (2, 'Appreciations');
3 |
4 | INSERT INTO questions (id, label, subject_id) VALUES (1, 'What''s your name ?', 1);
5 | INSERT INTO questions (id, label, subject_id) VALUES (2, 'How old are you ?', 1);
6 | INSERT INTO questions (id, label, subject_id) VALUES (3, 'Do you like this example ?', 2);
7 | INSERT INTO questions (id, label, subject_id) VALUES (4, 'Any suggestions ?', 2);
8 |
9 | INSERT INTO users (id, name) VALUES (1, 'demo');
10 |
11 | INSERT INTO user_responses (content, question_id, user_id) VALUES ('Joe', 1, 1);
12 | INSERT INTO user_responses (content, question_id, user_id) VALUES ('25', 2, 1);
13 |
--------------------------------------------------------------------------------
/backend/src/main/resources/i18n/messages_en.properties:
--------------------------------------------------------------------------------
1 | #
2 | # Copyright (C) 2018 Joumen Harzli
3 | #
4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 | # in compliance with the License. You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software distributed under the License
10 | # is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 | # or implied. See the License for the specific language governing permissions and limitations under
12 | # the License.
13 | #
14 | #
15 | error.internal=Something unexpected went wrong
16 | error.validation=Request content is invalid
17 | error.questionNotFound=The questions with ids {0} was not found
18 | error.userNotFound=The users with ids {0} was not found
19 |
--------------------------------------------------------------------------------
/backend/src/main/resources/i18n/messages_fr.properties:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MuhamedHabib/survey-example-spring-boot-angular/376888b31391948267c541143677d32ad9341076/backend/src/main/resources/i18n/messages_fr.properties
--------------------------------------------------------------------------------
/backend/src/main/resources/logback-spring.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 | true
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
--------------------------------------------------------------------------------
/backend/src/main/resources/schema.sql:
--------------------------------------------------------------------------------
1 | DROP TABLE IF EXISTS subjects;
2 | CREATE TABLE subjects (
3 | id INT8 NOT NULL AUTO_INCREMENT,
4 | label VARCHAR(100) NOT NULL,
5 | CONSTRAINT pk_subjects PRIMARY KEY (id)
6 | );
7 |
8 | DROP TABLE IF EXISTS questions;
9 | CREATE TABLE questions (
10 | id INT8 NOT NULL AUTO_INCREMENT,
11 | label VARCHAR(100) NOT NULL,
12 | subject_id INT8 NOT NULL,
13 | CONSTRAINT pk_questions PRIMARY KEY (id),
14 | CONSTRAINT fk_subjects_questions FOREIGN KEY (subject_id) REFERENCES subjects (id)
15 | );
16 |
17 | DROP TABLE IF EXISTS users;
18 | CREATE TABLE users (
19 | id INT8 NOT NULL AUTO_INCREMENT,
20 | name VARCHAR(100) NOT NULL,
21 | CONSTRAINT pk_users PRIMARY KEY (id)
22 | );
23 |
24 | DROP TABLE IF EXISTS user_responses;
25 | CREATE TABLE user_responses (
26 | content VARCHAR(100) NOT NULL,
27 | question_id INT8 NOT NULL,
28 | user_id INT8 NOT NULL,
29 | CONSTRAINT pk_user_responses PRIMARY KEY (question_id, user_id),
30 | CONSTRAINT fk_questions_user_responses FOREIGN KEY (question_id) REFERENCES questions (id),
31 | CONSTRAINT fk_users_user_responses FOREIGN KEY (user_id) REFERENCES users (id)
32 | );
33 |
34 |
--------------------------------------------------------------------------------
/backend/src/test/java/com/github/joumenharzli/surveypoc/SurveyPocApplicationTests.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 Joumen Harzli
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 | * in compliance with the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License
10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 | * or implied. See the License for the specific language governing permissions and limitations under
12 | * the License.
13 | *
14 | */
15 |
16 | package com.github.joumenharzli.surveypoc;
17 |
18 | import org.junit.Test;
19 | import org.junit.runner.RunWith;
20 | import org.springframework.boot.test.context.SpringBootTest;
21 | import org.springframework.test.context.junit4.SpringRunner;
22 |
23 | @RunWith(SpringRunner.class)
24 | @SpringBootTest
25 | public class SurveyPocApplicationTests {
26 |
27 | @Test
28 | public void contextLoads() {
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/backend/src/test/java/com/github/joumenharzli/surveypoc/repository/dao/QuestionDaoTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 Joumen Harzli
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 | * in compliance with the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License
10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 | * or implied. See the License for the specific language governing permissions and limitations under
12 | * the License.
13 | *
14 | */
15 |
16 | package com.github.joumenharzli.surveypoc.repository.dao;
17 |
18 | import java.util.List;
19 |
20 | import org.junit.Assert;
21 | import org.junit.Test;
22 | import org.junit.runner.RunWith;
23 | import org.springframework.beans.factory.annotation.Autowired;
24 | import org.springframework.boot.test.context.SpringBootTest;
25 | import org.springframework.test.context.junit4.SpringRunner;
26 |
27 | import com.github.joumenharzli.surveypoc.domain.Question;
28 |
29 | /**
30 | * QuestionDaoTest
31 | *
32 | * @author Joumen Harzli
33 | */
34 | @RunWith(SpringRunner.class)
35 | @SpringBootTest
36 | public class QuestionDaoTest {
37 |
38 | @Autowired
39 | private QuestionDao questionDao;
40 |
41 | @Test
42 | public void findAllSubjectsAndQuestions() throws Exception {
43 | List questions = questionDao.findAllQuestionsAndSubjects();
44 | Assert.assertNotNull(questions);
45 | Assert.assertEquals(questions.size(), 4);
46 | }
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/backend/src/test/java/com/github/joumenharzli/surveypoc/repository/dao/UserResponseDaoTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 Joumen Harzli
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 | * in compliance with the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License
10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 | * or implied. See the License for the specific language governing permissions and limitations under
12 | * the License.
13 | *
14 | */
15 |
16 | package com.github.joumenharzli.surveypoc.repository.dao;
17 |
18 | import java.util.Arrays;
19 | import java.util.List;
20 |
21 | import org.apache.commons.lang3.RandomStringUtils;
22 | import org.junit.Assert;
23 | import org.junit.Before;
24 | import org.junit.Test;
25 | import org.junit.runner.RunWith;
26 | import org.springframework.beans.factory.annotation.Autowired;
27 | import org.springframework.boot.test.context.SpringBootTest;
28 | import org.springframework.jdbc.core.JdbcTemplate;
29 | import org.springframework.test.context.junit4.SpringRunner;
30 | import org.springframework.test.jdbc.JdbcTestUtils;
31 |
32 | import com.github.joumenharzli.surveypoc.domain.Question;
33 | import com.github.joumenharzli.surveypoc.domain.User;
34 | import com.github.joumenharzli.surveypoc.domain.UserResponse;
35 |
36 | /**
37 | * UserResponseDaoTest
38 | *
39 | * @author Joumen Harzli
40 | */
41 | @RunWith(SpringRunner.class)
42 | @SpringBootTest
43 | public class UserResponseDaoTest {
44 |
45 | @Autowired
46 | UserResponseDao userResponseDao;
47 |
48 | @Autowired
49 | JdbcTemplate jdbcTemplate;
50 |
51 | @Before
52 | public void init() {
53 | JdbcTestUtils.deleteFromTables(jdbcTemplate, "user_responses");
54 | }
55 |
56 | @Test
57 | public void addUserResponsesTest() throws Exception {
58 |
59 | Long userId = 1L;
60 | Long question1Id = 3L;
61 | Long question2Id = 4L;
62 | String responseContent = RandomStringUtils.randomAlphabetic(5);
63 |
64 | int[] result = addUserResponses(userId, question1Id, question2Id, responseContent, responseContent);
65 | Arrays.stream(result).forEach((updatedRow) -> Assert.assertEquals(updatedRow, 1));
66 |
67 | List userResponses = findResponsesOfUserForQuestions(userId, question1Id, question2Id);
68 | userResponses.forEach((userResponse -> Assert.assertEquals(userResponse.getContent(), responseContent)));
69 | }
70 |
71 | @Test
72 | public void updateUserResponsesTest() {
73 |
74 | Long userId = 1L;
75 | Long question1Id = 3L;
76 | Long question2Id = 4L;
77 |
78 | String nonUpdatedContent = RandomStringUtils.randomAlphabetic(5);
79 | String updatedContent = RandomStringUtils.randomAlphabetic(5);
80 |
81 | addUserResponses(userId, question1Id, question2Id, nonUpdatedContent, nonUpdatedContent);
82 |
83 | List userResponses = findResponsesOfUserForQuestions(userId, question1Id, question2Id);
84 | userResponses.forEach((userResponse -> userResponse.setContent(updatedContent)));
85 |
86 | int[] result = userResponseDao.updateUserResponses(userResponses);
87 | Arrays.stream(result).forEach((updatedRow) -> Assert.assertEquals(updatedRow, 1));
88 |
89 | List updatedUserResponses = findResponsesOfUserForQuestions(userId, question1Id, question2Id);
90 | updatedUserResponses.forEach((userResponse -> Assert.assertEquals(userResponse.getContent(), updatedContent)));
91 |
92 | }
93 |
94 | private int[] addUserResponses(Long userId, Long question1Id, Long question2Id,
95 | String response1Content, String response2Content) {
96 | UserResponse userResponse1 = createUserResponse(userId, question1Id, response1Content);
97 | UserResponse userResponse2 = createUserResponse(userId, question2Id, response2Content);
98 |
99 | return userResponseDao.addUserResponses(Arrays.asList(userResponse1, userResponse2));
100 | }
101 |
102 | private List findResponsesOfUserForQuestions(Long userId, Long question1Id, Long question2Id) {
103 | return userResponseDao.findResponsesOfUserByUserIdAndQuestionIds(userId,
104 | Arrays.asList(question1Id, question2Id));
105 | }
106 |
107 | private UserResponse createUserResponse(Long userId, Long questionId, String responseContent) {
108 | return new UserResponse()
109 | .user(createUser(userId))
110 | .question(createQuestion(questionId))
111 | .content(responseContent);
112 | }
113 |
114 | private Question createQuestion(Long questionId) {
115 | return new Question()
116 | .id(questionId);
117 | }
118 |
119 | private User createUser(Long userId) {
120 | return new User()
121 | .id(userId);
122 | }
123 |
124 | }
125 |
--------------------------------------------------------------------------------
/backend/src/test/java/com/github/joumenharzli/surveypoc/service/mapper/SubjectMapperDecoratorTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 Joumen Harzli
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 | * in compliance with the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License
10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 | * or implied. See the License for the specific language governing permissions and limitations under
12 | * the License.
13 | *
14 | */
15 |
16 | package com.github.joumenharzli.surveypoc.service.mapper;
17 |
18 | import java.util.Arrays;
19 | import java.util.List;
20 |
21 | import org.junit.Assert;
22 | import org.junit.Test;
23 | import org.junit.runner.RunWith;
24 | import org.springframework.beans.factory.annotation.Autowired;
25 | import org.springframework.boot.test.context.SpringBootTest;
26 | import org.springframework.test.context.junit4.SpringRunner;
27 |
28 | import com.github.joumenharzli.surveypoc.domain.Question;
29 | import com.github.joumenharzli.surveypoc.domain.Subject;
30 | import com.github.joumenharzli.surveypoc.service.dto.SubjectDto;
31 |
32 | /**
33 | * SubjectMapperDecoratorTest
34 | *
35 | * @author Joumen Harzli
36 | */
37 | @RunWith(SpringRunner.class)
38 | @SpringBootTest
39 | public class SubjectMapperDecoratorTest {
40 |
41 | @Autowired
42 | SubjectMapper subjectMapper;
43 |
44 | @Test
45 | public void questionsToSubjectsDto() throws Exception {
46 | Question q1 = new Question().id(1L).label("question1").subject(new Subject().id(1L));
47 | Question q2 = new Question().id(2L).label("question2").subject(new Subject().id(1L));
48 | Question q3 = new Question().id(3L).label("question3").subject(new Subject().id(2L));
49 |
50 | List subjects = subjectMapper.questionsToSubjectsDto(Arrays.asList(q1, q2, q3));
51 |
52 | Assert.assertNotNull(subjects);
53 | Assert.assertEquals(subjects.size(), 2);
54 | subjects.forEach((this::assertSubjectDtoAndItsQuestionsNotNull));
55 | }
56 |
57 | private void assertSubjectDtoAndItsQuestionsNotNull(SubjectDto subjectDto) {
58 |
59 | Assert.assertNotNull(subjectDto.getQuestions());
60 | subjectDto.getQuestions().forEach((questionDto -> {
61 | Assert.assertNotNull(questionDto.getId());
62 | Assert.assertNotNull(questionDto.getLabel());
63 | }));
64 |
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/frontend/.angular-cli.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
3 | "project": {
4 | "name": "angular-material-with-angular-v5-wv5jbt"
5 | },
6 | "apps": [
7 | {
8 | "root": "src",
9 | "outDir": "dist",
10 | "assets": [
11 | "assets",
12 | "favicon.ico"
13 | ],
14 | "index": "index.html",
15 | "main": "main.ts",
16 | "polyfills": "polyfills.ts",
17 | "test": "test.ts",
18 | "tsconfig": "tsconfig.app.json",
19 | "testTsconfig": "tsconfig.spec.json",
20 | "prefix": "app",
21 | "styles": [
22 | "styles.scss"
23 | ],
24 | "scripts": [],
25 | "environmentSource": "environments/environment.ts",
26 | "environments": {
27 | "dev": "environments/environment.ts",
28 | "prod": "environments/environment.prod.ts"
29 | }
30 | }
31 | ],
32 | "e2e": {
33 | "protractor": {
34 | "config": "./protractor.conf.js"
35 | }
36 | },
37 | "lint": [
38 | {
39 | "project": "src/tsconfig.app.json"
40 | },
41 | {
42 | "project": "src/tsconfig.spec.json"
43 | },
44 | {
45 | "project": "e2e/tsconfig.e2e.json"
46 | }
47 | ],
48 | "test": {
49 | "karma": {
50 | "config": "./karma.conf.js"
51 | }
52 | },
53 | "defaults": {
54 | "styleExt": "css",
55 | "component": {}
56 | }
57 | }
--------------------------------------------------------------------------------
/frontend/.editorconfig:
--------------------------------------------------------------------------------
1 | # Editor configuration, see http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | indent_style = space
7 | indent_size = 2
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
11 | [*.md]
12 | max_line_length = off
13 | trim_trailing_whitespace = false
14 |
--------------------------------------------------------------------------------
/frontend/.gitignore:
--------------------------------------------------------------------------------
1 | # See http://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # compiled output
4 | /dist
5 | /tmp
6 | /out-tsc
7 |
8 | # dependencies
9 | /node_modules
10 |
11 | # IDEs and editors
12 | /.idea
13 | .project
14 | .classpath
15 | .c9/
16 | *.launch
17 | .settings/
18 | *.sublime-workspace
19 |
20 | # IDE - VSCode
21 | .vscode/*
22 | !.vscode/settings.json
23 | !.vscode/tasks.json
24 | !.vscode/launch.json
25 | !.vscode/extensions.json
26 |
27 | # misc
28 | /.sass-cache
29 | /connect.lock
30 | /coverage
31 | /libpeerconnection.log
32 | npm-debug.log
33 | testem.log
34 | /typings
35 |
36 | # e2e
37 | /e2e/*.js
38 | /e2e/*.map
39 |
40 | # System Files
41 | .DS_Store
42 | Thumbs.db
43 |
--------------------------------------------------------------------------------
/frontend/README.md:
--------------------------------------------------------------------------------
1 | # survey-poc-spring-boot-angular
2 | A survey poc using Angular 5, Material, Reactive Forms, Spring Boot..
3 |
4 |
5 | ## Development server
6 |
7 | Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
8 |
9 | ## Code scaffolding
10 |
11 | Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|module`.
12 |
13 | ## Build
14 |
15 | Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `-prod` flag for a production build.
16 |
17 | ## Running unit tests
18 |
19 | Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
20 |
21 | ## Running end-to-end tests
22 |
23 | Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
24 | Before running the tests make sure you are serving the app via `ng serve`.
25 |
--------------------------------------------------------------------------------
/frontend/e2e/app.e2e-spec.ts:
--------------------------------------------------------------------------------
1 | import { AngularTemplatePage } from './app.po';
2 |
3 | describe('angular-template App', () => {
4 | let page: AngularTemplatePage;
5 |
6 | beforeEach(() => {
7 | page = new AngularTemplatePage();
8 | });
9 |
10 | it('should display welcome message', () => {
11 | page.navigateTo();
12 | expect(page.getParagraphText()).toEqual('Welcome to app!');
13 | });
14 | });
15 |
--------------------------------------------------------------------------------
/frontend/e2e/app.po.ts:
--------------------------------------------------------------------------------
1 | import { browser, by, element } from 'protractor';
2 |
3 | export class AngularTemplatePage {
4 | navigateTo() {
5 | return browser.get('/');
6 | }
7 |
8 | getParagraphText() {
9 | return element(by.css('app-root h1')).getText();
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/frontend/e2e/tsconfig.e2e.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../out-tsc/e2e",
5 | "baseUrl": "./",
6 | "module": "commonjs",
7 | "target": "es5",
8 | "types": [
9 | "jasmine",
10 | "jasminewd2",
11 | "node"
12 | ]
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/frontend/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration file, see link for more information
2 | // https://karma-runner.github.io/0.13/config/configuration-file.html
3 |
4 | module.exports = function (config) {
5 | config.set({
6 | basePath: '',
7 | frameworks: ['jasmine', '@angular/cli'],
8 | plugins: [
9 | require('karma-jasmine'),
10 | require('karma-chrome-launcher'),
11 | require('karma-jasmine-html-reporter'),
12 | require('karma-coverage-istanbul-reporter'),
13 | require('@angular/cli/plugins/karma')
14 | ],
15 | client:{
16 | clearContext: false // leave Jasmine Spec Runner output visible in browser
17 | },
18 | coverageIstanbulReporter: {
19 | reports: [ 'html', 'lcovonly' ],
20 | fixWebpackSourcePaths: true
21 | },
22 | angularCli: {
23 | environment: 'dev'
24 | },
25 | reporters: ['progress', 'kjhtml'],
26 | port: 9876,
27 | colors: true,
28 | logLevel: config.LOG_INFO,
29 | autoWatch: true,
30 | browsers: ['Chrome'],
31 | singleRun: false
32 | });
33 | };
34 |
--------------------------------------------------------------------------------
/frontend/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "survey-poc-spring-boot-angular",
3 | "description": "A survey poc using Angular 5, Material, Reactive Forms, Spring Boot..",
4 | "homepage": "https://github.com/joumenharzli/survey-poc-spring-boot-angular",
5 | "author": {
6 | "name": "Joumen Harzli"
7 | },
8 | "version": "0.0.0",
9 | "license": "ALv2",
10 | "scripts": {
11 | "ng": "ng",
12 | "start": "ng serve",
13 | "build": "ng build",
14 | "test": "ng test",
15 | "lint": "ng lint",
16 | "e2e": "ng e2e",
17 | "ci": "ng e2e --watch=false"
18 | },
19 | "dependencies": {
20 | "@angular/animations": "^5.1.3",
21 | "@angular/cdk": "^5.0.3",
22 | "@angular/common": "^5.1.3",
23 | "@angular/compiler": "^5.1.3",
24 | "@angular/core": "^5.1.3",
25 | "@angular/forms": "^5.1.3",
26 | "@angular/http": "^5.1.3",
27 | "@angular/material": "^5.0.3",
28 | "@angular/platform-browser": "^5.1.3",
29 | "@angular/platform-browser-dynamic": "^5.1.3",
30 | "@angular/router": "^5.1.3",
31 | "core-js": "^2.5.3",
32 | "hammerjs": "2.0.8",
33 | "lodash": "^4.17.4",
34 | "rxjs": "^5.5.6",
35 | "web-animations-js": "2.3.1",
36 | "zone.js": "^0.8.19"
37 | },
38 | "devDependencies": {
39 | "@angular/cli": "^1.6.3",
40 | "@angular/compiler-cli": "^5.0.0",
41 | "@angular/language-service": "^5.0.0",
42 | "@types/jasmine": "~2.5.53",
43 | "@types/jasminewd2": "~2.0.2",
44 | "@types/node": "~6.0.60",
45 | "codelyzer": "~3.0.1",
46 | "jasmine-core": "~2.6.2",
47 | "jasmine-spec-reporter": "~4.1.0",
48 | "karma": "~1.7.0",
49 | "karma-chrome-launcher": "~2.1.1",
50 | "karma-cli": "~1.0.1",
51 | "karma-coverage-istanbul-reporter": "^1.2.1",
52 | "karma-jasmine": "~1.1.0",
53 | "karma-jasmine-html-reporter": "^0.2.2",
54 | "protractor": "~5.1.2",
55 | "ts-node": "~3.0.4",
56 | "tslint": "~5.3.2",
57 | "typescript": "~2.4.2"
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/frontend/protractor.conf.js:
--------------------------------------------------------------------------------
1 | // Protractor configuration file, see link for more information
2 | // https://github.com/angular/protractor/blob/master/lib/config.ts
3 |
4 | const { SpecReporter } = require('jasmine-spec-reporter');
5 |
6 | exports.config = {
7 | allScriptsTimeout: 11000,
8 | specs: [
9 | './e2e/**/*.e2e-spec.ts'
10 | ],
11 | capabilities: {
12 | 'browserName': 'chrome'
13 | },
14 | directConnect: true,
15 | baseUrl: 'http://localhost:4200/',
16 | framework: 'jasmine',
17 | jasmineNodeOpts: {
18 | showColors: true,
19 | defaultTimeoutInterval: 30000,
20 | print: function() {}
21 | },
22 | onPrepare() {
23 | require('ts-node').register({
24 | project: 'e2e/tsconfig.e2e.json'
25 | });
26 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
27 | }
28 | };
29 |
--------------------------------------------------------------------------------
/frontend/src/app/app.component.html:
--------------------------------------------------------------------------------
1 |
15 |
16 |
17 | Survey example
18 |
19 |
20 |
23 |
--------------------------------------------------------------------------------
/frontend/src/app/app.component.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 Joumen Harzli
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 | * in compliance with the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License
10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 | * or implied. See the License for the specific language governing permissions and limitations under
12 | * the License.
13 | *
14 | */
15 |
16 | import { Component } from '@angular/core';
17 |
18 | /**
19 | * A simple component that demonstrate a solution for creating surveys using Angular
20 | *
21 | * @export
22 | * @class AppComponent
23 | * @author Joumen HARZLI
24 | */
25 | @Component({
26 | selector: 'app-survey',
27 | templateUrl: 'app.component.html'
28 | })
29 |
30 | export class AppComponent { }
31 |
32 |
--------------------------------------------------------------------------------
/frontend/src/app/app.module.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 Joumen Harzli
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 | * in compliance with the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License
10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 | * or implied. See the License for the specific language governing permissions and limitations under
12 | * the License.
13 | *
14 | */
15 |
16 | import { NgModule } from '@angular/core';
17 | import { BrowserModule } from '@angular/platform-browser';
18 | import { MaterialModule } from './material.module';
19 |
20 | import { AppComponent } from './app.component';
21 | import { SurveyModule } from './survey/survey.module';
22 |
23 | @NgModule({
24 | imports: [
25 | BrowserModule,
26 | MaterialModule,
27 | SurveyModule
28 | ],
29 | declarations: [AppComponent],
30 | bootstrap: [AppComponent]
31 | })
32 | export class AppModule { }
33 |
--------------------------------------------------------------------------------
/frontend/src/app/material.module.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 Joumen Harzli
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 | * in compliance with the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License
10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 | * or implied. See the License for the specific language governing permissions and limitations under
12 | * the License.
13 | *
14 | */
15 |
16 | import { NgModule } from '@angular/core';
17 | import { CommonModule } from '@angular/common';
18 |
19 | import { A11yModule } from '@angular/cdk/a11y';
20 | import { BidiModule } from '@angular/cdk/bidi';
21 | import { ObserversModule } from '@angular/cdk/observers';
22 | import { OverlayModule } from '@angular/cdk/overlay';
23 | import { PlatformModule } from '@angular/cdk/platform';
24 | import { PortalModule } from '@angular/cdk/portal';
25 | import { ScrollDispatchModule } from '@angular/cdk/scrolling';
26 | import { CdkStepperModule } from '@angular/cdk/stepper';
27 | import { CdkTableModule } from '@angular/cdk/table';
28 | import {
29 | MatAutocompleteModule,
30 | MatButtonModule,
31 | MatButtonToggleModule,
32 | MatCardModule,
33 | MatCheckboxModule,
34 | MatChipsModule,
35 | MatDatepickerModule,
36 | MatDialogModule,
37 | MatExpansionModule,
38 | MatGridListModule,
39 | MatIconModule,
40 | MatInputModule,
41 | MatListModule,
42 | MatMenuModule,
43 | MatNativeDateModule,
44 | MatProgressBarModule,
45 | MatProgressSpinnerModule,
46 | MatRadioModule,
47 | MatRippleModule,
48 | MatSelectModule,
49 | MatSidenavModule,
50 | MatSliderModule,
51 | MatSlideToggleModule,
52 | MatSnackBarModule,
53 | MatStepperModule,
54 | MatTableModule,
55 | MatTabsModule,
56 | MatToolbarModule,
57 | MatTooltipModule,
58 | } from '@angular/material';
59 |
60 |
61 | @NgModule({
62 | exports: [
63 | // CDK
64 | A11yModule,
65 | BidiModule,
66 | ObserversModule,
67 | OverlayModule,
68 | PlatformModule,
69 | PortalModule,
70 | ScrollDispatchModule,
71 | CdkStepperModule,
72 | CdkTableModule,
73 |
74 | // Material
75 | MatAutocompleteModule,
76 | MatButtonModule,
77 | MatButtonToggleModule,
78 | MatCardModule,
79 | MatCheckboxModule,
80 | MatChipsModule,
81 | MatDatepickerModule,
82 | MatDialogModule,
83 | MatExpansionModule,
84 | MatGridListModule,
85 | MatIconModule,
86 | MatInputModule,
87 | MatListModule,
88 | MatMenuModule,
89 | MatProgressBarModule,
90 | MatProgressSpinnerModule,
91 | MatRadioModule,
92 | MatRippleModule,
93 | MatSelectModule,
94 | MatSidenavModule,
95 | MatSlideToggleModule,
96 | MatSliderModule,
97 | MatSnackBarModule,
98 | MatStepperModule,
99 | MatTableModule,
100 | MatTabsModule,
101 | MatToolbarModule,
102 | MatTooltipModule,
103 | MatNativeDateModule,
104 | ]
105 | })
106 | export class MaterialModule { }
107 |
--------------------------------------------------------------------------------
/frontend/src/app/shared/models/field-error.model.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 Joumen Harzli
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 | * in compliance with the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License
10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 | * or implied. See the License for the specific language governing permissions and limitations under
12 | * the License.
13 | *
14 | */
15 |
16 | export interface FieldError {
17 | field: string;
18 | code: string;
19 | message: string;
20 | }
21 |
--------------------------------------------------------------------------------
/frontend/src/app/shared/models/rest-error.model.ts:
--------------------------------------------------------------------------------
1 | import { FieldError } from './field-error.model';
2 |
3 | /*
4 | * Copyright (C) 2018 Joumen Harzli
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
7 | * in compliance with the License. You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software distributed under the License
12 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
13 | * or implied. See the License for the specific language governing permissions and limitations under
14 | * the License.
15 | *
16 | */
17 |
18 | export interface RestError {
19 | code: string;
20 | message: string;
21 | fieldsErrors?: FieldError[];
22 | }
23 |
--------------------------------------------------------------------------------
/frontend/src/app/survey/components/survey-form/survey-form.component.html:
--------------------------------------------------------------------------------
1 |
15 |
16 |
--------------------------------------------------------------------------------
/frontend/src/app/survey/components/survey-form/survey-form.component.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 Joumen Harzli
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 | * in compliance with the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License
10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 | * or implied. See the License for the specific language governing permissions and limitations under
12 | * the License.
13 | *
14 | */
15 |
16 | import { Component, OnInit, Input, Output, EventEmitter, ChangeDetectionStrategy } from '@angular/core';
17 | import { FormGroup, FormArray, FormBuilder, Validators } from '@angular/forms';
18 | import { findIndex, flatMap } from 'lodash';
19 |
20 | import { UserResponse, Subject, Question } from '../../models/survey.models';
21 | import { FieldError } from '../../../shared/models/field-error.model';
22 |
23 | @Component({
24 | selector: 'app-survey-form',
25 | templateUrl: './survey-form.component.html',
26 | changeDetection: ChangeDetectionStrategy.OnPush
27 | })
28 | export class SurveyFormComponent {
29 |
30 | form: FormGroup;
31 |
32 | _responses: UserResponse[];
33 |
34 | _fieldsErrors: FieldError[];
35 |
36 | @Input()
37 | subjects: Subject[] = [];
38 |
39 | @Input()
40 | set responses(responses: UserResponse[]) {
41 | if (responses) {
42 | this._responses = responses;
43 | this.buildForm();
44 | }
45 | }
46 |
47 | @Input()
48 | set fieldsErrors(fieldsErrors: FieldError[]) {
49 | if (fieldsErrors) {
50 | this._fieldsErrors = fieldsErrors;
51 | this.appendFieldsErrorsToForm(fieldsErrors);
52 | }
53 | }
54 |
55 | @Input()
56 | submitting: boolean;
57 |
58 | @Output()
59 | onSubmitForm: EventEmitter<{ responses: UserResponse[] }> = new EventEmitter();
60 |
61 | constructor(private formBuilder: FormBuilder) { }
62 |
63 | /**
64 | * Build the reponses form array by iterating over the provided responses
65 | * Every reponse is mapped by a form group
66 | *
67 | * @memberof AppComponent
68 | */
69 | buildForm() {
70 | this.form = this.formBuilder.group({ responses: this.formBuilder.array([]) });
71 | const questions = flatMap(this.subjects.map(subject => subject.questions));
72 |
73 | questions.forEach((question) =>
74 | (this.form.get('responses')).push(this.buildResponseFormGroup(question)));
75 | }
76 |
77 | /**
78 | * Submit the form
79 | * @param {FormGroup} form form to submit
80 | */
81 | submitForm(form: FormGroup) {
82 | this.onSubmitForm.emit(form.value);
83 | }
84 |
85 | /**
86 | * Create a form group that maps to the strcuture of the reponse
87 | *
88 | * @param {Question} response question that will be mapped
89 | * @returns a form group with the mapped response
90 | * @memberof AppComponent
91 | */
92 | buildResponseFormGroup(question: Question) {
93 |
94 | const responseIndex = findIndex(this._responses, response => response.questionId === question.id);
95 | const response = this._responses[responseIndex];
96 |
97 | return this.formBuilder.group({
98 | questionId: question.id,
99 | content: this.formBuilder.control(response ? response.content : '')
100 | });
101 |
102 | }
103 |
104 | /**
105 | * Get the first error message of the control
106 | * @param questionId id of the question mapped to the control
107 | */
108 | getContentErrorMessage(questionId: number) {
109 | const controlIndex = this.getFormGroupIndexByQuestionId(questionId);
110 |
111 | const fieldPath = 'responses[' + controlIndex + '].content';
112 |
113 | return this._fieldsErrors.filter(fieldError => fieldError.field === fieldPath)
114 | .map(fieldError => fieldError.message)[0];
115 | }
116 |
117 | /**
118 | * Check if the control is invalid
119 | * @param questionId id of the question mapped to the control
120 | */
121 | isContentInvalid(questionId: number) {
122 | const controlIndex = this.getFormGroupIndexByQuestionId(questionId);
123 |
124 | const fieldPath = 'responses.' + controlIndex + '.content';
125 | const field = this.form.get(fieldPath);
126 |
127 | if (field) {
128 | return field.invalid;
129 | }
130 |
131 | }
132 |
133 | /**
134 | * Get the index of the form group inside the form array
135 | * using the question id
136 | *
137 | * @param {number} questionId id of the question
138 | * @returns index of the form group
139 | * @throws Error if the form group was not found
140 | * @memberof AppComponent
141 | */
142 | getFormGroupIndexByQuestionId(questionId: number) {
143 | const index = findIndex(this.form.value.responses, response => response.questionId === questionId);
144 |
145 | if (index === -1) {
146 | throw new Error('No valid value was found for the response of the question with id ' + questionId);
147 | }
148 |
149 | return index;
150 | }
151 |
152 | /**
153 | * Extract the recived field errors and append them to the form
154 | * @param fieldsErrors errors of the fields
155 | */
156 | private appendFieldsErrorsToForm(fieldsErrors: FieldError[]) {
157 | fieldsErrors.forEach((element) => {
158 |
159 | const fieldError = {};
160 | fieldError[element.code] = true;
161 |
162 | const mappedField = element.field.replace('[', '.').replace(']', '');
163 | const formField = this.form.get(mappedField);
164 |
165 | if (formField) {
166 | formField.setErrors(fieldError);
167 | }
168 |
169 | });
170 | }
171 |
172 | /**
173 | * Used in the `ngFor` for better performance
174 | *
175 | * @param {any} entity entity iterated
176 | * @returns code of the entity
177 | * @memberof AppComponent
178 | */
179 | trackByFn(entity) {
180 | return entity.code;
181 | }
182 |
183 | }
184 |
--------------------------------------------------------------------------------
/frontend/src/app/survey/containers/survey-container/survey-container.component.html:
--------------------------------------------------------------------------------
1 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/frontend/src/app/survey/containers/survey-container/survey-container.component.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 Joumen Harzli
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 | * in compliance with the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License
10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 | * or implied. See the License for the specific language governing permissions and limitations under
12 | * the License.
13 | *
14 | */
15 |
16 | import { Component, OnInit } from '@angular/core';
17 | import { FormArray, FormBuilder, Validators } from '@angular/forms';
18 | import { findIndex, flatMap } from 'lodash';
19 | import 'rxjs/add/operator/switchMap';
20 |
21 | import { SurveyService } from '../../services/survey.service';
22 | import { Subject, UserResponse, Question } from '../../models/survey.models';
23 | import { MatSnackBar } from '@angular/material';
24 | import { FieldError } from '../../../shared/models/field-error.model';
25 | import { RestError } from '../../../shared/models/rest-error.model';
26 |
27 | @Component({
28 | selector: 'app-survey-container',
29 | templateUrl: './survey-container.component.html'
30 | })
31 | export class SurveyContainerComponent implements OnInit {
32 |
33 | loading = false;
34 | submitting = false;
35 | error = null;
36 |
37 | subjects: Subject[] = [];
38 | responses: UserResponse[] = [];
39 | fieldsErrors: FieldError[];
40 |
41 | constructor(private httpService: SurveyService, private snackBar: MatSnackBar) { }
42 |
43 | ngOnInit() {
44 | this.loadAllSubjectsAndQuestionsAndResponses();
45 | }
46 |
47 | loadAllSubjectsAndQuestionsAndResponses() {
48 | this.loading = true;
49 | this.error = null;
50 | this.httpService.getAllSubjectsAndQuestions()
51 | .switchMap(subjects => this.loadResponsesOfConnectedUserForQuestions(subjects),
52 | (subjects, responses) => ({ subjects: subjects, responses: responses }))
53 | .subscribe((result) => {
54 | this.subjects = result.subjects;
55 | this.responses = result.responses;
56 | this.loading = false;
57 | }, (error) => this.handleHttpError(error));
58 | }
59 |
60 | /**
61 | * handle the form submition
62 | *
63 | * @param responses of the user
64 | * @memberof AppComponent
65 | */
66 | submitForm(responses) {
67 | this.submitting = true;
68 | this.httpService.saveResponsesOfConnectedUserForQuestions(responses)
69 | .subscribe((savedResponses) => {
70 | this.responses = savedResponses;
71 | this.loading = false;
72 | this.submitting = false;
73 | this.openSnackBar('Responses submitted');
74 | }, (error) => this.handleHttpError(error));
75 | }
76 |
77 | loadResponsesOfConnectedUserForQuestions(subjects: Subject[]) {
78 | const questionIds = flatMap(subjects, subject => subject.questions).map(question => question.id);
79 | return this.httpService.getResponsesOfConnectedUserForQuestions(questionIds);
80 | }
81 |
82 | handleHttpError(errorResponse) {
83 | const error = errorResponse.error as RestError;
84 |
85 | if (error && error.message) {
86 | if (error.code === 'error.validation') {
87 | this.fieldsErrors = error.fieldsErrors;
88 | }
89 | const errorMessage = error.message;
90 | this.error = error;
91 | this.openSnackBar(errorMessage);
92 | } else {
93 | this.error = errorResponse;
94 | this.openSnackBar('Failed to connect to the server');
95 | }
96 | this.submitting = false;
97 | this.loading = false;
98 | }
99 |
100 | openSnackBar(message) {
101 | this.snackBar.open(message, 'Close', {
102 | duration: 2000,
103 | horizontalPosition: 'left'
104 | });
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/frontend/src/app/survey/models/survey.models.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 Joumen Harzli
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 | * in compliance with the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License
10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 | * or implied. See the License for the specific language governing permissions and limitations under
12 | * the License.
13 | *
14 | */
15 |
16 | export interface UserResponse {
17 | content: string;
18 | questionId: number;
19 | }
20 |
21 | export interface Subject {
22 | id: number;
23 | label: string;
24 | questions: Question[];
25 | }
26 |
27 | export interface Question {
28 | id: number;
29 | label: string;
30 | }
31 |
--------------------------------------------------------------------------------
/frontend/src/app/survey/services/survey.service.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 Joumen Harzli
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 | * in compliance with the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License
10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 | * or implied. See the License for the specific language governing permissions and limitations under
12 | * the License.
13 | *
14 | */
15 |
16 | import { Injectable } from '@angular/core';
17 | import { HttpClient } from '@angular/common/http';
18 | import { Observable } from 'rxjs/Observable';
19 |
20 | import { UserResponse, Question, Subject } from '../models/survey.models';
21 |
22 | @Injectable()
23 | export class SurveyService {
24 |
25 | SUBJECT_API = 'http://localhost:8080/api/v1/subjects';
26 | QUESTION_API = 'http://localhost:8080/api/v1/questions';
27 |
28 | constructor(private httpClient: HttpClient) { }
29 |
30 | /**
31 | * Get all the subjects and their questions
32 | *
33 | * @returns {Observable} all the found subjects and their questions
34 | */
35 | getAllSubjectsAndQuestions(): Observable {
36 | return this.httpClient.get(this.SUBJECT_API);
37 | }
38 |
39 | /**
40 | * Get all responses of the connected user for the questions
41 | *
42 | * @param {number[]} questions ids of the questions that the user may responded
43 | * @returns {Observable} the found responses of the connected user for the provided questions
44 | */
45 | getResponsesOfConnectedUserForQuestions(questions: number[]): Observable {
46 | return this.httpClient.get(this.QUESTION_API + '/' + questions.toString() + '/responses/me');
47 | }
48 |
49 | /**
50 | * Add and update the responses of the connected user for the provided questions
51 | *
52 | * @param {UserResponse[]} userResponses new responses of the user
53 | * @returns {Observable} the list of the saved responses of the user
54 | */
55 | saveResponsesOfConnectedUserForQuestions(userResponses: UserResponse[]): Observable {
56 | return this.httpClient.post(this.QUESTION_API + '/responses/me', userResponses);
57 | }
58 |
59 | }
60 |
--------------------------------------------------------------------------------
/frontend/src/app/survey/survey.module.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 Joumen Harzli
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 | * in compliance with the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License
10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 | * or implied. See the License for the specific language governing permissions and limitations under
12 | * the License.
13 | *
14 | */
15 |
16 | import { NgModule } from '@angular/core';
17 | import { CommonModule } from '@angular/common';
18 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
19 | import { FormsModule, ReactiveFormsModule } from '@angular/forms';
20 | import { HttpClientModule } from '@angular/common/http';
21 |
22 | import { SurveyContainerComponent } from './containers/survey-container/survey-container.component';
23 | import { SurveyFormComponent } from './components/survey-form/survey-form.component';
24 | import { SurveyService } from './services/survey.service';
25 | import { MaterialModule } from '../material.module';
26 |
27 | @NgModule({
28 | imports: [
29 | CommonModule,
30 | FormsModule,
31 | ReactiveFormsModule,
32 | HttpClientModule,
33 | BrowserAnimationsModule,
34 | MaterialModule
35 | ],
36 | declarations: [SurveyContainerComponent, SurveyFormComponent],
37 | providers: [SurveyService],
38 | exports: [SurveyContainerComponent]
39 | })
40 | export class SurveyModule { }
41 |
--------------------------------------------------------------------------------
/frontend/src/assets/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MuhamedHabib/survey-example-spring-boot-angular/376888b31391948267c541143677d32ad9341076/frontend/src/assets/.gitkeep
--------------------------------------------------------------------------------
/frontend/src/environments/environment.prod.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 Joumen Harzli
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 | * in compliance with the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License
10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 | * or implied. See the License for the specific language governing permissions and limitations under
12 | * the License.
13 | *
14 | */
15 |
16 | export const environment = {
17 | production: true
18 | };
19 |
--------------------------------------------------------------------------------
/frontend/src/environments/environment.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 Joumen Harzli
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 | * in compliance with the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License
10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 | * or implied. See the License for the specific language governing permissions and limitations under
12 | * the License.
13 | *
14 | */
15 |
16 | // The file contents for the current environment will overwrite these during build.
17 | // The build system defaults to the dev environment which uses `environment.ts`, but if you do
18 | // `ng build --env=prod` then `environment.prod.ts` will be used instead.
19 | // The list of which env maps to which file can be found in `.angular-cli.json`.
20 |
21 | export const environment = {
22 | production: false
23 | };
24 |
--------------------------------------------------------------------------------
/frontend/src/favicon.ico:
--------------------------------------------------------------------------------
1 | h &