// Path: src/main/java/org/vtsukur/spring/rest/market/domain/core/ad/Ad.java
// @Entity
// @Data
// public class Ad implements Identifiable<Long> {
// 
//     @Id
//     @GeneratedValue
//     private Long id;
// 
//     @Column(nullable = false)
//     @Enumerated(EnumType.STRING)
//     private Type type;
// 
//     public enum Type {
// 
//         BUY,
// 
//         SELL
// 
//     }
// 
//     @Column(nullable = false)
//     private BigInteger amount;
// 
//     @Column(nullable = false)
//     @Enumerated(EnumType.STRING)
//     private Currency currency;
// 
//     public enum Currency {
// 
//         USD,
// 
//         EUR
// 
//     }
// 
//     @Column(nullable = false)
//     private BigDecimal rate;
// 
//     @ManyToOne
//     @JoinColumn(nullable = false)
//     private User user;
// 
//     private Location location;
// 
//     @Embeddable
//     @Getter
//     @Setter
//     @NoArgsConstructor
//     @AllArgsConstructor
//     public static class Location {
// 
//         @Column(nullable = false)
//         private String city;
// 
//         private String area;
// 
//     }
// 
//     private String comment;
// 
//     @Lob
//     private LocalDateTime publishedAt;
// 
//     @Column(nullable = false)
//     @Enumerated(EnumType.STRING)
//     private Status status = Status.NEW;
// 
//     public enum Status {
// 
//         NEW,
// 
//         PUBLISHED,
// 
//         EXPIRED
// 
//     }
// 
//     public Ad publish() {
//         if (status == Status.NEW) {
//             publishedAt = LocalDateTime.now();
//             status = Status.PUBLISHED;
//         }
//         else {
//             throw new InvalidAdStateTransitionException("Ad is already published");
//         }
//         return this;
//     }
// 
//     public Ad expire() {
//         if (status == Status.PUBLISHED) {
//             status = Status.EXPIRED;
//         }
//         else {
//             throw new InvalidAdStateTransitionException(
//                     "Ad can be expire only when it is in the " + Status.PUBLISHED + " state");
//         }
//         return this;
//     }
// 
// }
// 
// Path: src/main/java/org/vtsukur/spring/rest/market/domain/core/ad/AdService.java
// @Service
// @Transactional
// public class AdService {
// 
//     @Autowired
//     private AdRepository adRepository;
// 
//     public Ad publish(Long id) throws InvalidAdStateTransitionException {
//         return findDoAndSave(id, Ad::publish);
//     }
// 
//     public Ad expire(Long id) throws InvalidAdStateTransitionException {
//         return findDoAndSave(id, Ad::expire);
//     }
// 
//     private Ad findDoAndSave(Long id, Action action) {
//         Ad foundAd = adRepository.findOne(id);
//         Ad modifiedAd = action.perform(foundAd);
//         return adRepository.save(modifiedAd);
//     }
// 
//     public Ad findOne(Long id) {
//         return adRepository.findOne(id);
//     }
// 
//     @FunctionalInterface
//     private interface Action {
// 
//         Ad perform(Ad ad);
// 
//     }
// 
// }
// 
// Path: src/main/java/org/vtsukur/spring/rest/market/domain/core/ad/InvalidAdStateTransitionException.java
// @ResponseStatus(HttpStatus.CONFLICT)
// public class InvalidAdStateTransitionException extends RuntimeException {
// 
//     public InvalidAdStateTransitionException(String message) {
//         super(message);
//     }
// 
// }


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.rest.webmvc.PersistentEntityResource;
import org.springframework.data.rest.webmvc.PersistentEntityResourceAssembler;
import org.springframework.data.rest.webmvc.RepositoryRestController;
import org.springframework.data.rest.webmvc.ResourceNotFoundException;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.vtsukur.spring.rest.market.domain.core.ad.Ad;
import org.vtsukur.spring.rest.market.domain.core.ad.AdService;
import org.vtsukur.spring.rest.market.domain.core.ad.InvalidAdStateTransitionException;
package org.vtsukur.spring.rest.market.domain.integration.ad;


/**
 * @author volodymyr.tsukur
 */
@RepositoryRestController
public class AdResourceController {

    @Autowired
    private AdService adService;

    @ResponseBody
    @RequestMapping(value = "/ads/{id}/publishing", method = RequestMethod.PUT, produces = "application/hal+json")
    public PersistentEntityResource publish(@PathVariable("id") Long id, PersistentEntityResourceAssembler assembler)
            throws InvalidAdStateTransitionException {
        return assembler.toFullResource(adService.publish(id));
    }

    @RequestMapping(value = "/ads/{id}/publishing", method = RequestMethod.HEAD)
    @ResponseBody
    public void publishHead(@PathVariable("id") Long id) {
        Ad ad = adService.findOne(id);
        if (ad == null || ad.getStatus() != Ad.Status.NEW) {
            throw new ResourceNotFoundException();
        }
    }

