Skip to content

Commit

Permalink
Added endpoints to send company notification email (#1152)
Browse files Browse the repository at this point in the history
* Bump elliptic from 6.5.2 to 6.5.7

Bumps [elliptic](https://github.com/indutny/elliptic) from 6.5.2 to 6.5.7.
- [Commits](indutny/elliptic@v6.5.2...v6.5.7)

---
updated-dependencies:
- dependency-name: elliptic
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <[email protected]>

* allow keypress events for textareas

* Fix data download (#1162)

* User is granted API access on approval (#1163)

* Added endpoints to send notification email

* Data version is now displayed (#1165)

* Addressed PR comments

* Updated Setup Java in Git Actions (#1166)

---------

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Hongxin <[email protected]>
Co-authored-by: bprize15 <[email protected]>
Co-authored-by: bprize15 <[email protected]>
Co-authored-by: Calvin Lu <[email protected]>
  • Loading branch information
6 people authored Oct 11, 2024
1 parent 036a2cf commit 16532d6
Show file tree
Hide file tree
Showing 23 changed files with 605 additions and 178 deletions.
5 changes: 3 additions & 2 deletions .github/workflows/after-master-commit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,10 @@ jobs:
version: ${{ steps.previoustag.outputs.tag }}

- name: 'Setup Java'
uses: actions/setup-java@v1
uses: actions/setup-java@v4
with:
java-version: 8
distribution: 'temurin'
java-version: 11

- name: 'Get Current Version Level'
id: 'version-level'
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/docker-sentry-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ jobs:
- uses: actions/setup-node@v2
with:
node-version: 12.16.1
- uses: actions/setup-java@v2
- uses: actions/setup-java@v4
with:
distribution: 'adopt'
java-version: '11'
distribution: 'temurin'
java-version: 11
- name: Install node.js packages
run: yarn install
- name: Package application with Jib
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/github-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ jobs:
- uses: actions/setup-node@v2
with:
node-version: 12.16.1
- uses: actions/setup-java@v2
- uses: actions/setup-java@v4
with:
distribution: 'adopt'
java-version: '11'
distribution: 'temurin'
java-version: 11
- name: Stop MySQL server
run: sudo /etc/init.d/mysql stop
- name: Install node.js packages
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,9 @@ public enum MailType {
, LIST_OF_UNAPPROVED_USERS(new MailTypeBuilder()
.templateName("listOfUnapprovedUsers")
.description("List of unapproved users"))
, TERMINATION_NOTIFICATION_EMAIL(new MailTypeBuilder()
.templateName("terminationNotificationEmail")
.description("Notifies admins about companies with licenses about to expire"))
, TEST(new MailTypeBuilder()
.templateName("testEmail")
.description("Test"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,7 @@ public interface CompanyRepository extends JpaRepository<Company, Long> {

@Cacheable(cacheResolver = "companyCacheResolver")
Optional<Company> findOneByNameIgnoreCase(String name);

@Query("select c from Company c where c.id in ?1")
List<Company> findCompaniesByIds(List<Long> ids);
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.mskcc.cbio.oncokb.service;

import org.mskcc.cbio.oncokb.domain.Company;
import org.mskcc.cbio.oncokb.domain.enumeration.LicenseStatus;
import org.mskcc.cbio.oncokb.service.dto.CompanyDTO;
import org.mskcc.cbio.oncokb.web.rest.vm.CompanyVM;
Expand Down Expand Up @@ -58,4 +59,7 @@ public interface CompanyService {
* @param id the id of the entity.
*/
void delete(Long id);


List<CompanyDTO> findCompaniesByIds(List<Long> ids);
}
15 changes: 15 additions & 0 deletions src/main/java/org/mskcc/cbio/oncokb/service/MailService.java
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,21 @@ public void sendEmailFromTemplate(UserDTO user, MailType mailType, String subjec
}
}

public void sendInternalEmailFromTemplate(MailType mailType, String subject, String to, Context additionalContext) {
Context context = new Context(Locale.ENGLISH);

if (additionalContext != null)
additionalContext.getVariableNames().forEach(name -> context.setVariable(name, additionalContext.getVariable(name)));

String from = jHipsterProperties.getMail().getFrom();
String content = templateEngine.process("mail/" + mailType.getTemplateName(), context);
try {
sendEmail(to, from, null, subject, content, null, false, true);
} catch (MailException | MessagingException e) {
log.warn("Internal email could not be sent to '{}'", to, e);
}
}

@Async
public void sendActivationEmail(UserDTO user) {
log.debug("Sending activation email to '{}'", user.getEmail());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ public class CompanyTermination implements Serializable {
private Integer notificationDays;
private Instant date;
private String notes;
private Boolean hasBeenNotified;

public Integer getNotificationDays() {
return notificationDays;
Expand All @@ -31,4 +32,12 @@ public String getNotes() {
public void setNotes(String note) {
this.notes = note;
}

public Boolean getHasBeenNotified() {
return hasBeenNotified;
}

public void setHasBeenNotified(Boolean hasBeenNotified) {
this.hasBeenNotified = hasBeenNotified;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
Expand Down Expand Up @@ -214,4 +215,14 @@ private void clearCompanyCaches(Company company) {
Objects.requireNonNull(cacheManager.getCache(this.cacheNameResolver.getCacheName(COMPANIES_BY_NAME_CACHE))).evict(company.getName());
}

@Override
@Transactional(readOnly = true)
public List<CompanyDTO> findCompaniesByIds(List<Long> ids) {
log.debug("Request to get Company : {}", ids);
return companyRepository.findCompaniesByIds(ids)
.stream()
.map(companyMapper::toDto)
.collect(Collectors.toCollection(ArrayList::new));
}

}
34 changes: 34 additions & 0 deletions src/main/java/org/mskcc/cbio/oncokb/web/rest/CompanyResource.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
import org.mskcc.cbio.oncokb.web.rest.vm.VerifyCompanyNameVM;
import org.mskcc.cbio.oncokb.service.dto.CompanyDTO;
import org.mskcc.cbio.oncokb.service.dto.UserDTO;
import org.mskcc.cbio.oncokb.service.dto.companyadditionalinfo.CompanyAdditionalInfoDTO;
import org.mskcc.cbio.oncokb.service.dto.companyadditionalinfo.CompanyLicense;
import org.mskcc.cbio.oncokb.service.dto.companyadditionalinfo.CompanyTermination;

import io.github.jhipster.web.util.ResponseUtil;
import org.slf4j.Logger;
Expand All @@ -28,6 +31,9 @@
import javax.validation.Valid;
import java.net.URI;
import java.net.URISyntaxException;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.Set;
Expand Down Expand Up @@ -195,4 +201,32 @@ public ResponseEntity<Void> deleteCompany(@PathVariable Long id) {
companyService.delete(id);
return ResponseEntity.ok().build();
}

@GetMapping("/companies/licenses-about-to-expire-pending-notification")
public List<Long> getLicensesAboutToExpirePendingNotification() {
log.debug("Request to get Company license is about to expire");
Instant now = Instant.now();
ArrayList<Long> companies = new ArrayList<>();
for (CompanyDTO company : companyService.findAll()) {
Optional<CompanyTermination> optionalTermination = Optional.ofNullable(company.getAdditionalInfo())
.map(CompanyAdditionalInfoDTO::getLicense)
.map(CompanyLicense::getTermination);
if (optionalTermination.isPresent()) {
CompanyTermination termination = optionalTermination.get();
Boolean hasBeenNotified = termination.getHasBeenNotified();
if (hasBeenNotified == null) {
hasBeenNotified = false;
}
Integer notificationDays = termination.getNotificationDays();
Instant terminationDate = termination.getDate();
if (!hasBeenNotified && notificationDays != null && terminationDate != null) {
Instant start = terminationDate.minus(notificationDays, ChronoUnit.DAYS);
if (now.isAfter(start) && now.isBefore(terminationDate)) {
companies.add(company.getId());
}
}
}
}
return companies;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,12 @@
import org.mskcc.cbio.oncokb.service.dto.CompanyDTO;
import org.mskcc.cbio.oncokb.service.dto.TerminationEmailDTO;
import org.mskcc.cbio.oncokb.service.dto.UserDTO;
import org.mskcc.cbio.oncokb.service.dto.companyadditionalinfo.CompanyAdditionalInfoDTO;
import org.mskcc.cbio.oncokb.service.dto.companyadditionalinfo.CompanyLicense;
import org.mskcc.cbio.oncokb.service.dto.companyadditionalinfo.CompanyTermination;
import org.mskcc.cbio.oncokb.service.mapper.UserMapper;
import org.mskcc.cbio.oncokb.web.rest.errors.BadRequestAlertException;
import org.mskcc.cbio.oncokb.web.rest.vm.CompanyVM;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
Expand All @@ -40,7 +44,9 @@
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.server.ResponseStatusException;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
import org.mskcc.cbio.oncokb.config.Constants;
import org.thymeleaf.context.Context;

/**
* REST controller for managing mails.
Expand Down Expand Up @@ -179,8 +185,58 @@ public TerminationEmailDTO getTerminationWarningEmail(@PathVariable Long company
return dto;
}

@PostMapping("mails/termination-warning")
@PostMapping("mails/company-termination-warning")
public void sendTerminationWarningEmail(@Valid @RequestBody TerminationEmailDTO terminationEmailDTO) throws MessagingException {
this.mailService.sendEmail(terminationEmailDTO);
}

@PostMapping("mails/company-termination-notification")
public void sendTerminationNotificationEmail(@Valid @RequestBody List<Long> companyIds) throws MessagingException {
List<CompanyDTO> companiesToNotify = companyService.findCompaniesByIds(companyIds);
EmailAddresses addresses = this.applicationProperties.getEmailAddresses();
Context context = new Context();
final String baseUrl = ServletUriComponentsBuilder.fromCurrentContextPath().build().toUriString();
context.setVariable("baseUrl", baseUrl);
context.setVariable("companies", companiesToNotify);
String to = addresses.getLicenseAddress();
mailService.sendInternalEmailFromTemplate(MailType.TERMINATION_NOTIFICATION_EMAIL, "Licenses are about to expire.", to, context);

for (CompanyDTO company : companiesToNotify) {
if (company.getAdditionalInfo() == null) {
company.setAdditionalInfo(new CompanyAdditionalInfoDTO());
}

CompanyAdditionalInfoDTO additionalInfoDTO = company.getAdditionalInfo();

if (additionalInfoDTO.getLicense() == null) {
additionalInfoDTO.setLicense(new CompanyLicense());
}

CompanyLicense license = additionalInfoDTO.getLicense();

if (license.getTermination() == null) {
license.setTermination(new CompanyTermination());
}

CompanyTermination termination = license.getTermination();

termination.setHasBeenNotified(true);

CompanyVM companyVM = new CompanyVM();
companyVM.setId(company.getId());
companyVM.setName(company.getName());
companyVM.setDescription(company.getDescription());
companyVM.setCompanyType(company.getCompanyType());
companyVM.setLicenseType(company.getLicenseType());
companyVM.setLicenseModel(company.getLicenseModel());
companyVM.setLicenseStatus(company.getLicenseStatus());
companyVM.setBusinessContact(company.getBusinessContact());
companyVM.setLegalContact(company.getLegalContact());
companyVM.setCompanyDomains(company.getCompanyDomains());
companyVM.setAdditionalInfo(company.getAdditionalInfo());

companyService.updateCompany(companyVM);
}

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" lang="en">
<head>
<title>Termination Notification Email</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<p>The following companies licenses are about to expire.</p>
<table>
<thead>
<tr style="text-align: left">
<th>Name</th>
<th>Link to Company Page</th>
</tr>
</thead>
<tbody>
<tr th:each="company : ${companies}">
<td th:text="${company.name}"/>
<td>
<a th:with="url=(@{|${baseUrl}/companies/${company.id}|})" th:href="${url}">Company Page</a>
</td>
</tr>
</tbody>
</table>
</body>
</html>
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ export class NewCompanyForm extends React.Component<INewCompanyFormProps> {
<AvForm
onValidSubmit={this.handleValidSubmit}
onKeyPress={(event: any) => {
if (event.which === 13) {
if (event.which === 13 && event.target.type !== 'textarea') {
event.preventDefault();
}
}}
Expand Down
2 changes: 1 addition & 1 deletion src/main/webapp/app/config/constants.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -821,7 +821,7 @@ export type DataRelease = {
};

export const DATA_RELEASES: DataRelease[] = [
{ date: '09192024', version: 'v4.21' },
{ date: '09252024', version: 'v4.21' },
{ date: '08152024', version: 'v4.20' },
{ date: '07042024', version: 'v4.19' },
{ date: '07022024', version: 'v4.18' },
Expand Down
3 changes: 1 addition & 2 deletions src/main/webapp/app/pages/apiAccessGroup/APIAccessPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ import AuthenticationStore from 'app/store/AuthenticationStore';
import { Linkout } from 'app/shared/links/Linkout';
import { If, Then, Else } from 'react-if';
import { getPageTitle } from 'app/shared/utils/Utils';
import { has } from 'app/shared/utils/LodashUtils';
import { Helmet } from 'react-helmet-async';

type DownloadAvailabilityWithDate = DataRelease & DownloadAvailability;
Expand Down Expand Up @@ -109,7 +108,7 @@ export default class APIAccessPage extends React.Component<{
// do not provide data on version 1
return DATA_RELEASES.filter(
release =>
has(availableVersions, release.version) &&
availableVersions[release.version] &&
!release.version.startsWith('v1')
).reduce((acc, next) => {
const currentVersionData: DownloadAvailability =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ export default function CompanyAdditionalInfo({
...additionalInfo?.license,
termination: {
...additionalInfo?.license?.termination,
hasBeenNotified: false,
date: terminationDate?.toISOString(),
notificationDays:
additionalInfo?.license?.termination
Expand Down Expand Up @@ -145,6 +146,7 @@ export default function CompanyAdditionalInfo({
...additionalInfo?.license,
termination: {
...additionalInfo?.license?.termination,
hasBeenNotified: false,
notificationDays: value
? +value
: ((undefined as unknown) as number),
Expand Down
7 changes: 5 additions & 2 deletions src/main/webapp/app/pages/companyPage/CompanyPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -670,7 +670,7 @@ export default class CompanyPage extends React.Component<ICompanyPage> {
this.editAllModeEnabled = false;
client
.getTerminationWarningEmailUsingGET({
id: this.company.id,
companyId: this.company.id,
})
.then(x => {
this.sendTerminationNoticePayload = x;
Expand Down Expand Up @@ -698,7 +698,10 @@ export default class CompanyPage extends React.Component<ICompanyPage> {
<AvForm
onValidSubmit={this.onValidFormSubmit}
onKeyPress={(event: any) => {
if (event.which === 13) {
if (
event.which === 13 &&
event.target.type !== 'textarea'
) {
event.preventDefault();
}
}}
Expand Down
Loading

0 comments on commit 16532d6

Please sign in to comment.