Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
mojo2012 committed May 13, 2019
2 parents 8d9e7f5 + b460d13 commit baf92aa
Show file tree
Hide file tree
Showing 41 changed files with 1,180 additions and 151 deletions.
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package $package;

import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.DependsOn;

import io.spotnext.core.CoreInit;
import io.spotnext.core.infrastructure.exception.ModuleInitializationException;
import io.spotnext.core.infrastructure.support.init.ModuleInit;

@DependsOn(value = "coreInit")
@SpringBootApplication(scanBasePackages = { "$package" })
public class Init extends ModuleInit {

Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
${AnsiColor.BRIGHT_GREEN}
${AnsiColor.BRIGHT_GREEN} ██████
${AnsiColor.BRIGHT_GREEN} ██████
${AnsiColor.BRIGHT_GREEN} ██████
${AnsiColor.BRIGHT_GREEN} ███████ ███████████ ██████████ █████████
${AnsiColor.BRIGHT_GREEN} █████████ ███████████████ █████ ███████ █████████
${AnsiColor.BRIGHT_GREEN} █████████ █████████████████ ██████ ███████ █████████
${AnsiColor.BRIGHT_GREEN} ██████ ██████ ██████ █████████ ████ ██████
${AnsiColor.BRIGHT_GREEN} ██████ ██████ ███████ █████████ ████ ██████
${AnsiColor.BRIGHT_GREEN} █████████ ██████ ██████████ ██████ ███████ █████████
${AnsiColor.BRIGHT_GREEN} █████████ ██████ ████████ █████ ███████ ████████
${AnsiColor.BRIGHT_GREEN} ███████ ██████ ██████ ██████████ ██████
${AnsiColor.BRIGHT_GREEN} ██████
${AnsiColor.BRIGHT_GREEN} ██████
${AnsiColor.BRIGHT_GREEN} ██████


${AnsiColor.BRIGHT_GREEN} ██████████
${AnsiColor.BRIGHT_GREEN} ██████████████████
${AnsiColor.BRIGHT_GREEN} ██████████ ██████████████████████ ██████ ██████████
${AnsiColor.BRIGHT_GREEN} ████████████ ████████ ███████████ ████████ ███████████████
${AnsiColor.BRIGHT_GREEN} █████████████ ██████████ ██████████ █████████ ██████████████████
${AnsiColor.BRIGHT_GREEN} █████████████ █████████████ █████████ ██████████ ██████████████████
${AnsiColor.BRIGHT_GREEN} ████████ ███████████████ ███████ ████████ ████████ █████
${AnsiColor.BRIGHT_GREEN} ████████ ███████████████ ████████ ████████ ████████ ████
${AnsiColor.BRIGHT_GREEN} █████████████ █████████████ ██████████ ████████ ████████████████
${AnsiColor.BRIGHT_GREEN} █████████████ ██████████ ███████████ ████████ ████████████████
${AnsiColor.BRIGHT_GREEN} █████████████ ████████ ████████████ ████████ ████████████████
${AnsiColor.BRIGHT_GREEN} ████████████ ██████████████████████ ████████ ███████████████
${AnsiColor.BRIGHT_GREEN} ██████████████████
${AnsiColor.BRIGHT_GREEN} ██████████

${groupId}:${artifactId} ${application.version}

29 changes: 29 additions & 0 deletions docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,35 @@ The core system contains the following impex files:
If any of these files exists in the custom project as well (in the correct folder!), it overrides the original files provided by the framework. So if you need to import "users" but don't want to override the default `users.impex`, just add a custom prefix to the impex file name.

### Other Services
#### Cronjob scheduling
Recurring tasks, like importing files from a hotfolder, can easily be implemented using the cronjob infrastructure. A cronjob is composed out of configuration holder (a subtype of `AbstractCronJob`) and a subclass of `AbstractCronJobPerformable`.

The `AbstractCronJob` item type holds various basic configuration settings about the cronjob:

* trigger used for scheduling
* bean id of the `performable`
* current status, eg. RUNNING
* result of the last run
* maximum number of retires in case of an unhandled exception

The code execution takes place in the `performable` which can be configured using the `AbstractCronJob.performable` property. This is just a bean id that will be scheduled and called according to the trigger. The trigger can be configured like a crontab entry `0/30 * * * * *` (= start every 30 seconds).

When the cronjob scheduler executes the `performable` the bean's `public PerformResult perform(AbstractCronJob cronJob)` method is called. In case the processing was successful, a `AbstractCronJobPerformable.SUCCESS` should be returned, otherwise there are also `FAILURE` if the cronjob had to be aborted or `ERROR` if there was an error, but the cronjob could be terminated gracefull.

If a running cronjob is requested to abort (eg. using `io.spotnext.core.infrastructure.scheduling.service.impl.CronJobService.abortCronJob(String)`) or the REST interface, the status `ABORTED` will be returned. A long running cronjob performable should therefore call `io.spotnext.core.infrastructure.scheduling.support.AbstractCronJobPerformable.abortIfRequested()` regularely to check if the cronjob should be aborted.

Cronjobs can easily be setup using ImpEx:
```impex
INSERT CronJobTrigger ; &ref ; second ; minute ; hour ; dayOfMonth ; month ; weekDay ;
; trigger ; 0/30 ; * ; * ; * ; * ; * ;
INSERT_UPDATE NoopCronJob ; uid [ unique = true ] ; performable ; trigger(&ref)
; empty-cronjob ; emptyCronJobPerformable ; trigger
```

> The `emptyCronJobPerformable` has to be implemented!
#### Localization & internationalization

There are two services dealing with this topic:
Expand Down
10 changes: 9 additions & 1 deletion docs/schemas/v2/itemtypes.xsd
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@
<xs:complexType name="ItemType">
<xs:complexContent>
<xs:extension base="JavaType">
<xs:attribute name="typeCode" type="xs:string" use="optional">
<xs:attribute name="typeCode" type="TypeCode" use="optional">
<xs:annotation>
<xs:documentation>
The internal code of the item type, used for
Expand All @@ -193,6 +193,14 @@
</xs:complexContent>
</xs:complexType>


<xs:simpleType name="TypeCode">
<xs:restriction base="xs:string">
<xs:minLength value="1" />
<xs:maxLength value="31" />
</xs:restriction>
</xs:simpleType>

<xs:complexType name="DefaultValue" mixed="true">
<xs:attribute name="value" use="optional" type="xs:string" />
</xs:complexType>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
package io.spotnext.commerce;

import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

import io.spotnext.commerce.facade.CartFacade;
import io.spotnext.commerce.facade.impl.DefaultCartFacade;
import io.spotnext.commerce.service.CartService;
import io.spotnext.commerce.service.impl.DefaultCartService;

/**
* The base spring configuration for this spOt plugin. Can be imported with "@Import(value = CommerceConfiguration.class)".
*/
Expand All @@ -12,5 +18,14 @@
"io.spotnext.commerce.facade" })
@EnableAutoConfiguration
public class CommerceConfiguration {
//

@Bean(name = { "defaultCartService", "cartService" })
CartService cartService() {
return new DefaultCartService();
}

@Bean(name = { "defaultCartFacade", "cartFacade" })
CartFacade cartFacade() {
return new DefaultCartFacade();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package io.spotnext.commerce.facade;

import io.spotnext.itemtype.commerce.order.CartData;
import io.spotnext.itemtype.commerce.order.CartModificationResult;

/**
* <p>
* CheckoutFacade interface.
* </p>
*/
public interface CartFacade {
/**
* Adds the product with the given ID and quantity to the current session cart
*
* @param productId
* @param quantity
*/
CartModificationResult addToCart(String productId, int quantity);

/**
* Returns the current session cart (creates a new one, if it doesn't exist)
*
* @return can never be null
*/
CartData getCurrentCart();

/**
* @return the amount of all items in the current session cart.
*/
int getCurrentAmountOfCartItems();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package io.spotnext.commerce.facade.impl;

import static io.spotnext.support.util.MiscUtil.$;

import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;

import io.spotnext.commerce.facade.CartFacade;
import io.spotnext.commerce.service.CartService;
import io.spotnext.itemtype.commerce.enumeration.CartModificationOperation;
import io.spotnext.itemtype.commerce.order.AbstractOrderEntry;
import io.spotnext.itemtype.commerce.order.AbstractOrderEntryData;
import io.spotnext.itemtype.commerce.order.Cart;
import io.spotnext.itemtype.commerce.order.CartData;
import io.spotnext.itemtype.commerce.order.CartEntryData;
import io.spotnext.itemtype.commerce.order.CartModification;
import io.spotnext.itemtype.commerce.order.CartModificationResult;
import io.spotnext.itemtype.commerce.order.ProductData;

public class DefaultCartFacade extends AbstractFacade implements CartFacade {

@Autowired
@Qualifier("cartService")
private CartService cartService;

@Override
public CartModificationResult addToCart(String productId, int quantity) {
final Optional<Cart> cart = cartService.getSessionCart(true);
final Cart currentCart = cart.get();

final CartModification modification = new CartModification();
modification.setCartId(currentCart.getUid());
modification.setOperation(CartModificationOperation.ADD_ENTRY);
modification.setProductId(productId);
modification.setQuantity(quantity);

return cartService.updateCart(modification);
}

@Override
public CartData getCurrentCart() {
return convertCart(cartService.getSessionCart(true).get());
}

private CartData convertCart(Cart cart) {
CartData cartData = new CartData();

cartData.setUid(cart.getUid());
cartData.setEntries((List<AbstractOrderEntryData>) cart.getEntries().stream().map(this::convertEntry).collect(Collectors.toList()));

return cartData;
}

private AbstractOrderEntryData convertEntry(AbstractOrderEntry entry) {
CartEntryData data = new CartEntryData();

data.setEntryNumber(entry.getEntryNumber());
data.setQuantity(entry.getQuantity());

ProductData product = new ProductData();
product.setUid(entry.getProduct().getUid());
product.setName($(() -> entry.getProduct().getName().get(), null));
product.setDescription($(() -> entry.getProduct().getDescription().get(), null));

data.setProduct(product);

return data;
}

@Override
public int getCurrentAmountOfCartItems() {
return getCurrentCart().getEntries().stream().mapToInt(e -> e.getQuantity()).sum();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import io.spotnext.commerce.exception.NoSuchCartEntryException;
Expand All @@ -31,7 +30,6 @@
import io.spotnext.itemtype.core.user.User;
import io.spotnext.itemtype.core.user.UserGroup;

@Service
public class DefaultCartService extends AbstractService implements CartService {

@Autowired
Expand Down Expand Up @@ -163,20 +161,21 @@ public CartModificationResult updateCart(CartModification modificationData) thro
CartModificationResult result = new CartModificationResult();

switch (modificationData.getOperation()) {
case ADD_ENTRY:
if (modificationData.getQuantity() > 0 && entry.isEmpty()) {
addModifiedCartEntries(result, addToCart(cart, modificationData.getProductId(), modificationData.getQuantity()));
}

// fall through because quantity 0 also means delete!
case UPDATE_ENTRY:
if (modificationData.getQuantity() > 0 && entry.isPresent()) {
addModifiedCartEntries(result,
updateCart(cart, entry.get().getEntryNumber(), entry.get().getProduct().getUid(), modificationData.getQuantity()));
int newQuantity = entry.get().getQuantity() + modificationData.getQuantity();

addModifiedCartEntries(result, updateCart(cart, entry.get().getEntryNumber(), entry.get().getProduct().getUid(), newQuantity));
break;
}

// fall through in case an entry number was specified
case ADD_ENTRY:
if (modificationData.getQuantity() > 0) {
addModifiedCartEntries(result, addToCart(cart, modificationData.getProductId(), modificationData.getQuantity()));
}

// fall through because quantity 0 also means delete!
case REMOVE_ENTRY:
if (entry.isPresent()) {
addModifiedCartEntries(result, removeFromCart(cart, entry.get().getEntryNumber()));
Expand Down
36 changes: 32 additions & 4 deletions spot-commerce-base/src/main/resources/commerce-base-itemtypes.xml
Original file line number Diff line number Diff line change
Expand Up @@ -76,12 +76,25 @@
<property name="discounts" type="DiscountDataList" />
</properties>
</bean>

<bean name="OrderEntryData" extends="AbstractOrderEntryData" package="io.spotnext.itemtype.commerce.order">
<properties>
</properties>
</bean>

<bean name="CartEntryData" extends="AbstractOrderEntryData" package="io.spotnext.itemtype.commerce.order">
<properties>
</properties>
</bean>


<bean name="ProductData" package="io.spotnext.itemtype.commerce.order">
<properties>
<property name="uid" type="String" />
<property name="name" type="int" />
<property name="description" type="ProductData" />
<property name="name" type="String" />
<property name="description" type="String" />
<property name="price" type="double" />
<property name="strikethroughPrice" type="double" />
</properties>
</bean>

Expand Down Expand Up @@ -159,11 +172,18 @@

<type name="Category" package="io.spotnext.itemtype.commerce.catalog" extends="CatalogItem">
<description>Categories are used to group products.</description>
<properties>
<property name="name" type="LocalizedString" />
<property name="description" type="LocalizedString" />
</properties>
</type>

<type name="Manufacturer" package="io.spotnext.itemtype.commerce.catalog" extends="UniqueIdItem">
<properties>
<property name="name" type="LocalizedString">
<description>The localized name of the manufacturer.</description>
</property>
<property name="description" type="LocalizedString">
</property>
<property name="logo" type="ImageMedia" />
</properties>
</type>

Expand Down Expand Up @@ -367,6 +387,14 @@
<source cardinality="many" itemType="Category" mappedBy="superCategories" />
<target cardinality="many" itemType="Category" mappedBy="subCategories" />
</relation>

<relation name="Product2Manufacturer">
<description>The categories the product is referenced by.</description>
<source cardinality="many" itemType="Product" mappedBy="products" />
<target cardinality="one" itemType="Manufacturer" mappedBy="manufacturer">
<description>The manufacturer of the product.</description>
</target>
</relation>

<relation name="AbstractOrder2AbstractOrderEntry">
<description>The categories the product is referenced by.</description>
Expand Down
Loading

0 comments on commit baf92aa

Please sign in to comment.