    @ResponseBody
    @RequestMapping(value = "/ads/{id}/expiration", method = RequestMethod.PUT, produces = "application/hal+json")
    public PersistentEntityResource expire(@PathVariable("id") Long id, PersistentEntityResourceAssembler assembler)
// Path: src/main/java/org/vtsukur/spring/rest/market/domain/core/ad/Ad.java
// @Entity
// @Data
// public class Ad implements Identifiable<Long> {
// 
//     @Id
//     @GeneratedValue
//     private Long id;
// 
//     @Column(nullable = false)
//     @Enumerated(EnumType.STRING)
//     private Type type;
// 
//     public enum Type {
// 
//         BUY,
// 
//         SELL
// 
//     }
// 
//     @Column(nullable = false)
//     private BigInteger amount;
// 
//     @Column(nullable = false)
//     @Enumerated(EnumType.STRING)
//     private Currency currency;
// 
//     public enum Currency {
// 
//         USD,
// 
//         EUR
// 
//     }
// 
//     @Column(nullable = false)
//     private BigDecimal rate;
// 
//     @ManyToOne
//     @JoinColumn(nullable = false)
//     private User user;
// 
//     private Location location;
// 
//     @Embeddable
//     @Getter
//     @Setter
//     @NoArgsConstructor
//     @AllArgsConstructor
//     public static class Location {
// 
//         @Column(nullable = false)
//         private String city;
// 
//         private String area;
// 
//     }
// 
//     private String comment;
// 
//     @Lob
//     private LocalDateTime publishedAt;
// 
//     @Column(nullable = false)
//     @Enumerated(EnumType.STRING)
//     private Status status = Status.NEW;
// 
//     public enum Status {
// 
//         NEW,
// 
//         PUBLISHED,
// 
//         EXPIRED
// 
//     }
// 
//     public Ad publish() {
//         if (status == Status.NEW) {
//             publishedAt = LocalDateTime.now();
//             status = Status.PUBLISHED;
//         }
//         else {
//             throw new InvalidAdStateTransitionException("Ad is already published");
//         }
//         return this;
//     }
// 
//     public Ad expire() {
//         if (status == Status.PUBLISHED) {
//             status = Status.EXPIRED;
//         }
//         else {
//             throw new InvalidAdStateTransitionException(
//                     "Ad can be expire only when it is in the " + Status.PUBLISHED + " state");
//         }
//         return this;
//     }
// 
// }
// 
// Path: src/main/java/org/vtsukur/spring/rest/market/domain/core/ad/AdService.java
// @Service
// @Transactional
// public class AdService {
// 
//     @Autowired
//     private AdRepository adRepository;
// 
//     public Ad publish(Long id) throws InvalidAdStateTransitionException {
//         return findDoAndSave(id, Ad::publish);
//     }
// 
//     public Ad expire(Long id) throws InvalidAdStateTransitionException {
//         return findDoAndSave(id, Ad::expire);
//     }
// 
//     private Ad findDoAndSave(Long id, Action action) {
//         Ad foundAd = adRepository.findOne(id);
//         Ad modifiedAd = action.perform(foundAd);
//         return adRepository.save(modifiedAd);
//     }
// 
//     public Ad findOne(Long id) {
//         return adRepository.findOne(id);
//     }
// 
//     @FunctionalInterface
//     private interface Action {
// 
//         Ad perform(Ad ad);
// 
//     }
// 
// }
// 
// Path: src/main/java/org/vtsukur/spring/rest/market/domain/core/ad/InvalidAdStateTransitionException.java
// @ResponseStatus(HttpStatus.CONFLICT)
// public class InvalidAdStateTransitionException extends RuntimeException {
// 
//     public InvalidAdStateTransitionException(String message) {
//         super(message);
//     }
// 
// }


// Path: src/main/java/org/vtsukur/spring/rest/market/domain/integration/ad/AdResourceController.java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.rest.webmvc.PersistentEntityResource;
import org.springframework.data.rest.webmvc.PersistentEntityResourceAssembler;
import org.springframework.data.rest.webmvc.RepositoryRestController;
import org.springframework.data.rest.webmvc.ResourceNotFoundException;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.vtsukur.spring.rest.market.domain.core.ad.Ad;
import org.vtsukur.spring.rest.market.domain.core.ad.AdService;
import org.vtsukur.spring.rest.market.domain.core.ad.InvalidAdStateTransitionException;

package org.vtsukur.spring.rest.market.domain.integration.ad;


/**
 * @author volodymyr.tsukur
 */
@RepositoryRestController
public class AdResourceController {

    @Autowired
    private AdService adService;

    @ResponseBody
    @RequestMapping(value = "/ads/{id}/publishing", method = RequestMethod.PUT, produces = "application/hal+json")
    public PersistentEntityResource publish(@PathVariable("id") Long id, PersistentEntityResourceAssembler assembler)
            throws InvalidAdStateTransitionException {
        return assembler.toFullResource(adService.publish(id));
    }

    @RequestMapping(value = "/ads/{id}/publishing", method = RequestMethod.HEAD)
    @ResponseBody
    public void publishHead(@PathVariable("id") Long id) {
        Ad ad = adService.findOne(id);
        if (ad == null || ad.getStatus() != Ad.Status.NEW) {
            throw new ResourceNotFoundException();
        }
    }

    @ResponseBody
    @RequestMapping(value = "/ads/{id}/expiration", method = RequestMethod.PUT, produces = "application/hal+json")
    public PersistentEntityResource expire(@PathVariable("id") Long id, PersistentEntityResourceAssembler assembler)
            throws InvalidAdStateTransitionException {