Commit a83bdc4a authored by François-Xavier Lebastard's avatar François-Xavier Lebastard
Browse files

UNOTOPLYS-171 feat(ordre écran) : réordonner les écrans;

mise en place du endpoint rest pour patcher l'ordre des écrans
parent 53485c86
......@@ -135,6 +135,11 @@
<artifactId>hibernate-types-52</artifactId>
<version>${hibernate-types.version}</version>
</dependency>
<dependency>
<groupId>com.github.java-json-tools</groupId>
<artifactId>json-patch</artifactId>
<version>1.13</version>
</dependency>
</dependencies>
</dependencyManagement>
......@@ -389,6 +394,10 @@
<groupId>com.vladmihalcea</groupId>
<artifactId>hibernate-types-52</artifactId>
</dependency>
<dependency>
<groupId>com.github.java-json-tools</groupId>
<artifactId>json-patch</artifactId>
</dependency>
</dependencies>
<build>
......
......@@ -106,10 +106,34 @@ public interface ScreenRepository extends JpaRepository<Screen, Long> {
* @param formId un identifiant de formulaire
* @param index un index (position) d'écran
*/
@Query(
value = "UPDATE screen SET index = (index - 1) WHERE index > :index AND form_id = :formId",
nativeQuery = true
)
@Query(value = "UPDATE screen SET index = (index - 1) WHERE index > :index AND form_id = :formId", nativeQuery = true)
@Modifying(flushAutomatically = true, clearAutomatically = true)
void decreaseScreenIndex(@Param("formId") Long formId, @Param("index") Integer index);
/**
* Déplace vers le bas tous les écrans dont l'index est >= nouvel index et < ancien index
* @param oldIndex l'ancienne position de l'écran
* @param newIndex la nouvelle position de l'écran
*/
@Query(value = "UPDATE screen SET index = index + 1 WHERE index >= :newIndex AND index < :oldIndex", nativeQuery = true)
@Modifying
void moveUp(@Param("oldIndex") Integer oldIndex, @Param("newIndex") Integer newIndex);
/**
* Déplace vers le haut tous les écrans dont l'index est > ancien index et <= nouvel index
* @param oldIndex l'ancienne position de l'écran
* @param newIndex la nouvelle position de l'écran
*/
@Query(value = "UPDATE screen SET index = index - 1 WHERE index > :oldIndex AND index <= :newIndex", nativeQuery = true)
@Modifying
void moveDown(@Param("oldIndex") Integer oldIndex, @Param("newIndex") Integer newIndex);
/**
* Change l'index
* @param id l'id d'un écran
* @param newIndex son nouvel index
*/
@Query(value = "UPDATE screen SET index = :newIndex WHERE id = :id", nativeQuery = true)
@Modifying(flushAutomatically = true, clearAutomatically = true)
void updateIndex(@Param("id") Long id, @Param("newIndex") Integer newIndex);
}
......@@ -12,6 +12,11 @@ import org.springframework.security.access.AccessDeniedException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.fge.jsonpatch.JsonPatch;
import com.github.fge.jsonpatch.JsonPatchException;
import com.unantes.orientactive.domain.Screen;
import com.unantes.orientactive.domain.User;
import com.unantes.orientactive.repository.ScreenRepository;
......@@ -213,4 +218,55 @@ public class ScreenService {
public Optional<ScreenDTO> findScreenByIndexAndFormId(final Integer index, final Long formId) {
return screenRepository.findScreenByIndexAndFormId(index, formId).map(screenMapper::toDto);
}
/**
* Modifie les élements de l'écran ciblés par le patch. Si l'index est modifié : met à jour les écrans impactés.
* @param id un identifiant d'écran en base de donnée
* @param patch le patch à appliquer
* @return
*/
public ScreenDTO patch(final Long id, final JsonPatch patch) {
final ScreenDTO oldScreen = findOne(id).orElseThrow(() -> new EntityNotFoundException(id));
final ObjectMapper objectMapper = new ObjectMapper();
final JsonNode screenNode = objectMapper.convertValue(oldScreen, JsonNode.class);
try {
final JsonNode nodePatched = patch.apply(screenNode);
final ScreenDTO patchedScreen = objectMapper.treeToValue(nodePatched, ScreenDTO.class);
updateScreenOrder(oldScreen, patchedScreen);
return patchedScreen;
} catch (JsonPatchException | JsonProcessingException e) {
throw new IllegalStateException(e);
}
}
/**
* Met à jour si nécessaire l'ordre d'un écran et ceux des écrans impactés
* @param oldScreen l'ancien écran
* @param patchedScreen l'écran modifié
*/
public void updateScreenOrder(final ScreenDTO oldScreen, final ScreenDTO patchedScreen) {
final Integer newIndex = patchedScreen.getIndex();
final Integer oldIndex = oldScreen.getIndex();
if (!newIndex.equals(oldIndex)) {
updateScreenOrder(oldScreen.getId(), oldIndex, newIndex);
}
}
/**
* Met à jour l'ordre d'un écran et ceux des écrans impactés
* @param id l'ancien écran
* @param oldIndex l'écran modifié
* @param newIndex l'écran modifié
*/
private void updateScreenOrder(final Long id, final Integer oldIndex, final Integer newIndex) {
if (oldIndex < newIndex) {
screenRepository.moveDown(oldIndex, newIndex);
screenRepository.updateIndex(id, newIndex);
} else if (oldIndex > newIndex) {
screenRepository.moveUp(oldIndex, newIndex);
screenRepository.updateIndex(id, newIndex);
} else {
log.debug("L'index de l'écran id = {} n'a pas été modifié", id);
}
}
}
......@@ -7,6 +7,7 @@ import javax.validation.constraints.NotNull;
import org.apache.commons.lang3.builder.ToStringBuilder;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.unantes.orientactive.converter.bean.Item;
import com.unantes.orientactive.validation.CheckJson;
......@@ -52,6 +53,7 @@ public class ScreenDTO implements Serializable {
private List<NextScreenExpressionDTO> nextScreenExpressions;
@JsonIgnore
public Integer getNextIndex() {
return index + 1;
}
......
package com.unantes.orientactive.service.dto;
/**
* Représente l'index d'un écran
*/
public class ScreenIndexDTO {
private Long id;
private int index;
public int getIndex() {
return index;
}
public void setIndex(final int index) {
this.index = index;
}
public Long getId() {
return id;
}
public void setId(final Long id) {
this.id = id;
}
}
package com.unantes.orientactive.web.rest;
import com.github.fge.jsonpatch.JsonPatch;
import com.unantes.orientactive.domain.User;
import com.unantes.orientactive.navigation.exception.ScreenNotFoundException;
import com.unantes.orientactive.service.ScreenService;
......@@ -14,6 +15,7 @@ import org.springframework.http.ResponseEntity;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
......@@ -108,6 +110,11 @@ public class ScreenResource {
.body(result);
}
@PatchMapping(path = "/screens/{id}", consumes = "application/json-patch+json")
public ResponseEntity<ScreenDTO> patch(@PathVariable Long id, @RequestBody JsonPatch patch) {
return ResponseEntity.ok().body(screenService.patch(id, patch));
}
/**
* {@code GET /screens/:id} : get the "id" screen.
*
......
......@@ -99,7 +99,9 @@ export default class ScreenService {
public updateIndex(id: number, index: number): Promise<IScreen> {
return new Promise<IScreen>((resolve, reject) => {
axios
.patch(`${baseApiUrl}/${id}`, { index })
.patch(`${baseApiUrl}/${id}`, [{ op: 'replace', path: '/index', value: index }], {
headers: { 'Content-Type': 'application/json-patch+json' },
})
.then(res => {
resolve(res.data);
})
......
......@@ -28,6 +28,7 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.Matchers.hasItem;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
......@@ -75,6 +76,10 @@ public class ScreenResourceIT {
public static final String SCREEN_2 = "screen2";
public static final String SCREEN_3 = "screen3";
public static final String SCREEN_4 = "screen4";
private List<NextScreenExpression> nextScreenExpressions;
@Autowired
......@@ -409,4 +414,101 @@ public class ScreenResourceIT {
Assert.assertEquals(1, em.createNativeQuery("select * from next_screen_expression").getResultList().size());
}
@Test
@Transactional
public void moveDownScreenTest() throws Exception {
Screen screen2 = new Screen().name(SCREEN_2).nameBo(SCREEN_2).index(2).form(form).reference(SCREEN_2).description(SCREEN_2).defaultNextScreenReference(DEFAULT_NEXT_SCREEN_REFERENCE).items(DEFAULT_ITEMS).nextScreenExpressions(nextScreenExpressions);
Screen screen3 = new Screen().name(SCREEN_3).nameBo(SCREEN_3).index(3).form(form).reference(SCREEN_3).description(SCREEN_3).defaultNextScreenReference(DEFAULT_NEXT_SCREEN_REFERENCE).items(DEFAULT_ITEMS).nextScreenExpressions(nextScreenExpressions);
Screen screen4 = new Screen().name(SCREEN_4).nameBo(SCREEN_4).index(4).form(form).reference(SCREEN_4).description(SCREEN_4).defaultNextScreenReference(DEFAULT_NEXT_SCREEN_REFERENCE).items(DEFAULT_ITEMS).nextScreenExpressions(nextScreenExpressions);
// Initialize the database
screenRepository.save(screen);
screenRepository.save(screen2);
screenRepository.save(screen3);
screenRepository.save(screen4);
// on descend l'écran deux à la place du 3
restScreenMockMvc
.perform(patch("/api/screens/{id}", screen2.getId())
.contentType("application/json-patch+json")
.content("[{ \"op\":\"replace\", \"path\":\"/index\", \"value\":\"3\"}]"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.index").value("3"));
checkIndex(screen.getReference(), (Integer) 1);
checkIndex(screen3.getReference(), (Integer) 2);
checkIndex(screen2.getReference(), (Integer) 3);
checkIndex(screen4.getReference(), (Integer) 4);
}
@Test
@Transactional
public void moveBottomScreenTest() throws Exception {
Screen screen2 = new Screen().name(SCREEN_2).nameBo(SCREEN_2).index(2).form(form).reference(SCREEN_2).description(SCREEN_2).defaultNextScreenReference(DEFAULT_NEXT_SCREEN_REFERENCE).items(DEFAULT_ITEMS).nextScreenExpressions(nextScreenExpressions);
Screen screen3 = new Screen().name(SCREEN_3).nameBo(SCREEN_3).index(3).form(form).reference(SCREEN_3).description(SCREEN_3).defaultNextScreenReference(DEFAULT_NEXT_SCREEN_REFERENCE).items(DEFAULT_ITEMS).nextScreenExpressions(nextScreenExpressions);
Screen screen4 = new Screen().name(SCREEN_4).nameBo(SCREEN_4).index(4).form(form).reference(SCREEN_4).description(SCREEN_4).defaultNextScreenReference(DEFAULT_NEXT_SCREEN_REFERENCE).items(DEFAULT_ITEMS).nextScreenExpressions(nextScreenExpressions);
// Initialize the database
screenRepository.save(screen);
screenRepository.save(screen2);
screenRepository.save(screen3);
screenRepository.save(screen4);
// on descend l'écran deux à la place du 3
restScreenMockMvc
.perform(patch("/api/screens/{id}", screen2.getId())
.contentType("application/json-patch+json")
.content("[{ \"op\":\"replace\", \"path\":\"/index\", \"value\":\"4\"}]"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.index").value("4"));
checkIndex(screen.getReference(), (Integer) 1);
checkIndex(screen3.getReference(), (Integer) 2);
checkIndex(screen4.getReference(), (Integer) 3);
checkIndex(screen2.getReference(), (Integer) 4);
}
@Test
@Transactional
public void moveTopScreenTest() throws Exception {
Screen screen2 = new Screen().name(SCREEN_2).nameBo(SCREEN_2).index(2).form(form).reference(SCREEN_2).description(SCREEN_2).defaultNextScreenReference(DEFAULT_NEXT_SCREEN_REFERENCE).items(DEFAULT_ITEMS).nextScreenExpressions(nextScreenExpressions);
Screen screen3 = new Screen().name(SCREEN_3).nameBo(SCREEN_3).index(3).form(form).reference(SCREEN_3).description(SCREEN_3).defaultNextScreenReference(DEFAULT_NEXT_SCREEN_REFERENCE).items(DEFAULT_ITEMS).nextScreenExpressions(nextScreenExpressions);
Screen screen4 = new Screen().name(SCREEN_4).nameBo(SCREEN_4).index(4).form(form).reference(SCREEN_4).description(SCREEN_4).defaultNextScreenReference(DEFAULT_NEXT_SCREEN_REFERENCE).items(DEFAULT_ITEMS).nextScreenExpressions(nextScreenExpressions);
// Initialize the database
screenRepository.save(screen);
screenRepository.save(screen2);
screenRepository.save(screen3);
screenRepository.save(screen4);
// on monte l'écran deux à la place du 1
restScreenMockMvc
.perform(patch("/api/screens/{id}", screen2.getId()).contentType("application/json-patch+json")
.content("[{ \"op\":\"replace\", \"path\":\"/index\", \"value\":\"1\"}]"))
.andExpect(status().isOk()).andExpect(jsonPath("$.index")
.value("1"));
checkIndex(screen2.getReference(), (Integer) 1);
checkIndex(screen.getReference(), (Integer) 2);
checkIndex(screen3.getReference(), (Integer) 3);
checkIndex(screen4.getReference(), (Integer) 4);
}
@Test
@Transactional
public void moveUpScreenTest() throws Exception {
Screen screen2 = new Screen().name(SCREEN_2).nameBo(SCREEN_2).index(2).form(form).reference(SCREEN_2).description(SCREEN_2).defaultNextScreenReference(DEFAULT_NEXT_SCREEN_REFERENCE).items(DEFAULT_ITEMS).nextScreenExpressions(nextScreenExpressions);
Screen screen3 = new Screen().name(SCREEN_3).nameBo(SCREEN_3).index(3).form(form).reference(SCREEN_3).description(SCREEN_3).defaultNextScreenReference(DEFAULT_NEXT_SCREEN_REFERENCE).items(DEFAULT_ITEMS).nextScreenExpressions(nextScreenExpressions);
Screen screen4 = new Screen().name(SCREEN_4).nameBo(SCREEN_4).index(4).form(form).reference(SCREEN_4).description(SCREEN_4).defaultNextScreenReference(DEFAULT_NEXT_SCREEN_REFERENCE).items(DEFAULT_ITEMS).nextScreenExpressions(nextScreenExpressions);
// Initialize the database
screenRepository.save(screen);
screenRepository.save(screen2);
screenRepository.save(screen3);
screenRepository.save(screen4);
// on monte l'écran deux à la place du 1
restScreenMockMvc
.perform(patch("/api/screens/{id}", screen3.getId()).contentType("application/json-patch+json")
.content("[{ \"op\":\"replace\", \"path\":\"/index\", \"value\":\"2\"}]"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.index").value("2"));
checkIndex(screen.getReference(), (Integer) 1);
checkIndex(screen3.getReference(), (Integer) 2);
checkIndex(screen2.getReference(), (Integer) 3);
checkIndex(screen4.getReference(), (Integer) 4);
}
private void checkIndex(final String screenReference, final Integer expectedIndex) {
Integer actualIndex = screenRepository.findByReference(screenReference).orElseThrow(IllegalStateException::new).getIndex();
Assert.assertEquals(expectedIndex, actualIndex);
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment