Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#30277 API/Factory methods to insert in the unique_fields table #30332

Closed
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
e0e83bd
bug(apps) fixing link to dotAI portlet (#29556)
wezell Oct 9, 2024
5fff6b7
#30277 API/Factory methods to insert in the unique_fields table
freddyDOTCMS Oct 11, 2024
8a61eaa
#30277 Javadoc
freddyDOTCMS Oct 11, 2024
59ce98b
Merge branch 'main' into issue-30277-Create-API-Factory-methods-to-in…
freddyDOTCMS Oct 11, 2024
4fcf5a7
#30306 Fixing test
freddyDOTCMS Oct 14, 2024
64d7a0e
Merge branch 'main' into issue-30277-Create-API-Factory-methods-to-in…
freddyDOTCMS Oct 14, 2024
23b73e5
Adding MainSuite
freddyDOTCMS Oct 14, 2024
28e0e04
Merge branch 'issue-30277-Create-API-Factory-methods-to-insert-in-the…
freddyDOTCMS Oct 14, 2024
5d0574a
Missing changes
freddyDOTCMS Oct 14, 2024
0403905
Merge branch 'main' into issue-30277-Create-API-Factory-methods-to-in…
freddyDOTCMS Oct 15, 2024
f17ca69
Merge branch 'main' into issue-30277-Create-API-Factory-methods-to-in…
freddyDOTCMS Oct 15, 2024
504efaa
#30277 Feedback
freddyDOTCMS Oct 15, 2024
0f40ac6
Merge branch 'issue-30277-Create-API-Factory-methods-to-insert-in-the…
freddyDOTCMS Oct 15, 2024
bc8ee9f
#30277 Feedback
freddyDOTCMS Oct 15, 2024
a19069c
#302077 Including CDI
freddyDOTCMS Oct 15, 2024
3c7fa5c
#302077 Renamong to API
freddyDOTCMS Oct 15, 2024
0aa9ced
feedback
freddyDOTCMS Oct 16, 2024
abb53a6
Merge branch 'main' into issue-30277-Create-API-Factory-methods-to-in…
freddyDOTCMS Oct 16, 2024
93f59f9
#30277 Create Strategy classes
freddyDOTCMS Oct 21, 2024
126c4ee
#30277 Validate when a Contentlet is Updated o saved
freddyDOTCMS Oct 21, 2024
df2e213
merge
freddyDOTCMS Oct 21, 2024
4069133
#30279 Fixing test
freddyDOTCMS Oct 25, 2024
7330a78
#30279 Fixing test
freddyDOTCMS Oct 25, 2024
9f50a0f
Merge remote-tracking branch 'origin/master' into issue-30277-Create-…
freddyDOTCMS Oct 25, 2024
5bdcb6e
merge
freddyDOTCMS Oct 25, 2024
1f4a1b1
Fixing test
freddyDOTCMS Oct 28, 2024
7381cc9
Merge branch 'main' into issue-30277-Create-API-Factory-methods-to-in…
freddyDOTCMS Oct 28, 2024
0d0fe1b
Removing unneeded changes
freddyDOTCMS Oct 28, 2024
555b48d
Merge branch 'issue-30277-Create-API-Factory-methods-to-insert-in-the…
freddyDOTCMS Oct 28, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
package com.dotcms.contenttype.business;

import com.dotcms.content.elasticsearch.business.ESContentletAPIImpl;
import com.dotcms.contenttype.model.field.Field;
import com.dotcms.contenttype.model.type.ContentType;
import com.dotmarketing.beans.Host;
import com.dotmarketing.business.APILocator;
import com.dotmarketing.exception.DotDataException;
import com.dotmarketing.exception.DotRuntimeException;
import com.dotmarketing.portlets.languagesmanager.model.Language;
import com.dotmarketing.util.StringUtils;
import com.liferay.util.StringPool;

import java.util.Map;
import java.util.Objects;

/**
* Represent the criteria used to determine if a value is unique or not
*/
public class UniqueFieldCriteria {
private final ContentType contentType;
private final Field field;
private final Object value;
private final Language language;
private final Host site;

public UniqueFieldCriteria(final Builder builder) {
this.contentType = builder.contentType;
this.field = builder.field;
this.value = builder.value;
this.language = builder.language;
this.site = builder.site;
}

/**
* Return a Map with the values in this Unique Field Criteria
* @return
*/
public Map<String, Object> toMap(){
return Map.of(
"contentTypeID", Objects.requireNonNull(contentType.id()),
"fieldVariableName", Objects.requireNonNull(field.variable()),
"fieldValue", value.toString(),
"languageId", language.getId(),
"hostId", site.getIdentifier(),
"uniquePerSite", isUniqueForSite(contentType.id(), field.variable())
);
}

/**
* return true if the uniquePerSite Field Variable is set to true.
*
* @param contentTypeId
* @param fieldVariableName
* @return
*/
private static boolean isUniqueForSite(String contentTypeId, String fieldVariableName) {
try {
final Field uniqueField = APILocator.getContentTypeFieldAPI().byContentTypeIdAndVar(contentTypeId, fieldVariableName);

return uniqueField.fieldVariableValue(ESContentletAPIImpl.UNIQUE_PER_SITE_FIELD_VARIABLE_NAME)
.map(Boolean::valueOf).orElse(false);
} catch (DotDataException e) {
throw new DotRuntimeException(e);
}
}

/**
* Return a hash calculated as follow:
*
* - If the uniquePerSite Field Variable is set to true then concat the:
* Content Type' id + Field Variable Name + Language's Id + Field Value
*
* - If the uniquePerSite Field Variable is set to false then concat the:
* Content Type' id + Field Variable Name + Language's Id + Field Value + Site's id
*
* @return
*/
public String hash(){
return StringUtils.hashText(contentType.id() + field.variable() + language.getId() + value +
((isUniqueForSite(contentType.id(), field.variable())) ? site.getIdentifier() : StringPool.BLANK));
}

public Field field() {
return field;
}

public Object value() {
return value;
}

public ContentType contentType() {
return contentType;
}

public Language language() {
return language;
}


public static class Builder {
private ContentType contentType;
private Field field;
private Object value;
private Language language;
private Host site;


public Builder setContentType(final ContentType contentType) {
this.contentType = contentType;
return this;
}

public Builder setField(final Field field) {
this.field = field;
return this;
}

public Builder setValue(final Object value) {
this.value = value;
return this;
}

public Builder setLanguage(final Language language) {
this.language = language;
return this;
}

public Builder setSite(final Host site) {
this.site = site;
return this;
}

public UniqueFieldCriteria build(){
Objects.requireNonNull(contentType);
Objects.requireNonNull(field);
Objects.requireNonNull(value);
Objects.requireNonNull(language);
Objects.requireNonNull(site);

return new UniqueFieldCriteria(this);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.dotcms.contenttype.business;

import com.dotmarketing.exception.DotDataException;

import java.util.Map;

/**
* This Factory allow you to interact with the unique_fields table
*/
public interface UniqueFieldFactory {

/**
* Insert a new register into the unique_fields table, if already exists another register with the same
* 'unique_key_val' then a {@link java.sql.SQLException} is thrown.
*
* @param key
* @param supportingValues
*/
void insert(final String key, final Map<String, Object> supportingValues) throws DotDataException;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.dotcms.contenttype.business;

import com.dotmarketing.common.db.DotConnect;
import com.dotmarketing.exception.DotDataException;

import java.util.Map;

/**
* Default implementation of {@link UniqueFieldFactory}
*/
public class UniqueFieldFactoryImpl implements UniqueFieldFactory {

private final static String INSERT_SQL = "INSERT INTO unique_fields (unique_key_val, supporting_values) VALUES (?, ?)";
/**
* Default implementation of {@link UniqueFieldFactory#insert(String, Map)}
*
* @param key
* @param supportingValues
*/
@Override
public void insert(final String key, final Map<String, Object> supportingValues) throws DotDataException {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the factory concept usually is next to an API, since you do not have any, and there are not any plans of covering more general cases, this is just a single line you may move straight to the Helper/API you have

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think is better kepp the Factory because I am going to include more SQL Statement here later

new DotConnect().setSQL(INSERT_SQL).addParam(key).addJSONParam(supportingValues).loadObjectResults();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package com.dotcms.contenttype.business;

import com.dotcms.business.WrapInTransaction;
import com.dotcms.content.elasticsearch.business.ESContentletAPIImpl;
import com.dotcms.util.CollectionsUtils;
import com.dotmarketing.business.FactoryLocator;
import com.dotmarketing.exception.DotDataException;
import com.dotmarketing.util.Logger;

import java.util.HashMap;
import java.util.Map;

/**
* This Helper allow you to interact with the unique_fields table
*/
class UniqueFieldUtil {

private final UniqueFieldFactory uniqueFieldFactory;

public UniqueFieldUtil(){
this(FactoryLocator.getUniqueFieldFactory());
}

public UniqueFieldUtil(final UniqueFieldFactory uniqueFieldFactory) {
this.uniqueFieldFactory = uniqueFieldFactory;
}


/**
* Insert a new unique field value, if the value is duplicated then a {@link java.sql.SQLException} is thrown.
*
* @param uniqueFieldCriteria
* @param contentletId
*
* @throws UniqueFieldValueDupliacatedException when the Value is duplicated
* @throws DotDataException when a DotDataException is throws
*/
@WrapInTransaction
public void insert(final UniqueFieldCriteria uniqueFieldCriteria, final String contentletId)
throws UniqueFieldValueDupliacatedException, DotDataException {

if (!uniqueFieldCriteria.field().unique()) {
final String message = String.format("The Field %s is not unique", uniqueFieldCriteria.field().variable());
Logger.debug(UniqueFieldUtil.class, message);
throw new IllegalArgumentException(message);
}

final boolean uniqueForSite = uniqueFieldCriteria.field().fieldVariableValue(ESContentletAPIImpl.UNIQUE_PER_SITE_FIELD_VARIABLE_NAME)
.map(Boolean::valueOf).orElse(false);

final Map<String, Object> supportingValues = new HashMap<>(uniqueFieldCriteria.toMap());
supportingValues.put("contentletsId", CollectionsUtils.list(contentletId));
supportingValues.put("uniquePerSite", uniqueForSite);

try {
Logger.debug(UniqueFieldUtil.class, "Including value in the unique_fields table");
uniqueFieldFactory.insert(uniqueFieldCriteria.hash(), supportingValues);
} catch (DotDataException e) {

if (isDuplicatedKeyError(e)) {
final String duplicatedValueMessage = String.format("The value %s for the field %s in the Content type %s is duplicated",
uniqueFieldCriteria.value(), uniqueFieldCriteria.field().variable(),
uniqueFieldCriteria.contentType().variable());

Logger.error(UniqueFieldUtil.class, duplicatedValueMessage);
throw new UniqueFieldValueDupliacatedException(duplicatedValueMessage);
}
}
}

private static boolean isDuplicatedKeyError(final Exception exeption) {
final String originalMessage = exeption.getMessage();

return originalMessage != null && originalMessage.startsWith(
"ERROR: duplicate key value violates unique constraint \"unique_fields_pkey\"");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.dotcms.contenttype.business;

/**
* Throw if try to insert a duplicated register in unique_fiedls table
*/
public class UniqueFieldValueDupliacatedException extends Exception{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done 0aa9ced


public UniqueFieldValueDupliacatedException(String message) {
super(message);
}
}
13 changes: 1 addition & 12 deletions dotCMS/src/main/java/com/dotmarketing/business/APILocator.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,18 +30,7 @@
import com.dotcms.content.elasticsearch.business.ESIndexAPI;
import com.dotcms.content.elasticsearch.business.IndiciesAPI;
import com.dotcms.content.elasticsearch.business.IndiciesAPIImpl;
import com.dotcms.contenttype.business.ContentTypeAPI;
import com.dotcms.contenttype.business.ContentTypeAPIImpl;
import com.dotcms.contenttype.business.ContentTypeDestroyAPI;
import com.dotcms.contenttype.business.ContentTypeDestroyAPIImpl;
import com.dotcms.contenttype.business.ContentTypeFieldLayoutAPI;
import com.dotcms.contenttype.business.ContentTypeFieldLayoutAPIImpl;
import com.dotcms.contenttype.business.DotAssetAPI;
import com.dotcms.contenttype.business.DotAssetAPIImpl;
import com.dotcms.contenttype.business.FieldAPI;
import com.dotcms.contenttype.business.FieldAPIImpl;
import com.dotcms.contenttype.business.StoryBlockAPI;
import com.dotcms.contenttype.business.StoryBlockAPIImpl;
import com.dotcms.contenttype.business.*;
import com.dotcms.device.DeviceAPI;
import com.dotcms.device.DeviceAPIImpl;
import com.dotcms.dotpubsub.DotPubSubProvider;
Expand Down
20 changes: 13 additions & 7 deletions dotCMS/src/main/java/com/dotmarketing/business/FactoryLocator.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,7 @@
import com.dotcms.cluster.business.ServerFactory;
import com.dotcms.content.elasticsearch.business.ESContentFactoryImpl;
import com.dotcms.content.elasticsearch.business.IndiciesFactory;
import com.dotcms.contenttype.business.ContentTypeFactory;
import com.dotcms.contenttype.business.ContentTypeFactoryImpl;
import com.dotcms.contenttype.business.FieldFactory;
import com.dotcms.contenttype.business.FieldFactoryImpl;
import com.dotcms.contenttype.business.RelationshipFactory;
import com.dotcms.contenttype.business.RelationshipFactoryImpl;
import com.dotcms.contenttype.business.*;
import com.dotcms.cube.CubeJSClientFactory;
import com.dotcms.cube.CubeJSClientFactoryImpl;
import com.dotcms.enterprise.DashboardProxy;
Expand Down Expand Up @@ -290,6 +285,15 @@ public static ContentAnalyticsFactory getContentAnalyticsFactory() {
return (ContentAnalyticsFactory) getInstance(FactoryIndex.CONTENT_ANALYTICS_FACTORY);
}

/**
* Returns the Factory object that handles operations related to unique_fields Validation
*
* @return An instance of the {@link HostFactory} object.
*/
public static UniqueFieldFactory getUniqueFieldFactory() {
return (UniqueFieldFactory) getInstance(FactoryIndex.UNIQUE_FIELD_FACTORY);
}

private static Object getInstance(FactoryIndex index) {

if(instance == null){
Expand Down Expand Up @@ -369,7 +373,8 @@ enum FactoryIndex
SYSTEM_TABLE_FACTORY,
CUBEJS_CLIENT_FACTORY,
LANGUAGE_VARIABLE_FACTORY,
CONTENT_ANALYTICS_FACTORY;
CONTENT_ANALYTICS_FACTORY,
UNIQUE_FIELD_FACTORY;

Object create() {
switch(this) {
Expand Down Expand Up @@ -414,6 +419,7 @@ Object create() {
case CUBEJS_CLIENT_FACTORY: return new CubeJSClientFactoryImpl();
case LANGUAGE_VARIABLE_FACTORY: return new LanguageVariableFactoryImpl();
case CONTENT_ANALYTICS_FACTORY: CDIUtils.getBean(ContentAnalyticsFactory.class).orElseThrow(() -> new DotRuntimeException("ContentAnalyticsFactory not found"));
case UNIQUE_FIELD_FACTORY: return new UniqueFieldFactoryImpl();
}
throw new AssertionError("Unknown Factory Index: " + this);
}
Expand Down
8 changes: 4 additions & 4 deletions dotcms-integration/src/test/java/com/dotcms/MainSuite2b.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,7 @@
import com.dotcms.content.business.json.LegacyJSONObjectRenderTest;
import com.dotcms.content.elasticsearch.business.ESIndexAPITest;
import com.dotcms.content.model.hydration.MetadataDelegateTest;
import com.dotcms.contenttype.business.ContentTypeDestroyAPIImplTest;
import com.dotcms.contenttype.business.ContentTypeInitializerTest;
import com.dotcms.contenttype.business.StoryBlockAPITest;
import com.dotcms.contenttype.business.*;
import com.dotcms.csspreproc.CSSCacheTest;
import com.dotcms.csspreproc.CSSPreProcessServletTest;
import com.dotcms.dotpubsub.RedisPubSubImplTest;
Expand Down Expand Up @@ -385,7 +383,9 @@
SimpleInjectionIT.class,
LegacyJSONObjectRenderTest.class,
Task241013RemoveFullPathLcColumnFromIdentifierTest.class,
Task241009CreatePostgresJobQueueTablesTest.class
Task241009CreatePostgresJobQueueTablesTest.class,
UniqueFieldFactoryImplTest.class,
UniqueFieldUtilTest.class
})

public class MainSuite2b {
Expand Down
Loading