<|start_filename|>src/test/java/org/socialsignin/spring/data/dynamodb/repository/query/DynamoDBQueryLookupStrategyTest.java<|end_filename|>
/**
 * Copyright © 2018 spring-data-dynamodb (https://github.com/derjust/spring-data-dynamodb)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.socialsignin.spring.data.dynamodb.repository.query;

import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.socialsignin.spring.data.dynamodb.core.DynamoDBOperations;
import org.springframework.data.repository.query.QueryLookupStrategy;
import org.springframework.data.repository.query.QueryLookupStrategy.Key;

import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertSame;

@RunWith(MockitoJUnitRunner.class)
public class DynamoDBQueryLookupStrategyTest {
	@Rule
	public ExpectedException expectedException = ExpectedException.none();
	@Mock
	private DynamoDBOperations dynamoDBOperations;

	@Test
	public void testCreate() {
		QueryLookupStrategy actual;
		actual = DynamoDBQueryLookupStrategy.create(dynamoDBOperations, Key.CREATE);
		assertNotNull(actual);

		actual = DynamoDBQueryLookupStrategy.create(dynamoDBOperations, Key.CREATE_IF_NOT_FOUND);
		assertNotNull(actual);
	}

	@Test
	public void testNull() {
		QueryLookupStrategy actualNull = DynamoDBQueryLookupStrategy.create(dynamoDBOperations, null);
		QueryLookupStrategy actualCreate = DynamoDBQueryLookupStrategy.create(dynamoDBOperations, Key.CREATE);

		assertSame(actualNull.getClass(), actualCreate.getClass());
	}

	@Test
	public void testDeclaredQuery() {
		expectedException.expect(IllegalArgumentException.class);
		expectedException.expectMessage("Unsupported query lookup strategy USE_DECLARED_QUERY!");

		DynamoDBQueryLookupStrategy.create(dynamoDBOperations, Key.USE_DECLARED_QUERY);
	}

}


<|start_filename|>src/main/java/org/socialsignin/spring/data/dynamodb/repository/support/FieldAndGetterReflectionEntityInformation.java<|end_filename|>
/**
 * Copyright © 2018 spring-data-dynamodb (https://github.com/derjust/spring-data-dynamodb)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.socialsignin.spring.data.dynamodb.repository.support;

import org.springframework.data.repository.core.support.AbstractEntityInformation;
import org.springframework.lang.NonNull;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

/**
 * {@link org.springframework.data.repository.core.EntityInformation}
 * implementation that inspects getters for an annotation and invokes this
 * getter's value to retrieve the id.
 *
 * @author <NAME>
 * @author <NAME>
 */
public class FieldAndGetterReflectionEntityInformation<T, ID> extends AbstractEntityInformation<T, ID> {

	protected Method method;
	private Field field;

	/**
	 * Creates a new {@link FieldAndGetterReflectionEntityInformation} inspecting
	 * the given domain class for a getter carrying the given annotation.
	 *
	 * @param domainClass
	 *            must not be {@literal null}.
	 * @param annotation
	 *            must not be {@literal null}.
	 */
	public FieldAndGetterReflectionEntityInformation(@NonNull Class<T> domainClass,
			@NonNull final Class<? extends Annotation> annotation) {

		super(domainClass);
		Assert.notNull(annotation, "annotation must not be null!");

		ReflectionUtils.doWithMethods(domainClass, (method) -> {
			if (method.getAnnotation(annotation) != null) {
				this.method = method;
				return;
			}
		});

		if (method == null) {
			field = null;
			ReflectionUtils.doWithFields(domainClass, (field) -> {
				if (field.getAnnotation(annotation) != null) {
					this.field = field;
					return;
				}
			});
		}

		Assert.isTrue(this.method != null || this.field != null,
				String.format("No field or method annotated with %s found!", annotation.toString()));
		Assert.isTrue(this.method == null || this.field == null,
				String.format("Both field and method annotated with %s found!", annotation.toString()));

		if (method != null) {
			ReflectionUtils.makeAccessible(method);
		}
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see org.springframework.data.repository.core.EntityInformation#getId(java
	 * .lang.Object)
	 */
	@Override
	@SuppressWarnings("unchecked")
	public ID getId(T entity) {
		if (method != null) {
			return entity == null ? null : (ID) ReflectionUtils.invokeMethod(method, entity);
		} else {
			return entity == null ? null : (ID) ReflectionUtils.getField(field, entity);
		}
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see org.springframework.data.repository.core.EntityInformation#getIdType()
	 */
	@Override
	@SuppressWarnings("unchecked")
	public Class<ID> getIdType() {
		return (Class<ID>) (method != null ? method.getReturnType() : field.getType());
	}
}


<|start_filename|>src/main/java/org/socialsignin/spring/data/dynamodb/config/EnableDynamoDBAuditing.java<|end_filename|>
/**
 * Copyright © 2018 spring-data-dynamodb (https://github.com/derjust/spring-data-dynamodb)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.socialsignin.spring.data.dynamodb.config;

import org.springframework.context.annotation.Import;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * Annotation to enable auditing in DynamoDB via annotation configuration.
 *
 * @author <NAME>
 */
@Inherited
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(DynamoDBAuditingRegistrar.class)
public @interface EnableDynamoDBAuditing {

	/**
	 * @return Configures the {@link org.springframework.data.domain.AuditorAware}
	 *         bean to be used to lookup the current principal.
	 */
	String auditorAwareRef() default "";

	/**
	 * @return Configures whether the creation and modification dates are set.
	 *         Defaults to {@literal true}.
	 */
	boolean setDates() default true;

	/**
	 * @return Configures whether the entity shall be marked as modified on
	 *         creation. Defaults to {@literal true}.
	 */
	boolean modifyOnCreate() default true;

