package elw.dao;
import base.pattern.Result;
import com.google.common.io.InputSupplier;
import elw.dao.ctx.CtxAttachment;
import elw.dao.ctx.CtxSlot;
import elw.dao.ctx.CtxSolution;
import elw.dao.rest.RestEnrollment;
import elw.dao.rest.RestEnrollmentSummary;
import elw.dao.rest.RestSolution;
import elw.vo.*;
import org.akraievoy.couch.Squab;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nonnull;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;
/**
* Data access abstraction, which is implemented directly and
* has security-related decorator.
*/
public interface Queries {
void invalidateCaches();
Group group(String groupId);
List<Attachment> attachments(final CtxSlot ctxSlot);
Attachment attachment(Ctx ctxVer, String slotId, String id);
SortedMap<String, List<Solution>> solutions(Ctx ctx);
List<Solution> solutions(CtxSlot ctx);
// FIXME ensure in-place streaming with out-of-band PUT after the entity goes live
Result createFile(
Ctx ctx, FileSlot slot,
FileBase file, InputSupplier<? extends InputStream> inputSupplier,
String contentType
);
List<? extends FileBase> files(
String scope, Ctx ctx, FileSlot slot
);
FileBase file(String scope, Ctx ctx, FileSlot slot, String id);
InputSupplier<InputStream> solutionInput(
@Nonnull CtxSolution ctxSolution, @Nonnull String fileName
);
List<Course> courses();
List<String> courseIds();
Course course(String courseId);
Admin adminSome(String login);
List<Admin> admins();
List<Group> groups();
List<Enrollment> enrollments();
// LATER all methods should be some-ish, as ReST is OK with returning 404s
Enrollment enrollmentSome(String id);
List<Enrollment> enrollmentsForGroup(String groupId);
SortedMap<Long, Score> scores(Ctx ctx, FileSlot slot, Solution file);
Score score(CtxSolution ctx);
Score score(CtxSolution ctx, Long stamp);
long createScore(CtxSolution ctxSolution, Score score);
void updateSolution(Solution solution);
String fileText(
FileBase file, String attachment
) throws IOException;
List<String> fileLines(
FileBase file, String attachment
) throws IOException;
List<String> groupIds();
List<String> enrollmentIds();
Enrollment enrollment(String id);
RestEnrollmentSummary restScores(String enrId, Collection<String> studentIds);
RestEnrollment restEnrollment(String enrId, final String sourceAddress);
Map<String,RestSolution> restSolutions(String enrId, SolutionFilter filter);
boolean createSolution(
final CtxSlot ctxSlot,
final Solution solution,
final String contentType,
final InputSupplier<InputStream> inputSupplier
);
/**
* Way more straightforward context resolution/validation strategy.
*
* Sample couchId is in the form of: <code><pre>
* Solution-ka95-04-aos_w11-3-lr-lr4-4-code-upload_gvovuo9f.txt-gvovuo9g-
* </pre></code>
*
* There're lots of checks towards inconsistent DB, not only
* checks against misbehaving injections.
*
* @param enrollmentId enrollment ID
* @param couchId full Couch ID of the solution
* @param studentFilter extra validation on resolved student object
*
* @return {@link elw.dao.Queries.CtxResolutionState} with SlotCtx and Squab.Path,
* for further resolution
*/
CtxResolutionState resolveSlot(
String enrollmentId,
String couchId,
StudentFilter studentFilter
);
/**
* Proceed with resolution from Slot to Solution scope.
* @see elw.dao.QueriesImpl#resolveSlot(String, String, elw.dao.Queries.StudentFilter)
*/
CtxSolution resolveSolution(
String enrollmentId,
String couchId,
StudentFilter studentFilter
);
InputSupplier<InputStream> attachmentInput(
@Nonnull CtxAttachment ctxAttachment,
@Nonnull String fileName
);
interface StudentFilter {
boolean allows(Student student);
}
RestSolution restSolution(
String enrollmentId,
String solutionId,
StudentFilter studentFilter
);
class CtxResolutionState {
private static final Logger log =
LoggerFactory.getLogger(CtxResolutionState.class);
public static final CtxResolutionState FAILED =
new CtxResolutionState(null, null);
public final CtxSlot ctxSlot;
public final Squab.Path path;
public CtxResolutionState(
final Squab.Path path,
final CtxSlot ctxSlot
) {
this.ctxSlot = ctxSlot;
this.path = path;
}
public boolean complete() {
return path != null && ctxSlot != null;
}
public static CtxResolutionState failed(
final String couchId,
final String message
) {
log.warn("couchId '" + couchId + "': " + message);
return FAILED;
}
public static CtxResolutionState failed(
final String couchId,
final Squab.Path path,
final String message
) {
log.warn("couchId '" + couchId + "': " + message);
return new CtxResolutionState(path, null);
}
}
}