Skip to content

Commit

Permalink
SAK-49258 Webapi: Add endpoints to get and bulk update various entiti…
Browse files Browse the repository at this point in the history
  • Loading branch information
stetsche authored Jan 29, 2024
1 parent 759c4b2 commit 94dd3e7
Show file tree
Hide file tree
Showing 8 changed files with 1,208 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -11085,7 +11085,10 @@ public void setAvailability(boolean hidden, Time releaseDate, Time retractDate)

@Override
public void setAvailabilityInstant(boolean hidden, Instant releaseDate, Instant retractDate) {
setAvailability(hidden, timeService.newTime(releaseDate.toEpochMilli()), timeService.newTime(retractDate.toEpochMilli()));
// setAvailability accepts null for releaseDate and retractDate, we need to accept it here as well and pass it safely
setAvailability(hidden,
Optional.ofNullable(releaseDate).map(Instant::toEpochMilli).map(timeService::newTime).orElse(null),
Optional.ofNullable(retractDate).map(Instant::toEpochMilli).map(timeService::newTime).orElse(null));
}

public void setHidden()
Expand Down
8 changes: 8 additions & 0 deletions webapi/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,14 @@
<groupId>org.sakaiproject.samigo</groupId>
<artifactId>samigo-services</artifactId>
</dependency>
<dependency>
<groupId>org.sakaiproject.samigo</groupId>
<artifactId>samigo-hibernate</artifactId>
</dependency>
<dependency>
<groupId>org.sakaiproject.lessonbuilder</groupId>
<artifactId>lessonbuilder-api</artifactId>
</dependency>
<dependency>
<groupId>javax.json</groupId>
<artifactId>javax.json-api</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/******************************************************************************
* Copyright 2023 sakaiproject.org Licensed under the Educational
* Community 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://opensource.org/licenses/ECL-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.sakaiproject.webapi.beans;

import org.apache.commons.lang3.ObjectUtils;
import org.sakaiproject.lessonbuildertool.SimplePageItem;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@JsonInclude(Include.NON_NULL)
public class LessonItemRestBean {


private Long id;
private Long pageId;
private Integer sequence;
private Integer type;
private String contentRef;
private String title;
private String format;
private String html;


@JsonIgnore
public boolean isCreatable() {
boolean requiredFieldsPresent = ObjectUtils.allNotNull(
pageId,
type,
title
);

return requiredFieldsPresent;
}

public static LessonItemRestBean of(SimplePageItem lessonItem) {
return LessonItemRestBean.builder()
.id(lessonItem.getId())
.pageId(lessonItem.getPageId())
.sequence(lessonItem.getSequence())
.type(lessonItem.getType())
.contentRef(lessonItem.getSakaiId())
.title(lessonItem.getName())
.format(lessonItem.getFormat())
.html(lessonItem.getHtml())
.build();
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
/******************************************************************************
* Copyright 2023 sakaiproject.org Licensed under the Educational
* Community 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://opensource.org/licenses/ECL-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.sakaiproject.webapi.beans;

import java.time.Instant;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

import org.sakaiproject.api.app.messageforums.OpenForum;
import org.sakaiproject.assignment.api.model.Assignment;
import org.sakaiproject.content.api.ContentEntity;
import org.sakaiproject.entity.api.ResourceProperties;
import org.sakaiproject.tool.assessment.facade.PublishedAssessmentFacade;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@JsonInclude(Include.NON_NULL)
public class SiteEntityRestBean {

private static final String SITE_SEGMENT = "/site/";
private static final String GROUP_SEGMENT = "/group/";

private String id;
private SiteEntityType type;
private String title;
private Instant openDate;
private Instant dueDate;
private Instant closeDate;
private Boolean dateRestricted;
private Set<String> groupRefs;
private Set<TimeExceptionRestBean> timeExceptions;

@SuppressWarnings("unchecked")
public static SiteEntityRestBean of(PublishedAssessmentFacade assessment,
Set<TimeExceptionRestBean> timeExceptions) {
String siteId = assessment.getOwnerSiteId();
Set<String> groupRefs = Optional.ofNullable(assessment.getReleaseToGroups())
.map(Map::keySet)
.map(groupIds -> (Set<String>) groupIds)
.map(groupIds -> groupIds.stream()
.map(groupId -> SITE_SEGMENT + siteId + GROUP_SEGMENT + groupId)
.collect(Collectors.toSet()))
.orElse(Collections.emptySet());
String assessmentId = Optional.ofNullable(assessment.getPublishedAssessmentId())
.map(id -> id.toString())
.orElse(null);

return SiteEntityRestBean.builder()
.id(assessmentId)
.type(SiteEntityType.ASSESSMENT)
.title(assessment.getTitle())
.openDate(Optional.ofNullable(assessment.getStartDate()).map(Date::toInstant).orElse(null))
.dueDate(Optional.ofNullable(assessment.getDueDate()).map(Date::toInstant).orElse(null))
.closeDate(Optional.ofNullable(assessment.getRetractDate()).map(Date::toInstant).orElse(null))
.dateRestricted(true)
.groupRefs(groupRefs)
.timeExceptions(timeExceptions)
.build();
}

public static SiteEntityRestBean of(Assignment assignment) {
Set<String> groupRefs = Assignment.Access.GROUP.equals(assignment.getTypeOfAccess())
? Set.copyOf(assignment.getGroups())
: Collections.emptySet();

return SiteEntityRestBean.builder()
.id(assignment.getId())
.type(SiteEntityType.ASSIGNMENT)
.title(assignment.getTitle())
.openDate(assignment.getOpenDate())
.dueDate(assignment.getDueDate())
.closeDate(assignment.getCloseDate())
.dateRestricted(true)
.groupRefs(groupRefs)
.build();
}

public static SiteEntityRestBean of(OpenForum forum) {
return SiteEntityRestBean.builder()
.id(Optional.ofNullable(forum.getId()).map(id -> id.toString()).orElse(null))
.type(SiteEntityType.FORUM)
.title(forum.getTitle())
.openDate(Optional.ofNullable(forum.getOpenDate()).map(Date::toInstant).orElse(null))
.closeDate(Optional.ofNullable(forum.getCloseDate()).map(Date::toInstant).orElse(null))
.dateRestricted(forum.getAvailabilityRestricted())
.build();
}

@SuppressWarnings("unchecked")
public static SiteEntityRestBean of(ContentEntity resource) {
boolean dateRestricted = resource.getReleaseInstant() != null || resource.getRetractInstant() != null;
SiteEntityType type = resource.isResource() ? SiteEntityType.RESOURCE : SiteEntityType.RESOURCE_FOLDER;

return SiteEntityRestBean.builder()
.id(resource.getId())
.type(type)
.title(resource.getProperties().getProperty(ResourceProperties.PROP_DISPLAY_NAME))
.openDate(resource.getReleaseInstant())
.closeDate(resource.getRetractInstant())
.groupRefs(Set.copyOf(resource.getGroups()))
.dateRestricted(dateRestricted)
.build();
}

public static Comparator<SiteEntityRestBean> comparator() {
return Comparator.comparing(SiteEntityRestBean::getType)
.thenComparing(SiteEntityRestBean::getTitle);
}


public enum SiteEntityType {
ASSESSMENT,
ASSIGNMENT,
FORUM,
RESOURCE,
RESOURCE_FOLDER,
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/******************************************************************************
* Copyright 2023 sakaiproject.org Licensed under the Educational
* Community 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://opensource.org/licenses/ECL-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.sakaiproject.webapi.beans;

import java.time.Instant;
import java.util.Date;
import java.util.Optional;

import org.apache.commons.lang3.StringUtils;
import org.sakaiproject.tool.assessment.data.dao.assessment.ExtendedTime;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(of = "forEntityRef")
@JsonInclude(Include.NON_NULL)
public class TimeExceptionRestBean {

private static final String SITE_SEGMENT = "/site/";
private static final String GROUP_SEGMENT = "/group/";
private static final String USER_SEGMENT = "/user/";

private String forEntityRef;
private Instant openDate;
private Instant dueDate;
private Instant closeDate;


@JsonIgnore
public boolean isValid() {
boolean refValid = StringUtils.containsAny(forEntityRef, GROUP_SEGMENT, USER_SEGMENT);

boolean datesValid = openDate != null && dueDate != null
&& (dueDate.isAfter(openDate) || dueDate.equals(openDate))
&& (closeDate == null || closeDate.isAfter(dueDate) || closeDate.equals(dueDate));

return refValid && datesValid;
}

public ExtendedTime toExtendedTime() {
ExtendedTime extendedTime = new ExtendedTime();

if (openDate != null) {
extendedTime.setStartDate(Date.from(openDate));
}

if (dueDate != null) {
extendedTime.setDueDate(Date.from(dueDate));
}

if (closeDate != null) {
extendedTime.setRetractDate(Date.from(closeDate));
}

if (StringUtils.contains(forEntityRef, GROUP_SEGMENT)) {
extendedTime.setGroup(StringUtils.substringAfter(forEntityRef, GROUP_SEGMENT));
} else {
extendedTime.setUser(StringUtils.substringAfter(forEntityRef, USER_SEGMENT));
}

return extendedTime;
}

public static TimeExceptionRestBean of(String siteId, ExtendedTime extendedTime) {
String entityRef = Optional.ofNullable(StringUtils.trimToNull(extendedTime.getUser()))
.map(userId -> USER_SEGMENT + userId)
.or(() -> Optional.ofNullable(StringUtils.trimToNull(extendedTime.getGroup()))
.map(groupId -> SITE_SEGMENT + siteId + GROUP_SEGMENT + groupId))
.orElse(null);
return TimeExceptionRestBean.builder()
.forEntityRef(entityRef)
.openDate(Optional.ofNullable(extendedTime.getStartDate()).map(Date::toInstant).orElse(null))
.dueDate(Optional.ofNullable(extendedTime.getDueDate()).map(Date::toInstant).orElse(null))
.closeDate(Optional.ofNullable(extendedTime.getRetractDate()).map(Date::toInstant).orElse(null))
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@

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

import org.apache.commons.lang3.StringUtils;
import org.sakaiproject.authz.api.SecurityService;
Expand Down Expand Up @@ -104,6 +106,28 @@ public ResponseEntity<Condition> createCondition(@PathVariable String siteId, @R
return ResponseEntity.badRequest().build();
}

@PostMapping(value = "/sites/{siteId}/conditions/bulk", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Set<Condition>> createConditions(@PathVariable String siteId, @RequestBody Set<Condition> conditions) {
Session session = checkSakaiSession();
Site site = checkSite(siteId);

if (!canUpdateCondition(session.getUserId(), site.getReference())) {
return ResponseEntity.status(HttpStatus.FORBIDDEN).build();
}

for (Condition condition : conditions) {
if (!(StringUtils.isBlank(condition.getId()) && StringUtils.equals(siteId, condition.getSiteId()))) {
return ResponseEntity.badRequest().build();
}
}

Set<Condition> savedConditions = conditions.stream()
.map(conditionService::saveCondition)
.collect(Collectors.toSet());

return ResponseEntity.ok(savedConditions);
}

@PutMapping(value = "/sites/{siteId}/conditions/{conditionId}", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Condition> updateCondition(@PathVariable String siteId, @PathVariable String conditionId,
@RequestBody Condition condition) {
Expand Down
Loading

0 comments on commit 94dd3e7

Please sign in to comment.