	/**
	 * @return Configures a
	 *         {@link org.springframework.data.auditing.DateTimeProvider} bean name
	 *         that allows customizing the {@link org.joda.time.DateTime} to be used
	 *         for setting creation and modification dates.
	 */
	String dateTimeProviderRef() default "";
}

<|start_filename|>src/test/java/org/socialsignin/spring/data/dynamodb/mapping/event/LoggingEventListenerTest.java<|end_filename|>
/**
 * Copyright © 2018 spring-data-dynamodb (https://github.com/derjust/spring-data-dynamodb)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.socialsignin.spring.data.dynamodb.mapping.event;

import com.amazonaws.services.dynamodbv2.datamodeling.PaginatedQueryList;
import com.amazonaws.services.dynamodbv2.datamodeling.PaginatedScanList;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.socialsignin.spring.data.dynamodb.domain.sample.User;
import uk.org.lidalia.slf4jext.Level;
import uk.org.lidalia.slf4jtest.TestLogger;
import uk.org.lidalia.slf4jtest.TestLoggerFactory;

import java.util.ArrayList;
import java.util.List;

import static java.util.Arrays.asList;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.when;
import static uk.org.lidalia.slf4jtest.LoggingEvent.trace;

@RunWith(MockitoJUnitRunner.class)
public class LoggingEventListenerTest {

	private final TestLogger logger = TestLoggerFactory.getTestLogger(LoggingEventListener.class);
	private final User sampleEntity = new User();
	@Mock
	private PaginatedQueryList<User> sampleQueryList;
	@Mock
	private PaginatedScanList<User> sampleScanList;

	private LoggingEventListener underTest;

	@Before
	public void setUp() {
		underTest = new LoggingEventListener();

		logger.setEnabledLevels(Level.TRACE);

		List<User> queryList = new ArrayList<>();
		queryList.add(sampleEntity);
		when(sampleQueryList.stream()).thenReturn(queryList.stream());
		when(sampleScanList.stream()).thenReturn(queryList.stream());
	}

	@After
	public void clearLoggers() {
		TestLoggerFactory.clear();
	}

	@Test
	public void testAfterDelete() {
		underTest.onApplicationEvent(new AfterDeleteEvent<>(sampleEntity));

		assertThat(logger.getLoggingEvents(), is(asList(trace("onAfterDelete: {}", sampleEntity))));
	}

	@Test
	public void testAfterLoad() {
		underTest.onApplicationEvent(new AfterLoadEvent<>(sampleEntity));

		assertThat(logger.getLoggingEvents(), is(asList(trace("onAfterLoad: {}", sampleEntity))));
	}

	@Test
	public void testAfterQuery() {
		underTest.onApplicationEvent(new AfterQueryEvent<>(sampleQueryList));

		assertThat(logger.getLoggingEvents(), is(asList(trace("onAfterQuery: {}", sampleEntity))));
	}

	@Test
	public void testAfterSave() {
		underTest.onApplicationEvent(new AfterSaveEvent<>(sampleEntity));

		assertThat(logger.getLoggingEvents(), is(asList(trace("onAfterSave: {}", sampleEntity))));
	}

	@Test
	public void testAfterScan() {
		underTest.onApplicationEvent(new AfterScanEvent<>(sampleScanList));

		assertThat(logger.getLoggingEvents(), is(asList(trace("onAfterScan: {}", sampleEntity))));
	}

	@Test
	public void testBeforeDelete() {
		underTest.onApplicationEvent(new BeforeDeleteEvent<>(sampleEntity));

		assertThat(logger.getLoggingEvents(), is(asList(trace("onBeforeDelete: {}", sampleEntity))));
	}

	@Test
	public void testBeforeSave() {
		underTest.onApplicationEvent(new BeforeSaveEvent<>(sampleEntity));

		assertThat(logger.getLoggingEvents(), is(asList(trace("onBeforeSave: {}", sampleEntity))));
	}

}


<|start_filename|>src/main/java/org/socialsignin/spring/data/dynamodb/mapping/DynamoDBPersistentPropertyImpl.java<|end_filename|>
/**
 * Copyright © 2018 spring-data-dynamodb (https://github.com/derjust/spring-data-dynamodb)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.socialsignin.spring.data.dynamodb.mapping;

import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBHashKey;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBIgnore;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBVersionAttribute;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.Reference;
import org.springframework.data.annotation.Transient;
import org.springframework.data.mapping.Association;
import org.springframework.data.mapping.model.AnnotationBasedPersistentProperty;
import org.springframework.data.mapping.model.Property;
import org.springframework.data.mapping.model.SimpleTypeHolder;

import java.lang.annotation.Annotation;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

/**
 * {@link DynamoDBPersistentProperty} implementation
 * 
 * @author <NAME>
 * @author <NAME>
 */
class DynamoDBPersistentPropertyImpl extends AnnotationBasedPersistentProperty<DynamoDBPersistentProperty>
		implements
			DynamoDBPersistentProperty {

	private static final Collection<Class<? extends Annotation>> ASSOCIATION_ANNOTATIONS;
	private static final Collection<Class<? extends Annotation>> ID_ANNOTATIONS;

	static {

		Set<Class<? extends Annotation>> annotations = new HashSet<Class<? extends Annotation>>();

		annotations.add(Reference.class); // Reference not yet supported
		ASSOCIATION_ANNOTATIONS = Collections.unmodifiableSet(annotations);

		annotations = new HashSet<>();
		annotations.add(Id.class);
		annotations.add(DynamoDBHashKey.class);
		ID_ANNOTATIONS = annotations;
	}

	/**
	 * Creates a new {@link DynamoDBPersistentPropertyImpl}
	 * 
	 * @param property
	 *            must not be {@literal null}.
	 * @param owner
	 *            must not be {@literal null}.
	 * @param simpleTypeHolder
	 *            must not be {@literal null}.
	 */

	public DynamoDBPersistentPropertyImpl(Property property, DynamoDBPersistentEntityImpl<?> owner,
			SimpleTypeHolder simpleTypeHolder) {
		super(property, owner, simpleTypeHolder);
	}

	@Override
	public boolean isWritable() {
		return !isAnnotationPresent(DynamoDBIgnore.class);
	}

	public boolean isHashKeyProperty() {
		return isAnnotationPresent(DynamoDBHashKey.class);
	}

	public boolean isCompositeIdProperty() {
		return isAnnotationPresent(Id.class);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.springframework.data.mapping.model.AnnotationBasedPersistentProperty
	 * #isIdProperty()
	 */
	@Override
	public boolean isIdProperty() {

		for (Class<? extends Annotation> annotation : ID_ANNOTATIONS) {
			if (isAnnotationPresent(annotation)) {
				return true;
			}
		}

		return false;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.springframework.data.mapping.model.AbstractPersistentProperty#isEntity ()
	 */
	// @Override

	public boolean isEntity() {

		return isAnnotationPresent(Reference.class);// Reference not Yet
		// Supported
		// return propertyDescriptor != null
		// && propertyDescriptor.getPropertyType().isAnnotationPresent(
		// DynamoDBTable.class);

		// return false;

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.springframework.data.mapping.model.AnnotationBasedPersistentProperty
	 * #isAssociation()
	 */
	@Override
	public boolean isAssociation() {

		for (Class<? extends Annotation> annotationType : ASSOCIATION_ANNOTATIONS) {
			if (findAnnotation(annotationType) != null) {
				// No query lookup yet supported ( see
				// Repositories.getPersistentEntity(..) )
				// return !information.isCollectionLike();
				return true;
			}
		}

		return false;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.springframework.data.mapping.model.AnnotationBasedPersistentProperty
	 * #isTransient()
	 */
	@Override
	public boolean isTransient() {
		return isAnnotationPresent(Transient.class) || super.isTransient() || isAnnotationPresent(DynamoDBIgnore.class);
	}

	@Override
	public boolean isVersionProperty() {
		return super.isVersionProperty() || isAnnotationPresent(DynamoDBVersionAttribute.class);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.springframework.data.mapping.model.AbstractPersistentProperty#
	 * createAssociation()
	 */
	@Override
	protected Association<DynamoDBPersistentProperty> createAssociation() {
		return new Association<DynamoDBPersistentProperty>(this, null);
	}
}


<|start_filename|>src/main/java/org/socialsignin/spring/data/dynamodb/marshaller/Date2EpocheDynamoDBMarshaller.java<|end_filename|>
/**
 * Copyright © 2018 spring-data-dynamodb (https://github.com/derjust/spring-data-dynamodb)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.socialsignin.spring.data.dynamodb.marshaller;

import java.text.DateFormat;
import java.text.FieldPosition;
import java.text.ParsePosition;
import java.util.Date;

public class Date2EpocheDynamoDBMarshaller extends DateDynamoDBMarshaller {

	private static final class EpcoheDateFormat extends DateFormat {
		private static final long serialVersionUID = 2969564523817434535L;

		@Override
		public StringBuffer format(Date date, StringBuffer toAppendTo, FieldPosition fieldPosition) {
			long epoche = date.getTime();
			toAppendTo.append(epoche);
			return toAppendTo;
		}

		@Override
		public Date parse(String source, ParsePosition pos) {
			long epoche = Long.parseLong(source);
			pos.setIndex(source.length());
			return new Date(epoche);
		}

	};

	@Override
	public DateFormat getDateFormat() {
		return new EpcoheDateFormat();
	}

}


<|start_filename|>src/main/java/org/socialsignin/spring/data/dynamodb/config/DynamoDBAuditingRegistrar.java<|end_filename|>
/**
 * Copyright © 2018 spring-data-dynamodb (https://github.com/derjust/spring-data-dynamodb)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.socialsignin.spring.data.dynamodb.config;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.socialsignin.spring.data.dynamodb.mapping.DynamoDBMappingContext;
import org.socialsignin.spring.data.dynamodb.mapping.event.AuditingEventListener;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.data.auditing.IsNewAwareAuditingHandler;
import org.springframework.data.auditing.config.AuditingBeanDefinitionRegistrarSupport;
import org.springframework.data.auditing.config.AuditingConfiguration;
import org.springframework.data.config.ParsingUtils;
import org.springframework.util.Assert;

import java.lang.annotation.Annotation;

import static org.socialsignin.spring.data.dynamodb.config.BeanNames.MAPPING_CONTEXT_BEAN_NAME;
import static org.springframework.beans.factory.config.BeanDefinition.ROLE_INFRASTRUCTURE;

/**
 * {@link org.springframework.context.annotation.ImportBeanDefinitionRegistrar}
 * to enable {@link EnableDynamoDBAuditing} annotation.
 *
 * @author <NAME>
 */
class DynamoDBAuditingRegistrar extends AuditingBeanDefinitionRegistrarSupport {

	private static Logger LOGGER = LoggerFactory.getLogger(DynamoDBAuditingRegistrar.class);

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.springframework.data.auditing.config.
	 * AuditingBeanDefinitionRegistrarSupport#getAnnotation()
	 */
	@Override
	protected Class<? extends Annotation> getAnnotation() {
		return EnableDynamoDBAuditing.class;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.springframework.data.auditing.config.
	 * AuditingBeanDefinitionRegistrarSupport#getAuditingHandlerBeanName()
	 */
	@Override
	protected String getAuditingHandlerBeanName() {
		return "dynamoDBAuditingHandler";
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.springframework.data.auditing.config.
	 * AuditingBeanDefinitionRegistrarSupport#registerBeanDefinitions(org.
	 * springframework.core.type.AnnotationMetadata,
	 * org.springframework.beans.factory.support.BeanDefinitionRegistry)
	 */
	@Override
	public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry registry) {
		LOGGER.trace("registerBeanDefinitions");
		Assert.notNull(annotationMetadata, "AnnotationMetadata must not be null!");
		Assert.notNull(registry, "BeanDefinitionRegistry must not be null!");

		defaultDependenciesIfNecessary(registry, annotationMetadata);
		super.registerBeanDefinitions(annotationMetadata, registry);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.springframework.data.auditing.config.
	 * AuditingBeanDefinitionRegistrarSupport#getAuditHandlerBeanDefinitionBuilder(
	 * org.springframework.data.auditing.config.AuditingConfiguration)
	 */
	@Override
	protected BeanDefinitionBuilder getAuditHandlerBeanDefinitionBuilder(AuditingConfiguration configuration) {
		LOGGER.trace("getAuditHandlerBeanDefinitionBuilder");
		Assert.notNull(configuration, "AuditingConfiguration must not be null!");

		BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(IsNewAwareAuditingHandler.class);
		builder.addConstructorArgReference(MAPPING_CONTEXT_BEAN_NAME);
		return configureDefaultAuditHandlerAttributes(configuration, builder);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.springframework.data.auditing.config.
	 * AuditingBeanDefinitionRegistrarSupport#registerAuditListener(org.
	 * springframework.beans.factory.config.BeanDefinition,
	 * org.springframework.beans.factory.support.BeanDefinitionRegistry)
	 */
	@Override
	protected void registerAuditListenerBeanDefinition(BeanDefinition auditingHandlerDefinition,
			BeanDefinitionRegistry registry) {
		LOGGER.trace("registerAuditListenerBeanDefinition");
		Assert.notNull(auditingHandlerDefinition, "BeanDefinition must not be null!");
		Assert.notNull(registry, "BeanDefinitionRegistry must not be null!");

		BeanDefinitionBuilder listenerBeanDefinitionBuilder = BeanDefinitionBuilder
				.rootBeanDefinition(AuditingEventListener.class);
		listenerBeanDefinitionBuilder.addConstructorArgValue(
				ParsingUtils.getObjectFactoryBeanDefinition(getAuditingHandlerBeanName(), registry));

		registerInfrastructureBeanWithId(listenerBeanDefinitionBuilder.getBeanDefinition(),
				AuditingEventListener.class.getName(), registry);
	}

	/**
	 * Register default bean definitions for a {@link DynamoDBMappingContext} and an
	 * {@link org.springframework.data.support.IsNewStrategyFactory} in case we
	 * don't find beans with the assumed names in the registry.
	 *
	 * @param registry
	 *            the {@link BeanDefinitionRegistry} to use to register the
	 *            components into.
	 * @param source
	 *            the source which the registered components shall be registered
	 *            with
	 */
	private void defaultDependenciesIfNecessary(BeanDefinitionRegistry registry, Object source) {
		LOGGER.trace("defaultDependenciesIfNecessary. source:{}", source);
		LOGGER.trace("is registry.containsBeanDefinition {} {}", MAPPING_CONTEXT_BEAN_NAME,
				registry.containsBeanDefinition(MAPPING_CONTEXT_BEAN_NAME));
		if (!registry.containsBeanDefinition(MAPPING_CONTEXT_BEAN_NAME)) {

			RootBeanDefinition definition = new RootBeanDefinition(DynamoDBMappingContext.class);
			definition.setRole(ROLE_INFRASTRUCTURE);
			definition.setSource(source);

			registry.registerBeanDefinition(MAPPING_CONTEXT_BEAN_NAME, definition);
		}
	}
}

<|start_filename|>src/main/java/org/socialsignin/spring/data/dynamodb/query/ScanExpressionCountQuery.java<|end_filename|>
/**
 * Copyright © 2018 spring-data-dynamodb (https://github.com/derjust/spring-data-dynamodb)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.socialsignin.spring.data.dynamodb.query;

import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBScanExpression;
import org.socialsignin.spring.data.dynamodb.core.DynamoDBOperations;
import org.springframework.util.Assert;

public class ScanExpressionCountQuery<T> extends AbstractSingleEntityQuery<Long> implements Query<Long> {

	private DynamoDBScanExpression scanExpression;

	private Class<T> domainClass;

	private boolean pageQuery;

	public ScanExpressionCountQuery(DynamoDBOperations dynamoDBOperations, Class<T> clazz,
			DynamoDBScanExpression scanExpression, boolean pageQuery) {
		super(dynamoDBOperations, Long.class);
		this.scanExpression = scanExpression;
		this.domainClass = clazz;
		this.pageQuery = pageQuery;
	}

	@Override
	public Long getSingleResult() {
		assertScanCountEnabled(isScanCountEnabled());
		return Long.valueOf(dynamoDBOperations.count(domainClass, scanExpression));
	}

	public void assertScanCountEnabled(boolean scanCountEnabled) {
		if (pageQuery) {
			Assert.isTrue(scanCountEnabled, "Scanning for the total counts for this query is not enabled.  "
					+ "To enable annotate your repository method with @EnableScanCount, or "
					+ "enable scanning for all repository methods by annotating your repository interface with @EnableScanCount.  This total count is required to serve this Page query - if total counts are not desired an alternative approach could be to replace the Page query with a Slice query ");

		} else {
			Assert.isTrue(scanCountEnabled, "Scanning for counts for this query is not enabled.  "
					+ "To enable annotate your repository method with @EnableScanCount, or "
					+ "enable scanning for all repository methods by annotating your repository interface with @EnableScanCount");
		}
	}

}


<|start_filename|>src/test/java/org/socialsignin/spring/data/dynamodb/repository/support/EnableScanAnnotationPermissionTest.java<|end_filename|>
/**
 * Copyright © 2018 spring-data-dynamodb (https://github.com/derjust/spring-data-dynamodb)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.socialsignin.spring.data.dynamodb.repository.support;

import org.junit.Before;
import org.junit.Test;
import org.socialsignin.spring.data.dynamodb.domain.sample.User;
import org.socialsignin.spring.data.dynamodb.repository.EnableScan;
import org.socialsignin.spring.data.dynamodb.repository.EnableScanCount;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;

import java.util.List;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

public class EnableScanAnnotationPermissionTest {

	@EnableScan
	public interface SampleRepository {
		List<User> findAll();
	}

	public interface SampleNoScanRepository {
		List<User> findAll();
	}

	@EnableScanCount
	public interface SampleMethodRepository {

		@EnableScan
		int someMethodThatsIgnored();

		@EnableScan
		int count();

		@EnableScan
		void deleteAll();

		@EnableScan
		List<User> findAll();
		@EnableScan
		Page<User> findAll(Pageable pageable);

	}

	@Before
	public void setUp() {

	}

	@Test
	public void testSampleRepository() {
		EnableScanAnnotationPermissions underTest = new EnableScanAnnotationPermissions(SampleRepository.class);

		assertTrue(underTest.isCountUnpaginatedScanEnabled());
		assertTrue(underTest.isDeleteAllUnpaginatedScanEnabled());
		assertTrue(underTest.isFindAllPaginatedScanEnabled());
		assertFalse(underTest.isFindAllUnpaginatedScanCountEnabled());
		assertTrue(underTest.isFindAllUnpaginatedScanEnabled());
	}

	@Test
	public void testSampleNoScanRepository() {
		EnableScanAnnotationPermissions underTest = new EnableScanAnnotationPermissions(SampleMethodRepository.class);

		assertTrue(underTest.isCountUnpaginatedScanEnabled());
		assertTrue(underTest.isDeleteAllUnpaginatedScanEnabled());
		assertTrue(underTest.isFindAllPaginatedScanEnabled());
		assertTrue(underTest.isFindAllUnpaginatedScanCountEnabled());
		assertTrue(underTest.isFindAllUnpaginatedScanEnabled());
	}

	@Test
	public void testSampleMethodRepository() {
		EnableScanAnnotationPermissions underTest = new EnableScanAnnotationPermissions(SampleNoScanRepository.class);

		assertFalse(underTest.isCountUnpaginatedScanEnabled());
		assertFalse(underTest.isDeleteAllUnpaginatedScanEnabled());
		assertFalse(underTest.isFindAllPaginatedScanEnabled());
		assertFalse(underTest.isFindAllUnpaginatedScanCountEnabled());
		assertFalse(underTest.isFindAllUnpaginatedScanEnabled());
	}

}


<|start_filename|>src/test/java/org/socialsignin/spring/data/dynamodb/marshaller/Date2IsoDynamoDBMarshallerTest.java<|end_filename|>
/**
 * Copyright © 2018 spring-data-dynamodb (https://github.com/derjust/spring-data-dynamodb)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.socialsignin.spring.data.dynamodb.marshaller;

import org.junit.Before;
import org.junit.Test;

import java.util.Date;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;

public class Date2IsoDynamoDBMarshallerTest {

	private Date2IsoDynamoDBMarshaller underTest;

	@Before
	public void setUp() {
		underTest = new Date2IsoDynamoDBMarshaller();
	}

	@Test
	public void testNullMarshall() {
		String actual = underTest.marshall(null);

		assertNull(actual);
	}

	@Test
	public void testMarshall() {
		assertEquals("1970-01-01T00:00:00.000Z", underTest.marshall(new Date(0)));
		assertEquals("1970-01-01T00:00:00.000Z", underTest.convert(new Date(0)));
	}

	@Test
	public void testUnmarshallNull() {
		Date actual = underTest.unmarshall(Date.class, null);

		assertNull(actual);
	}

	@Test
	public void testUnmarshall() {
		assertEquals(new Date(0), underTest.unmarshall(Date.class, "1970-01-01T00:00:00.000Z"));
		assertEquals(new Date(0), underTest.unconvert("1970-01-01T00:00:00.000Z"));
	}

	@Test(expected = RuntimeException.class)
	public void testUnmarshallGarbage() {
		underTest.unmarshall(Date.class, "something");
	}
}


<|start_filename|>src/test/java/org/socialsignin/spring/data/dynamodb/query/ScanExpressionCountQueryTest.java<|end_filename|>
/**
 * Copyright © 2018 spring-data-dynamodb (https://github.com/derjust/spring-data-dynamodb)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.socialsignin.spring.data.dynamodb.query;

import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBScanExpression;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.socialsignin.spring.data.dynamodb.core.DynamoDBOperations;
import org.socialsignin.spring.data.dynamodb.domain.sample.User;

import static org.junit.Assert.assertTrue;

@RunWith(MockitoJUnitRunner.class)
public class ScanExpressionCountQueryTest {

	@Rule
	public ExpectedException expectedException = ExpectedException.none();

	@Mock
	private DynamoDBOperations dynamoDBOperations;
	@Mock
	private DynamoDBScanExpression scanExpression;

	private ScanExpressionCountQuery<User> underTest;

	@Test
	public void testScanCountEnabledTrueTrue() {
		underTest = new ScanExpressionCountQuery<>(dynamoDBOperations, User.class, scanExpression, true);

		underTest.assertScanCountEnabled(true);

		assertTrue(true);
	}

	@Test
	public void testScanCountEnabledTrueFalse() {
		expectedException.expect(IllegalArgumentException.class);
		expectedException.expectMessage("Scanning for the total counts for this query is not enabled. "
				+ " To enable annotate your repository method with @EnableScanCount, or enable scanning for all repository methods by annotating your repository interface with @EnableScanCount. "
				+ " This total count is required to serve this Page query - if total counts are not desired an alternative approach could be to replace the Page query with a Slice query ");
		underTest = new ScanExpressionCountQuery<>(dynamoDBOperations, User.class, scanExpression, true);

		underTest.assertScanCountEnabled(false);
	}

	@Test
	public void testScanCountEnabledFalseTrue() {
		underTest = new ScanExpressionCountQuery<>(dynamoDBOperations, User.class, scanExpression, false);

		underTest.assertScanCountEnabled(true);

		assertTrue(true);
	}

	@Test
	public void testScanCountEnabledFalseFalse() {
		expectedException.expect(IllegalArgumentException.class);
		expectedException.expectMessage("Scanning for counts for this query is not enabled. "
				+ " To enable annotate your repository method with @EnableScanCount, or enable scanning for all repository methods by annotating your repository interface with @EnableScanCount");
		underTest = new ScanExpressionCountQuery<>(dynamoDBOperations, User.class, scanExpression, false);

		underTest.assertScanCountEnabled(false);
	}
}


<|start_filename|>src/main/java/org/socialsignin/spring/data/dynamodb/mapping/event/ValidatingDynamoDBEventListener.java<|end_filename|>
/**
 * Copyright © 2018 spring-data-dynamodb (https://github.com/derjust/spring-data-dynamodb)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.socialsignin.spring.data.dynamodb.mapping.event;
/*
 * Copyright 2012 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.Validator;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * javax.validation dependant entities validator. When it is registered as
 * Spring component its automatically invoked before entities are saved in
 * database.
 * 
 * @author <NAME>
 * @author <NAME>
 */
public class ValidatingDynamoDBEventListener extends AbstractDynamoDBEventListener<Object> {

	private static final Logger LOG = LoggerFactory.getLogger(ValidatingDynamoDBEventListener.class);

	private final Validator validator;

	/**
	 * Creates a new {@link ValidatingDynamoDBEventListener} using the given
	 * {@link Validator}.
	 * 
	 * @param validator
	 *            must not be {@literal null}.
	 */
	public ValidatingDynamoDBEventListener(Validator validator) {
		Assert.notNull(validator, "validator must not be null!");
		this.validator = validator;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.socialsignin.spring.data.dynamodb.mapping.event.
	 * AbstractDynamoDBEventListener#onBeforeSave(java.lang.Object)
	 */
	@Override
	public void onBeforeSave(Object source) {

		LOG.debug("Validating object: {}", source);

		List<String> messages = new ArrayList<>();
		Set<ConstraintViolation<Object>> violations = validator.validate(source);
		if (!violations.isEmpty()) {
			Set<ConstraintViolation<?>> genericViolationSet = new HashSet<>();
			for (ConstraintViolation<?> v : violations) {
				genericViolationSet.add(v);
				messages.add(v.toString());
			}
			LOG.info("During object: {} validation violations found: {}", source, violations);
			throw new ConstraintViolationException(messages.toString(), genericViolationSet);
		}
	}
}


<|start_filename|>src/test/java/org/socialsignin/spring/data/dynamodb/domain/sample/Installation.java<|end_filename|>
/**
 * Copyright © 2018 spring-data-dynamodb (https://github.com/derjust/spring-data-dynamodb)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.socialsignin.spring.data.dynamodb.domain.sample;

import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBAutoGeneratedKey;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBHashKey;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBIndexHashKey;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBIndexRangeKey;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBTable;

import java.io.Serializable;
import java.util.Date;

/**
 * Test for <a href=
 * "https://github.com/spring-data-dynamodb/spring-data-dynamodb/issues/52">Issue
 * 52</a>.
 */
@DynamoDBTable(tableName = "installations")
public class Installation implements Serializable {
	private static final long serialVersionUID = 1L;

	@DynamoDBHashKey
	@DynamoDBAutoGeneratedKey
	public String id;

	@DynamoDBIndexHashKey(globalSecondaryIndexName = "idx-global-systemid")
	private String systemId;

	@DynamoDBIndexRangeKey(globalSecondaryIndexName = "idx-global-systemid")
	private Date updatedAt;

	public Installation() {

	}

	public Installation(final String systemId, final Date updatedAt) {
		this.systemId = systemId;
		this.updatedAt = updatedAt;
	}

	public String getId() {
		return id;
	}

	public void setId(final String id) {
		this.id = id;
	}

	public String getSystemId() {
		return systemId;
	}

	public void setSystemId(final String systemId) {
		this.systemId = systemId;
	}

	public Date getUpdatedAt() {
		return updatedAt;
	}

	public void setUpdatedAt(final Date updatedAt) {
		this.updatedAt = updatedAt;
	}

	@Override
	public String toString() {
		return "Installation [id='" + id + "', systemId='" + systemId + "', updatedAt='" + updatedAt + "']";
	}
}


<|start_filename|>src/main/java/org/socialsignin/spring/data/dynamodb/mapping/event/AbstractDynamoDBEventListener.java<|end_filename|>
/**
 * Copyright © 2018 spring-data-dynamodb (https://github.com/derjust/spring-data-dynamodb)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.socialsignin.spring.data.dynamodb.mapping.event;

/*
 * Copyright 2014 by the original author(s).
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import com.amazonaws.services.dynamodbv2.datamodeling.PaginatedQueryList;
import com.amazonaws.services.dynamodbv2.datamodeling.PaginatedScanList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationListener;
import org.springframework.core.GenericTypeResolver;

import java.util.List;
import java.util.function.Consumer;

/**
 * Base class to implement domain class specific {@link ApplicationListener}s.
 *
 * @author <NAME>
 * @author <NAME>
 */
public abstract class AbstractDynamoDBEventListener<E> implements ApplicationListener<DynamoDBMappingEvent<?>> {

	private static final Logger LOG = LoggerFactory.getLogger(AbstractDynamoDBEventListener.class);
	private final Class<?> domainClass;

	/**
	 * Creates a new {@link AbstractDynamoDBEventListener}.
	 */
	public AbstractDynamoDBEventListener() {
		Class<?> typeArgument = GenericTypeResolver.resolveTypeArgument(this.getClass(),
				AbstractDynamoDBEventListener.class);
		this.domainClass = typeArgument == null ? Object.class : typeArgument;
	}

	protected Class<?> getDomainClass() {
		return this.domainClass;
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see org.springframework.context.ApplicationListener#onApplicationEvent(org
	 * .springframework.context.ApplicationEvent)
	 */
	@Override
	public void onApplicationEvent(DynamoDBMappingEvent<?> event) {

		@SuppressWarnings("unchecked")
		E source = (E) event.getSource();

		// source can not be null as java.util.EventObject can not be constructed with
		// null
		assert source != null;

		if (event instanceof AfterScanEvent) {

			publishEachElement((PaginatedScanList<?>) source, this::onAfterScan);
			return;
		} else if (event instanceof AfterQueryEvent) {

			publishEachElement((PaginatedQueryList<?>) source, this::onAfterQuery);
			return;
		}
		// Check for matching domain type and invoke callbacks
		else if (domainClass.isAssignableFrom(source.getClass())) {
			if (event instanceof BeforeSaveEvent) {
				onBeforeSave(source);
				return;
			} else if (event instanceof AfterSaveEvent) {
				onAfterSave(source);
				return;
			} else if (event instanceof BeforeDeleteEvent) {
				onBeforeDelete(source);
				return;
			} else if (event instanceof AfterDeleteEvent) {
				onAfterDelete(source);
				return;
			} else if (event instanceof AfterLoadEvent) {
				onAfterLoad(source);
				return;
			}
		}
		// we should never end up here
		assert false;
	}

	@SuppressWarnings("unchecked")
	private void publishEachElement(List<?> list, Consumer<E> publishMethod) {
		list.stream().filter(o -> domainClass.isAssignableFrom(o.getClass())).map(o -> (E) o).forEach(publishMethod);
	}

	public void onBeforeSave(E source) {
		LOG.debug("onBeforeSave({}, {})", source);
	}

	public void onAfterSave(E source) {
		LOG.debug("onAfterSave({}, {})", source);
	}

	public void onAfterLoad(E source) {
		LOG.debug("onAfterLoad({})", source);
	}

	public void onAfterDelete(E source) {
		LOG.debug("onAfterDelete({})", source);
	}

	public void onBeforeDelete(E source) {
		LOG.debug("onBeforeDelete({})", source);
	}

	public void onAfterScan(E source) {
		LOG.debug("onAfterScan({})", source);
	}

	public void onAfterQuery(E source) {
		LOG.debug("onAfterQuery({})", source);
	}

}


<|start_filename|>src/main/java/org/socialsignin/spring/data/dynamodb/config/AbstractDynamoDBConfiguration.java<|end_filename|>
/**
 * Copyright © 2018 spring-data-dynamodb (https://github.com/derjust/spring-data-dynamodb)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.socialsignin.spring.data.dynamodb.config;

import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDB;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBTable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.socialsignin.spring.data.dynamodb.mapping.DynamoDBMappingContext;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;

import java.util.HashSet;
import java.util.Set;

/**
 * Base class for Spring Data DynamoDB configuration using JavaConfig.
 *
 * @author <NAME>
 */
@Configuration
public abstract class AbstractDynamoDBConfiguration {

	private static final Logger LOGGER = LoggerFactory.getLogger(AbstractDynamoDBConfiguration.class);

	public abstract AmazonDynamoDB amazonDynamoDB();

	public abstract AWSCredentials amazonAWSCredentials();

	/**
	 * Return the base packages to scan for mapped {@link DynamoDBTable}s. Will
	 * return the package name of the configuration class' (the concrete class, not
	 * this one here) by default. So if you have a {@code com.acme.AppConfig}
	 * extending {@link AbstractDynamoDBConfiguration} the base package will be
	 * considered {@code com.acme} unless the method is overriden to implement
	 * alternate behaviour.
	 *
	 * @return the base package to scan for mapped {@link DynamoDBTable} classes or
	 *         {@literal null} to not enable scanning for entities.
	 */
	protected String[] getMappingBasePackages() {

		Package mappingBasePackage = getClass().getPackage();
		String basePackage = mappingBasePackage == null ? null : mappingBasePackage.getName();

		return new String[]{basePackage};
	}

	/**
	 * Creates a {@link DynamoDBMappingContext} equipped with entity classes scanned
	 * from the mapping base package.
	 *
	 * @see #getMappingBasePackages()
	 * @return A newly created {@link DynamoDBMappingContext}
	 * @throws ClassNotFoundException
	 *             if the class with {@link DynamoDBTable} annotation can't be
	 *             loaded
	 */
	@Bean
	public DynamoDBMappingContext dynamoDBMappingContext() throws ClassNotFoundException {

		DynamoDBMappingContext mappingContext = new DynamoDBMappingContext();
		mappingContext.setInitialEntitySet(getInitialEntitySet());

		return mappingContext;
	}

	/**
	 * Scans the mapping base package for classes annotated with
	 * {@link DynamoDBTable}.
	 *
	 * @see #getMappingBasePackages()
	 * @return All classes with {@link DynamoDBTable} annotation
	 * @throws ClassNotFoundException
	 *             if the class with {@link DynamoDBTable} annotation can't be
	 *             loaded
	 */
	protected Set<Class<?>> getInitialEntitySet() throws ClassNotFoundException {

		Set<Class<?>> initialEntitySet = new HashSet<>();

		String[] basePackages = getMappingBasePackages();

		for (String basePackage : basePackages) {
			LOGGER.trace("getInitialEntitySet. basePackage: {}", basePackage);

			if (StringUtils.hasText(basePackage)) {
				ClassPathScanningCandidateComponentProvider componentProvider = new ClassPathScanningCandidateComponentProvider(
						false);
				componentProvider.addIncludeFilter(new AnnotationTypeFilter(DynamoDBTable.class));

				for (BeanDefinition candidate : componentProvider.findCandidateComponents(basePackage)) {
					String candidateClass = candidate.getBeanClassName();
					if (candidateClass != null) {
						LOGGER.trace("getInitialEntitySet. candidate: {}", candidateClass);
						initialEntitySet.add(ClassUtils.forName(candidateClass,
								AbstractDynamoDBConfiguration.class.getClassLoader()));
					} else {
						LOGGER.warn("getInitialEntitySet. candidate: {} did not provide a class", candidate);
					}
				}
			}
		}

		return initialEntitySet;
	}

}

<|start_filename|>src/test/java/org/socialsignin/spring/data/dynamodb/domain/sample/CustomerDocument.java<|end_filename|>
/**
 * Copyright © 2018 spring-data-dynamodb (https://github.com/derjust/spring-data-dynamodb)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.socialsignin.spring.data.dynamodb.domain.sample;

import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBAttribute;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBHashKey;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBIgnore;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBRangeKey;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBTable;
import org.springframework.data.annotation.Id;

@DynamoDBTable(tableName = "CustomerDocuments")
public class CustomerDocument {

	@Id
	private CustomerDocumentId customerDocumentId;

	@DynamoDBAttribute
	private String s3Location;

	public CustomerDocument() {
	}

	public CustomerDocument(String customerId, String documentType, String version, String s3Location) {
		this.customerDocumentId = new CustomerDocumentId(customerId, documentType, version);
		this.s3Location = s3Location;
	}

	@DynamoDBHashKey(attributeName = "customerId|documentType")
	public String getCustomerDocumentKey() {

		if (customerDocumentId == null) {
			return null;
		}

		return customerDocumentId.getCustomerDocumentKey();

	}

	public void setCustomerDocumentKey(String customerDocumentKey) {

		if (customerDocumentId == null) {
			this.customerDocumentId = new CustomerDocumentId();
		}

		customerDocumentId.setCustomerDocumentKey(customerDocumentKey);

	}

	@DynamoDBRangeKey(attributeName = "version")
	public String getVersion() {

		if (customerDocumentId == null) {
			return null;
		}

		return customerDocumentId.getVersion();

	}

	public void setVersion(String version) {

		if (customerDocumentId == null) {
			this.customerDocumentId = new CustomerDocumentId();
		}

		customerDocumentId.setVersion(version);

	}

	@DynamoDBIgnore
	public String getCustomerId() {

		if (customerDocumentId == null) {
			return null;
		}

		return customerDocumentId.getCustomerId();

	}

	@DynamoDBIgnore
	public String getDocumentType() {

		if (customerDocumentId == null) {
			return null;
		}

		return customerDocumentId.getDocumentType();

	}

	@DynamoDBIgnore
	public CustomerDocumentId getCustomerDocumentId() {
		return customerDocumentId;
	}

}

<|start_filename|>src/test/java/org/socialsignin/spring/data/dynamodb/mapping/DynamoDBPersistentPropertyImplUnitTest.java<|end_filename|>
/**
 * Copyright © 2018 spring-data-dynamodb (https://github.com/derjust/spring-data-dynamodb)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.socialsignin.spring.data.dynamodb.mapping;

import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBIgnore;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBTable;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.junit.MockitoJUnitRunner;

import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.junit.Assert.assertThat;

/**
 * Unit tests for {@link DynamoDBPersistentPropertyImpl}.
 * 
 * @author <NAME>
 * @author <NAME>
 */
@RunWith(MockitoJUnitRunner.class)
public class DynamoDBPersistentPropertyImplUnitTest {

	DynamoDBMappingContext context;
	DynamoDBPersistentEntity<?> entity;

	@Before
	public void setUp() {

		context = new DynamoDBMappingContext();
		entity = context.getPersistentEntity(Sample.class);
	}

	/**
	 * @see DATAJPA-284
	 */
	@Test
	public void considersOtherPropertiesAsNotTransient() {

		DynamoDBPersistentProperty property = entity.getPersistentProperty("otherProp");
		assertThat(property, is(notNullValue()));
	}

	/**
	 * @see DATAJPA-376
	 */
	@Test
	public void considersDynamoDBIgnoredPropertiesAsTransient() {
		assertThat(entity.getPersistentProperty("ignoredProp"), is(nullValue()));
	}

	@DynamoDBTable(tableName = "sample")
	static class Sample {

		private String ignoredProp = "ignored";
		private String otherProp = "other";

		public String getOtherProp() {
			return otherProp;
		}

		public void setOtherProp(String otherProp) {
			this.otherProp = otherProp;
		}

		@DynamoDBIgnore
		public String getIgnoredProp() {
			return ignoredProp;
		}

		public void setIgnoredProp(String ignoredProp) {
			this.ignoredProp = ignoredProp;
		}
	}

}


<|start_filename|>src/test/java/org/socialsignin/spring/data/dynamodb/repository/query/DynamoDBEntityWithHashKeyOnlyCriteriaUnitTest.java<|end_filename|>
/**
 * Copyright © 2018 spring-data-dynamodb (https://github.com/derjust/spring-data-dynamodb)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.socialsignin.spring.data.dynamodb.repository.query;

import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import org.socialsignin.spring.data.dynamodb.domain.sample.User;
import org.socialsignin.spring.data.dynamodb.repository.support.DynamoDBEntityInformation;

import java.util.Date;

@RunWith(MockitoJUnitRunner.class)
public class DynamoDBEntityWithHashKeyOnlyCriteriaUnitTest
		extends
			AbstractDynamoDBQueryCriteriaUnitTest<DynamoDBEntityWithHashKeyOnlyCriteria<User, String>> {

	@Mock
	private DynamoDBEntityInformation<User, String> entityInformation;

	@Before
	public void setUp() {
		Mockito.when(entityInformation.getHashKeyPropertyName()).thenReturn("id");
		criteria = new DynamoDBEntityWithHashKeyOnlyCriteria<>(entityInformation, null);
	}

	@Test
	public void testHasIndexHashKeyEqualConditionAnd_WhenConditionCriteriaIsEqualityOnAPropertyWhichIsAnIndexHashKeyButNotAHashKey() {
		Mockito.when(entityInformation.isGlobalIndexHashKeyProperty("name")).thenReturn(true);
		criteria.withPropertyEquals("name", "some name", String.class);
		boolean hasIndexHashKeyEqualCondition = criteria.hasIndexHashKeyEqualCondition();
		Assert.assertTrue(hasIndexHashKeyEqualCondition);
	}

	@Test
	public void testHasIndexHashKeyEqualCondition_WhenConditionCriteriaIsEqualityOnAPropertyWhichIsNotAnIndexHashKeyButIsAHashKey() {
		Mockito.when(entityInformation.isGlobalIndexHashKeyProperty("id")).thenReturn(false);
		criteria.withPropertyEquals("id", "some id", String.class);
		boolean hasIndexHashKeyEqualCondition = criteria.hasIndexHashKeyEqualCondition();
		Assert.assertFalse(hasIndexHashKeyEqualCondition);
	}

	@Test
	public void testHasIndexHashKeyEqualCondition_WhenConditionCriteriaIsEqualityOnAPropertyWhichIsBothAnIndexHashKeyAndAHashKey() {
		Mockito.when(entityInformation.isGlobalIndexHashKeyProperty("id")).thenReturn(true);
		criteria.withPropertyEquals("id", "some id", String.class);
		boolean hasIndexHashKeyEqualCondition = criteria.hasIndexHashKeyEqualCondition();
		Assert.assertTrue(hasIndexHashKeyEqualCondition);
	}

	@Test
	public void testHasIndexHashKeyEqualCondition_WhenConditionCriteriaIsEqualityOnAPropertyWhichIsNeitherAnIndexHashKeyOrAHashKey() {
		Mockito.when(entityInformation.isGlobalIndexHashKeyProperty("joinDate")).thenReturn(false);
		criteria.withPropertyEquals("joinDate", new Date(), Date.class);
		boolean hasIndexHashKeyEqualCondition = criteria.hasIndexHashKeyEqualCondition();
		Assert.assertFalse(hasIndexHashKeyEqualCondition);
	}

	@Test
	public void testHasIndexRangeKeyCondition_WhenConditionCriteriaIsEqualityOnAPropertyWhichIsAnIndexRangeKeyButNotAHashKey() {
		Mockito.when(entityInformation.isGlobalIndexRangeKeyProperty("name")).thenReturn(true);
		criteria.withPropertyEquals("name", "some name", String.class);
		boolean hasIndexRangeKeyCondition = criteria.hasIndexRangeKeyCondition();
		Assert.assertTrue(hasIndexRangeKeyCondition);
	}

	@Test
	public void testHasIndexRangeKeyCondition_WhenConditionCriteriaIsEqualityOnAPropertyWhichIsNotAnIndexRangeKeyButIsAHashKey() {
		Mockito.when(entityInformation.isGlobalIndexRangeKeyProperty("id")).thenReturn(false);
		criteria.withPropertyEquals("id", "some id", String.class);
		boolean hasIndexRangeKeyCondition = criteria.hasIndexRangeKeyCondition();
		Assert.assertFalse(hasIndexRangeKeyCondition);
	}

	@Test
	public void testHasIndexRangeKeyCondition_WhenConditionCriteriaIsEqualityOnAPropertyWhichIsBothAnIndexRangeKeyAndAHashKey() {
		Mockito.when(entityInformation.isGlobalIndexRangeKeyProperty("id")).thenReturn(true);
		criteria.withPropertyEquals("id", "some id", String.class);
		boolean hasIndexRangeKeyCondition = criteria.hasIndexRangeKeyCondition();
		Assert.assertTrue(hasIndexRangeKeyCondition);
	}

	@Test
	public void testHasIndexRangeKeyCondition_WhenConditionCriteriaIsEqualityOnAPropertyWhichIsNeitherAnIndexRangeKeyOrAHashKey() {
		Mockito.when(entityInformation.isGlobalIndexRangeKeyProperty("joinDate")).thenReturn(false);
		criteria.withPropertyEquals("joinDate", new Date(), Date.class);
		boolean hasIndexRangeKeyCondition = criteria.hasIndexRangeKeyCondition();
		Assert.assertFalse(hasIndexRangeKeyCondition);
	}

	// repeat

	@Test
	public void testHasIndexHashKeyEqualConditionAnd_WhenConditionCriteriaIsNonEqualityConditionOnAPropertyWhichIsAnIndexHashKeyButNotAHashKey() {
		Mockito.when(entityInformation.isGlobalIndexHashKeyProperty("name")).thenReturn(true);
		criteria.withPropertyBetween("name", "some name", "some other name", String.class);
		boolean hasIndexHashKeyEqualCondition = criteria.hasIndexHashKeyEqualCondition();
		Assert.assertFalse(hasIndexHashKeyEqualCondition);
	}

	@Test
	public void testHasIndexHashKeyEqualCondition_WhenConditionCriteriaIsNonEqualityConditionOnAPropertyWhichIsNotAnIndexHashKeyButIsAHashKey() {
		Mockito.when(entityInformation.isGlobalIndexHashKeyProperty("name")).thenReturn(false);
		criteria.withPropertyBetween("name", "some name", "some other name", String.class);
		boolean hasIndexHashKeyEqualCondition = criteria.hasIndexHashKeyEqualCondition();
		Assert.assertFalse(hasIndexHashKeyEqualCondition);
	}

	@Test
	public void testHasIndexHashKeyEqualCondition_WhenConditionCriteriaIsNonEqualityConditionOnAPropertyWhichIsBothAnIndexHashKeyAndAHashKey() {
		Mockito.when(entityInformation.isGlobalIndexHashKeyProperty("id")).thenReturn(true);
		criteria.withPropertyBetween("id", "some id", "some other id", String.class);
		boolean hasIndexHashKeyEqualCondition = criteria.hasIndexHashKeyEqualCondition();
		Assert.assertFalse(hasIndexHashKeyEqualCondition);
	}

	@Test
	public void testHasIndexHashKeyEqualCondition_WhenConditionCriteriaIsNonEqualityConditionOnAPropertyWhichIsNeitherAnIndexHashKeyOrAHashKey() {
		Mockito.when(entityInformation.isGlobalIndexHashKeyProperty("joinDate")).thenReturn(false);
		criteria.withPropertyBetween("joinDate", new Date(), new Date(), Date.class);
		boolean hasIndexHashKeyEqualCondition = criteria.hasIndexHashKeyEqualCondition();
		Assert.assertFalse(hasIndexHashKeyEqualCondition);
	}

	@Test
	public void testHasIndexRangeKeyCondition_WhenConditionCriteriaIsNonEqualityConditionOnAPropertyWhichIsAnIndexRangeKeyButNotAHashKey() {
		Mockito.when(entityInformation.isGlobalIndexRangeKeyProperty("name")).thenReturn(true);
		criteria.withPropertyBetween("name", "some name", "some other name", String.class);
		boolean hasIndexRangeKeyCondition = criteria.hasIndexRangeKeyCondition();
		Assert.assertTrue(hasIndexRangeKeyCondition);
	}

	@Test
	public void testHasIndexRangeKeyCondition_WhenConditionCriteriaIsNonEqualityConditionOnAPropertyWhichIsNotAnIndexRangeKeyButIsAHashKey() {
		Mockito.when(entityInformation.isGlobalIndexRangeKeyProperty("id")).thenReturn(false);
		criteria.withPropertyBetween("id", "some id", "some other id", String.class);
		boolean hasIndexRangeKeyCondition = criteria.hasIndexRangeKeyCondition();
		Assert.assertFalse(hasIndexRangeKeyCondition);
	}

	@Test
	public void testHasIndexRangeKeyCondition_WhenConditionCriteriaIsNonEqualityConditionOnAPropertyWhichIsBothAnIndexRangeKeyAndAHashKey() {
		Mockito.when(entityInformation.isGlobalIndexRangeKeyProperty("id")).thenReturn(true);
		criteria.withPropertyBetween("id", "some id", "some other id", String.class);
		boolean hasIndexRangeKeyCondition = criteria.hasIndexRangeKeyCondition();
		Assert.assertTrue(hasIndexRangeKeyCondition);
	}

	@Test
	public void testHasIndexRangeKeyCondition_WhenConditionCriteriaIsNonEqualityConditionOnAPropertyWhichIsNeitherAnIndexRangeKeyOrAHashKey() {
		Mockito.when(entityInformation.isGlobalIndexRangeKeyProperty("joinDate")).thenReturn(false);
		criteria.withPropertyBetween("joinDate", new Date(), new Date(), Date.class);
		boolean hasIndexRangeKeyCondition = criteria.hasIndexRangeKeyCondition();
		Assert.assertFalse(hasIndexRangeKeyCondition);
	}

}
