Skip to content

Commit

Permalink
feat(Announcements): correcting query that retrieves announcements fr…
Browse files Browse the repository at this point in the history
…om prod and fixing postman Ref:s#25736 (#27480)

* #25736 the postman is set to create the CT taking into account the announcementDate as publish date

* #25736 megre latest from master

* #25736 allowing time for publishing to take effect

* #25736 debugging postman failure

* #25736 correcting announcements remote query

* #25736 code clean up

* #25736 updating postman test

* #25736 url query fix

* #25736 adjusting test

* #25736 imrproved IT

* #25736 log should be debug
  • Loading branch information
fabrizzio-dotCMS committed Feb 2, 2024
1 parent c7df3b2 commit ace5e2f
Show file tree
Hide file tree
Showing 12 changed files with 254 additions and 195 deletions.
205 changes: 148 additions & 57 deletions dotCMS/src/curl-test/Announcements.postman_collection.json

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public interface AnnouncementsHelper {
Lazy.of(() -> Config.getIntProperty("ANNOUNCEMENTS_LIMIT", 5));


List<Announcement> getAnnouncements(String languageIdOrCode, boolean refreshCache,
List<Announcement> getAnnouncements(boolean refreshCache,
Integer limit, User user);

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@
import com.dotcms.system.announcements.Announcement;
import com.dotcms.system.announcements.AnnouncementsCache;
import com.dotcms.system.announcements.AnnouncementsCacheImpl;
import com.dotmarketing.business.APILocator;
import com.dotmarketing.portlets.languagesmanager.business.LanguageAPI;
import com.dotmarketing.portlets.languagesmanager.model.Language;
import com.dotmarketing.util.Logger;
import com.liferay.portal.model.User;
import java.util.List;
Expand All @@ -15,75 +12,46 @@
*/
public class AnnouncementsHelperImpl implements AnnouncementsHelper{

private final LanguageAPI languageAPI;

private final AnnouncementsCache announcementsCache;

private final AnnouncementsLoader loader;


public AnnouncementsHelperImpl() {
this(APILocator.getLanguageAPI(), new RemoteAnnouncementsLoaderImpl(), new AnnouncementsCacheImpl());
this(new RemoteAnnouncementsLoaderImpl(), new AnnouncementsCacheImpl());
}

/**
* Constructor
* @param languageAPI LanguageAPI
* @param announcementsCache AnnouncementsCache
*/
public AnnouncementsHelperImpl(LanguageAPI languageAPI, AnnouncementsLoader loader, AnnouncementsCache announcementsCache) {
this.languageAPI = languageAPI;
public AnnouncementsHelperImpl(AnnouncementsLoader loader, AnnouncementsCache announcementsCache) {
this.announcementsCache = announcementsCache;
this.loader = loader;
}


/**
* Get the language by id or code, if not found fallback to default language
* @param languageIdOrCode String
* @return Language
*/
Language getLanguage(final String languageIdOrCode) {
Language language;
try {
final String[] split = languageIdOrCode.split("-");
if(split.length > 1) {
language = languageAPI.getLanguage(split[0], split[1]);
} else {
language = languageAPI.getLanguage(languageIdOrCode);
}
} catch (Exception e) {
Logger.debug(AnnouncementsHelperImpl.class, String.format(" failed to get lang [%s] with message: [%s] fallback to default language", languageIdOrCode, e.getMessage()));
language = languageAPI.getDefaultLanguage();
}
return language;
}


/**
* Get the announcements from the cache or from the remote server
* @param languageIdOrCode String
*
* @param refreshCache boolean
* @param limit Integer
* @param user User
* @return List<Announcement>
*/
@Override
public List<Announcement> getAnnouncements( final String languageIdOrCode, final boolean refreshCache, final Integer limit, final User user) {
public List<Announcement> getAnnouncements(final boolean refreshCache, final Integer limit, final User user) {

Logger.debug(AnnouncementsHelperImpl.class,String.format("Getting announcements for language: %s refreshCache: %s limit: %d, user: %s ", languageIdOrCode, refreshCache, limit, user.getUserId()));
final Language language = getLanguage(languageIdOrCode);
Logger.debug(AnnouncementsHelperImpl.class,String.format("Getting announcements refreshCache: %s limit: %d, user: %s ", refreshCache, limit, user.getUserId()));
final int limitValue = getLimit(limit);
if(!refreshCache) {
Logger.debug(this, "Getting announcements from cache for language: " + language.getId() + " limit: " + limitValue);
final List<Announcement> announcements = announcementsCache.get(language);
Logger.debug(this, "Getting announcements from cache for limit: " + limitValue);
final List<Announcement> announcements = announcementsCache.get();
if (announcements != null && !announcements.isEmpty()) {
return getSubList(limitValue, announcements);
}
}
final List<Announcement> announcements = loader.loadAnnouncements(language);
final List<Announcement> announcements = loader.loadAnnouncements();
if(!announcements.isEmpty()){
announcementsCache.put(language, announcements);
announcementsCache.put(announcements);
return getSubList(limitValue, announcements);
}
return List.of();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,8 @@ public interface AnnouncementsLoader {

/**
* Load the announcements for the given language
* @param language Language
* @return List<Announcement>
*/
List<Announcement> loadAnnouncements(Language language);
List<Announcement> loadAnnouncements();

}
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@ public AnnouncementsResource() {
@Produces({MediaType.APPLICATION_JSON, "application/javascript"})
public final ResponseEntityView<List<Announcement>> announcements(@Context final HttpServletRequest request,
@Context final HttpServletResponse response,
@QueryParam("langIdOrCode") final String langIdOrCode,
@QueryParam("refreshCache") final boolean refreshCache,
@QueryParam("limit") final int limit
) {
Expand All @@ -74,7 +73,7 @@ public final ResponseEntityView<List<Announcement>> announcements(@Context final
.rejectWhenNoUser(true)
.init();
final User user = initData.getUser();
final List<Announcement> announcements = helper.getAnnouncements(langIdOrCode, refreshCache , limit, user);
final List<Announcement> announcements = helper.getAnnouncements(refreshCache , limit, user);
return new ResponseEntityView<>(announcements); // 200
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,14 @@
import com.dotcms.content.business.json.ContentletJsonHelper;
import com.dotcms.rest.RestClientBuilder;
import com.dotcms.system.announcements.Announcement;
import com.dotmarketing.business.APILocator;
import com.dotmarketing.exception.DotRuntimeException;
import com.dotmarketing.portlets.languagesmanager.business.LanguageAPI;
import com.dotmarketing.portlets.languagesmanager.model.Language;
import com.dotmarketing.util.Config;
import com.dotmarketing.util.Logger;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
import io.vavr.Lazy;
import io.vavr.control.Try;
Expand All @@ -21,6 +19,7 @@
import java.time.format.DateTimeFormatterBuilder;
import java.time.temporal.ChronoField;
import java.util.List;
import java.util.function.Supplier;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.MediaType;
Expand All @@ -31,35 +30,47 @@
*/
public class RemoteAnnouncementsLoaderImpl implements AnnouncementsLoader{

//The CT varN/ame used to retrieve the Announcements
static final Lazy<String>DOT_ANNOUNCEMENT_CT =
Lazy.of(()-> Config.getStringProperty("DOT_ANNOUNCEMENT_CT", "Announcement"));

//This is the url to the dotCMS instance set to provide and feed all consumers with announcements
static final Lazy<String> ANNOUNCEMENTS_BASE_URL =
static final Lazy<String> BASE_URL_LAZY_SUPPLIER =
Lazy.of(() -> Config.getStringProperty("ANNOUNCEMENTS_BASE_URL", "https://www.dotcms.com"));

//The query pattern to retrieve the Announcements
static final String ANNOUNCEMENTS_QUERY_PATTERN = "%s/api/content/render/false/query/+contentType:%s +languageId:%d +deleted:false +live:true/orderBy/%s.announcementDate desc";
static final String ANNOUNCEMENTS_QUERY = "/api/content/render/false/query/+contentType:Announcement%20+languageId:1%20+deleted:false%20+live:true%20/orderby/Announcement.announcementDate%20desc";

Supplier<String> baseURL = BASE_URL_LAZY_SUPPLIER;

/**
* Default constructor
*/
public RemoteAnnouncementsLoaderImpl() {
}

/**
* Constructor
* @param baseURL Supplier<String>
*/
@VisibleForTesting
public RemoteAnnouncementsLoaderImpl(final Supplier<String> baseURL) {
this.baseURL = baseURL;
}

/**
* Load the announcements from the remote dotCMS instance
* @param language Language
* @return List<Announcement>
*/
@Override
public List<Announcement> loadAnnouncements(final Language language) {
final JsonNode jsonNode = loadRemoteAnnouncements(language);
public List<Announcement> loadAnnouncements() {
final JsonNode jsonNode = loadRemoteAnnouncements();
return toAnnouncements(jsonNode);
}

/**
* Load the announcements from the remote dotCMS instance
* @param language Language
* @return JsonNode
*/
JsonNode loadRemoteAnnouncements(final Language language) {
final String url = buildURL(language);
JsonNode loadRemoteAnnouncements() {
final String url = buildURL();
//Let's log the url to retrieve the announcements for debugging purposes on postman
Logger.debug(AnnouncementsHelperImpl.class, String.format("loading announcements from [%s]", url));
try {
final Client client = restClient();
final WebTarget webTarget = client.target(url);
Expand All @@ -83,12 +94,10 @@ JsonNode loadRemoteAnnouncements(final Language language) {

/**
* Build the url to retrieve the announcements
* @param language Language
* @return String
*/
String buildURL(Language language) {
final String raw = String.format(ANNOUNCEMENTS_QUERY_PATTERN, ANNOUNCEMENTS_BASE_URL.get(),
DOT_ANNOUNCEMENT_CT.get(), language.getId(), DOT_ANNOUNCEMENT_CT.get());
String buildURL() {
final String raw = baseURL.get() + ANNOUNCEMENTS_QUERY;
//clean up double slashes in the url
return raw.replaceAll("(?<!(http:|https:))//", "/");
}
Expand All @@ -101,23 +110,6 @@ static Client restClient() {
return RestClientBuilder.newClient();
}

/**
* Get the language by id or code, if not found fallback to default language
* @param languageIdOrCode String
* @return Language
*/
Language getLanguage(final String languageIdOrCode) {
final LanguageAPI languageAPI = APILocator.getLanguageAPI();
Language language;
try {
language = languageAPI.getLanguage(languageIdOrCode);
} catch (Exception e) {
Logger.debug(AnnouncementsHelperImpl.class, String.format(" failed to get lang [%s] with message: [%s] fallback to default language", languageIdOrCode, e.getMessage()));
language = languageAPI.getDefaultLanguage();
}
return language;
}

DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.appendPattern("yyyy-MM-dd HH:mm:ss")
.appendFraction(ChronoField.NANO_OF_SECOND, 0, 3, true)
Expand All @@ -129,7 +121,7 @@ Language getLanguage(final String languageIdOrCode) {
* @param root JsonNode
* @return List<Announcement>
*/
List<Announcement> toAnnouncements(final JsonNode root) {
List<Announcement> toAnnouncements(final JsonNode root) {

final ImmutableList.Builder<Announcement> announcements = ImmutableList.builder();
final JsonNode nodes = root.get("contentlets");
Expand All @@ -147,9 +139,6 @@ List<Announcement> toAnnouncements(final JsonNode root) {
final LocalDateTime date = LocalDateTime.parse(dateString, formatter);
final LocalDateTime modDate = LocalDateTime.parse(modDateString, formatter);

final Language language = getLanguage(langId);


announcements.add(
Announcement.builder()
.identifier(identifier)
Expand All @@ -159,8 +148,7 @@ List<Announcement> toAnnouncements(final JsonNode root) {
.announcementDateAsISO8601(date.toString())
.type(type)
.url(url)
.languageId(language.getId())
.languageCode(language.getIsoCode())
.languageId(langId)
.modDate(modDate.toInstant(java.time.ZoneOffset.UTC))
.modDateAsISO8601(modDate.toString())
.description(description)
Expand All @@ -172,4 +160,4 @@ List<Announcement> toAnnouncements(final JsonNode root) {
}


}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,7 @@ public interface AbstractAnnouncement {

String inode();

String languageCode();

long languageId();
String languageId();

String type();

Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
package com.dotcms.system.announcements;

import com.dotmarketing.portlets.languagesmanager.model.Language;
import java.util.List;

public interface AnnouncementsCache {

void clearCache();

void put(Language language, List<Announcement> announcements);
void put(List<Announcement> announcements);

List<Announcement> get(Language language);
List<Announcement> get();

}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import com.dotcms.business.SystemCache;
import com.dotcms.cache.Expirable;
import com.dotmarketing.business.CacheLocator;
import com.dotmarketing.portlets.languagesmanager.model.Language;
import com.dotmarketing.util.Config;
import com.google.common.annotations.VisibleForTesting;
import io.vavr.Lazy;
Expand All @@ -18,7 +17,7 @@ public class AnnouncementsCacheImpl implements AnnouncementsCache {
static final Lazy<Integer> ANNOUNCEMENTS_TTL =
Lazy.of(() -> Config.getIntProperty("ANNOUNCEMENTS_TTL", 3600 ));

static final String ANNOUNCEMENTS = "announcements::%s";
static final String ANNOUNCEMENTS_KEY = "dot::announcements";
private final SystemCache systemCache;

public AnnouncementsCacheImpl() {
Expand All @@ -28,32 +27,27 @@ public AnnouncementsCacheImpl() {

@Override
public void clearCache() {
systemCache.clearCache();
}

private String hashKey(Language language) {
return String.format(ANNOUNCEMENTS, language.getIsoCode());
systemCache.remove(ANNOUNCEMENTS_KEY);
}

@Override
public void put(final Language language, final List<Announcement> announcements) {
this.put(language, announcements, ANNOUNCEMENTS_TTL.get());
public void put(final List<Announcement> announcements) {
this.put(announcements, ANNOUNCEMENTS_TTL.get());
}

@VisibleForTesting
public void put(final Language language, final List<Announcement> announcements , final long ttl) {
systemCache.put(hashKey(language), new CacheEntryImpl(announcements,ttl));
public void put(final List<Announcement> announcements , final long ttl) {
systemCache.put(ANNOUNCEMENTS_KEY, new CacheEntryImpl(announcements,ttl));
}

@SuppressWarnings("unchecked")
@Override
public List<Announcement> get(final Language language) {
final String key = String.format(ANNOUNCEMENTS, language.getIsoCode());
final Object object = systemCache.get(key);
public List<Announcement> get() {
final Object object = systemCache.get(ANNOUNCEMENTS_KEY);
if (object instanceof Expirable) {
final CacheEntryImpl entry = (CacheEntryImpl) object;
if (entry.isExpired()) {
systemCache.remove(key);
systemCache.remove(ANNOUNCEMENTS_KEY);
return List.of();
}
return entry.getAnnouncements();
Expand Down

0 comments on commit ace5e2f

Please sign in to comment.