master moved from 68df28d58697 to 79eb8a4e32f8 3 new revisions: Revision: 66f6fc428e9c Author: Andrew Lindesay <apl@xxxxxxxxxxxxxx> Date: Wed Feb 12 10:07:08 2014 UTC Log: + various small fixes... http://code.google.com/p/haiku-depot-web-app/source/detail?r=66f6fc428e9c Revision: b52dfe053b09 Author: Andrew Lindesay <apl@xxxxxxxxxxxxxx> Date: Wed Feb 12 10:37:01 2014 UTC Log: + integration test for the pkg screenshot controller http://code.google.com/p/haiku-depot-web-app/source/detail?r=b52dfe053b09 Revision: 79eb8a4e32f8 Author: Andrew Lindesay <apl@xxxxxxxxxxxxxx> Date: Fri Feb 14 08:42:33 2014 UTC Log: + integration test for the pkg icon controller http://code.google.com/p/haiku-depot-web-app/source/detail?r=79eb8a4e32f8 ============================================================================== Revision: 66f6fc428e9c Author: Andrew Lindesay <apl@xxxxxxxxxxxxxx> Date: Wed Feb 12 10:07:08 2014 UTC Log: + various small fixes + changes to support screenshot api + tests http://code.google.com/p/haiku-depot-web-app/source/detail?r=66f6fc428e9c Added:/haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/pkg/GetPkgScreenshotsRequest.java /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/pkg/GetPkgScreenshotsResult.java /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/pkg/RemovePkgIconRequest.java /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/pkg/RemovePkgIconResult.java /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/pkg/RemovePkgScreenshotRequest.java /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/pkg/RemovePkgScreenshotResult.java /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/pkg/ReorderPkgScreenshotsRequest.java /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/pkg/ReorderPkgScreenshotsResult.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/pkg/controller/PkgScreenshotController.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/pkg/model/BadPkgScreenshotException.java
/haikudepotserver-webapp/src/main/webapp/img/favicon.ico /haikudepotserver-webapp/src/test/resources/sample-320x240.png Deleted:/haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/pkg/RemoveIconRequest.java /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/pkg/RemoveIconResult.java
Modified:/haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/PkgApi.java
/haikudepotserver-docs/src/main/latex/docs/part-api.tex /haikudepotserver-parent/pom.xml /haikudepotserver-webapp/pom.xml/haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/api1/PkgApiImpl.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/Pkg.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/PkgScreenshot.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/pkg/PkgService.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/pkg/controller/PkgIconController.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/security/AuthorizationService.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/security/model/Permission.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/support/ImageHelper.java
/haikudepotserver-webapp/src/main/resources/HaikuDepot.map.xml /haikudepotserver-webapp/src/main/resources/spring/servlet-context.xml /haikudepotserver-webapp/src/main/webapp/js/app/controller/more.html/haikudepotserver-webapp/src/main/webapp/js/app/controller/morecontroller.js /haikudepotserver-webapp/src/main/webapp/js/app/controller/runtimeinformation.html /haikudepotserver-webapp/src/main/webapp/js/app/controller/viewpkgcontroller.js /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/api1/PkgApiIT.java /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/api1/support/IntegrationTestSupportService.java
======================================= --- /dev/null+++ /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/pkg/GetPkgScreenshotsRequest.java Wed Feb 12 10:07:08 2014 UTC
@@ -0,0 +1,18 @@ +/* + * Copyright 2014, Andrew Lindesay + * Distributed under the terms of the MIT License. + */ + +package org.haikuos.haikudepotserver.api1.model.pkg; + +public class GetPkgScreenshotsRequest { + + public String pkgName; + + public GetPkgScreenshotsRequest() { + } + + public GetPkgScreenshotsRequest(String pkgName) { + this.pkgName = pkgName; + } +} ======================================= --- /dev/null+++ /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/pkg/GetPkgScreenshotsResult.java Wed Feb 12 10:07:08 2014 UTC
@@ -0,0 +1,18 @@ +/* + * Copyright 2014, Andrew Lindesay + * Distributed under the terms of the MIT License. + */ + +package org.haikuos.haikudepotserver.api1.model.pkg; + +import java.util.List; + +public class GetPkgScreenshotsResult { + + public List<PkgScreenshot> items; + + public static class PkgScreenshot { + public String code; + } + +} ======================================= --- /dev/null+++ /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/pkg/RemovePkgIconRequest.java Wed Feb 12 10:07:08 2014 UTC
@@ -0,0 +1,28 @@ +/* + * Copyright 2014, Andrew Lindesay + * Distributed under the terms of the MIT License. + */ + +package org.haikuos.haikudepotserver.api1.model.pkg; + +public class RemovePkgIconRequest { + + /**+ * <p>This is the name of the package that you wish to reset the icon for.</p>
+ */ + + public String name; + + public RemovePkgIconRequest() { + } + + + public RemovePkgIconRequest(String name) { + + if(null==name || 0==name.length()) {+ throw new IllegalArgumentException("the name must be supplied when removing the icon for a package");
+ } + + this.name = name; + } +} ======================================= --- /dev/null+++ /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/pkg/RemovePkgIconResult.java Wed Feb 12 10:07:08 2014 UTC
@@ -0,0 +1,4 @@ +package org.haikuos.haikudepotserver.api1.model.pkg; + +public class RemovePkgIconResult { +} ======================================= --- /dev/null+++ /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/pkg/RemovePkgScreenshotRequest.java Wed Feb 12 10:07:08 2014 UTC
@@ -0,0 +1,19 @@ +/* + * Copyright 2014, Andrew Lindesay + * Distributed under the terms of the MIT License. + */ + +package org.haikuos.haikudepotserver.api1.model.pkg; + +public class RemovePkgScreenshotRequest { + + public String code; + + public RemovePkgScreenshotRequest() { + } + + public RemovePkgScreenshotRequest(String code) { + this.code = code; + } + +} ======================================= --- /dev/null+++ /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/pkg/RemovePkgScreenshotResult.java Wed Feb 12 10:07:08 2014 UTC
@@ -0,0 +1,9 @@ +/* + * Copyright 2014, Andrew Lindesay + * Distributed under the terms of the MIT License. + */ + +package org.haikuos.haikudepotserver.api1.model.pkg; + +public class RemovePkgScreenshotResult { +} ======================================= --- /dev/null+++ /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/pkg/ReorderPkgScreenshotsRequest.java Wed Feb 12 10:07:08 2014 UTC
@@ -0,0 +1,28 @@ +/* + * Copyright 2014, Andrew Lindesay + * Distributed under the terms of the MIT License. + */ + +package org.haikuos.haikudepotserver.api1.model.pkg; + +import java.util.List; + +public class ReorderPkgScreenshotsRequest { + + public String pkgName; + + /**+ * <p>This is an ordered list of codes that describe the ordering desired for the screenshots of this package.</p>
+ */ + + public List<String> codes; + + public ReorderPkgScreenshotsRequest() { + } ++ public ReorderPkgScreenshotsRequest(String pkgName, List<String> codes) {
+ this.pkgName = pkgName; + this.codes = codes; + } + +} ======================================= --- /dev/null+++ /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/pkg/ReorderPkgScreenshotsResult.java Wed Feb 12 10:07:08 2014 UTC
@@ -0,0 +1,9 @@ +/* + * Copyright 2014, Andrew Lindesay + * Distributed under the terms of the MIT License. + */ + +package org.haikuos.haikudepotserver.api1.model.pkg; + +public class ReorderPkgScreenshotsResult { +} ======================================= --- /dev/null+++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/pkg/controller/PkgScreenshotController.java Wed Feb 12 10:07:08 2014 UTC
@@ -0,0 +1,245 @@ +/* + * Copyright 2014, Andrew Lindesay + * Distributed under the terms of the MIT License. + */ + +package org.haikuos.haikudepotserver.pkg.controller; + +import com.google.common.base.Optional; +import com.google.common.base.Strings; +import com.google.common.net.HttpHeaders; +import com.google.common.net.MediaType; +import org.apache.cayenne.ObjectContext; +import org.apache.cayenne.configuration.server.ServerRuntime; +import org.haikuos.haikudepotserver.dataobjects.Pkg; +import org.haikuos.haikudepotserver.dataobjects.PkgScreenshot; +import org.haikuos.haikudepotserver.dataobjects.User; +import org.haikuos.haikudepotserver.pkg.PkgService; +import org.haikuos.haikudepotserver.pkg.model.BadPkgScreenshotException; +import org.haikuos.haikudepotserver.security.AuthorizationService; +import org.haikuos.haikudepotserver.security.model.Permission; +import org.haikuos.haikudepotserver.support.ByteCounterOutputStream; +import org.haikuos.haikudepotserver.support.NoOpOutputStream; +import org.haikuos.haikudepotserver.support.web.AbstractController;+import org.haikuos.haikudepotserver.web.controller.WebResourceGroupController;
+import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +@Controller +@RequestMapping("/pkgscreenshot") +public class PkgScreenshotController extends AbstractController { ++ protected static Logger logger = LoggerFactory.getLogger(WebResourceGroupController.class);
++ public final static String HEADER_SCREENSHOTCODE = "X-HaikuDepotServer-ScreenshotCode";
+ + public final static String KEY_PKGNAME = "pkgname"; + public final static String KEY_SCREENSHOTCODE = "code"; + public final static String KEY_FORMAT = "format"; + public final static String KEY_TARGETWIDTH = "tw"; + public final static String KEY_TARGETHEIGHT = "th"; + + protected static int SCREENSHOT_SIDE_LIMIT = 1500; + + @Resource + ServerRuntime serverRuntime; + + @Resource + PkgService pkgService; + + @Resource + AuthorizationService authorizationService; ++ @RequestMapping(value = "/{"+KEY_SCREENSHOTCODE+"}.{"+KEY_FORMAT+"}", method = RequestMethod.HEAD)
+ public void fetchHead( + HttpServletRequest request, + HttpServletResponse response, + @RequestParam(value = KEY_TARGETWIDTH) Integer targetWidth, + @RequestParam(value = KEY_TARGETHEIGHT) Integer targetHeight, + @PathVariable(value = KEY_FORMAT) String format,+ @PathVariable(value = KEY_SCREENSHOTCODE) String screenshotCode)
+ throws IOException { ++ if(null!=targetWidth && (targetWidth <= 0 || targetWidth > SCREENSHOT_SIDE_LIMIT)) {
+ throw new BadSize(); + } ++ if(null!=targetHeight && (targetHeight <= 0 || targetHeight > SCREENSHOT_SIDE_LIMIT)) {
+ throw new BadSize(); + } + + if(null==targetHeight) { + targetHeight = SCREENSHOT_SIDE_LIMIT * 2; + } + + if(null==targetWidth) { + targetWidth = SCREENSHOT_SIDE_LIMIT * 2; + } + + if(Strings.isNullOrEmpty(screenshotCode)) { + throw new MissingScreenshotCode(); + } + + if(Strings.isNullOrEmpty(format) || !"png".equals(format)) { + throw new MissingOrBadFormat(); + } + + ObjectContext context = serverRuntime.getContext();+ Optional<PkgScreenshot> screenshotOptional = PkgScreenshot.getByCode(context, screenshotCode);
+ + if(!screenshotOptional.isPresent()) { + throw new ScreenshotNotFound(); + } ++ ByteCounterOutputStream byteCounter = new ByteCounterOutputStream(new NoOpOutputStream());
+ + pkgService.writePkgScreenshotImage( + byteCounter, + context, + screenshotOptional.get(), + targetWidth, + targetHeight); ++ response.setHeader(HttpHeaders.CONTENT_LENGTH, Long.toString(byteCounter.getCounter()));
+ response.setContentType(MediaType.PNG.toString()); + response.setDateHeader( + HttpHeaders.LAST_MODIFIED,+ screenshotOptional.get().getPkg().getModifyTimestampSecondAccuracy().getTime());
+ + } ++ @RequestMapping(value = "/{"+KEY_SCREENSHOTCODE+"}.{"+KEY_FORMAT+"}", method = RequestMethod.GET)
+ public void fetchGet( + HttpServletRequest request, + HttpServletResponse response,+ @RequestParam(value = KEY_TARGETWIDTH, required = true) int targetWidth, + @RequestParam(value = KEY_TARGETHEIGHT, required = true) int targetHeight,
+ @PathVariable(value = KEY_FORMAT) String format,+ @PathVariable(value = KEY_SCREENSHOTCODE) String screenshotCode)
+ throws IOException { + + if(targetWidth <= 0 || targetWidth > SCREENSHOT_SIDE_LIMIT) { + throw new BadSize(); + } + + if(targetHeight <= 0 || targetHeight > SCREENSHOT_SIDE_LIMIT) { + throw new BadSize(); + } + + if(Strings.isNullOrEmpty(screenshotCode)) { + throw new MissingScreenshotCode(); + } + + if(Strings.isNullOrEmpty(format) || !"png".equals(format)) { + throw new MissingOrBadFormat(); + } + + ObjectContext context = serverRuntime.getContext();+ Optional<PkgScreenshot> screenshotOptional = PkgScreenshot.getByCode(context, screenshotCode);
+ + if(!screenshotOptional.isPresent()) { + throw new ScreenshotNotFound(); + } ++ ByteCounterOutputStream byteCounter = new ByteCounterOutputStream(new NoOpOutputStream());
++ response.setHeader(HttpHeaders.CONTENT_LENGTH, Long.toString(byteCounter.getCounter()));
+ response.setContentType(MediaType.PNG.toString()); + response.setDateHeader( + HttpHeaders.LAST_MODIFIED,+ screenshotOptional.get().getPkg().getModifyTimestampSecondAccuracy().getTime());
+ + pkgService.writePkgScreenshotImage( + response.getOutputStream(), + context, + screenshotOptional.get(), + targetWidth, + targetHeight); + } + + /**+ * <p>This handler will take-up an HTTP PUT that provides a new screenshot for the package.</p>
+ */ ++ @RequestMapping(value = "/{"+KEY_PKGNAME+"}.{"+KEY_FORMAT+"}", method = RequestMethod.PUT)
+ public void put( + HttpServletRequest request, + HttpServletResponse response, + @PathVariable(value = KEY_FORMAT) String format,+ @PathVariable(value = KEY_PKGNAME) String pkgName) throws IOException {
++ if(Strings.isNullOrEmpty(pkgName) | | !Pkg.NAME_PATTERN.matcher(pkgName).matches()) {
+ throw new MissingPkgName(); + } + + if(Strings.isNullOrEmpty(format) || !"png".equals(format)) { + throw new MissingOrBadFormat(); + } + + ObjectContext context = serverRuntime.getContext(); + + Optional<Pkg> pkg = Pkg.getByName(context, pkgName); + + if(!pkg.isPresent()) { + throw new PkgNotFound(); + } + + // check the authorization + + Optional<User> user = tryObtainAuthenticatedUser(context); ++ if(!authorizationService.check(context, user.orNull(), pkg.get(), Permission.PKG_EDITSCREENSHOT)) { + logger.warn("attempt to add a pkg screenshot, but there is no user present or that user is not able to edit the pkg");
+ throw new PkgAuthorizationFailure(); + } + + String screenshotCode; + + try { + screenshotCode = pkgService.storePkgScreenshotImage( + request.getInputStream(), + context, + pkg.get()).getCode(); + } + catch(BadPkgScreenshotException badIcon) { + throw new MissingOrBadFormat(); + } + + context.commitChanges(); + + response.setHeader(HEADER_SCREENSHOTCODE,screenshotCode); + response.setStatus(HttpServletResponse.SC_OK); + } ++ // these are the various errors that can arise in supplying or providing a package icon.
++ @ResponseStatus(value= HttpStatus.UNSUPPORTED_MEDIA_TYPE, reason="the target width or height must be greater than zero and less than the maximum")
+ public class BadSize extends RuntimeException {} ++ @ResponseStatus(value= HttpStatus.BAD_REQUEST, reason="the package name must be supplied")
+ public class MissingPkgName extends RuntimeException {} ++ @ResponseStatus(value= HttpStatus.BAD_REQUEST, reason="the screenshot code must be supplied")
+ public class MissingScreenshotCode extends RuntimeException {} ++ @ResponseStatus(value= HttpStatus.UNSUPPORTED_MEDIA_TYPE, reason="the format must be supplied and must (presently) be 'png'")
+ public class MissingOrBadFormat extends RuntimeException {} ++ @ResponseStatus(value= HttpStatus.NOT_FOUND, reason="the requested package was unable to found")
+ public class PkgNotFound extends RuntimeException {} ++ @ResponseStatus(value= HttpStatus.NOT_FOUND, reason="the requested screenshot was unable to found")
+ public class ScreenshotNotFound extends RuntimeException {} ++ @ResponseStatus(value= HttpStatus.UNAUTHORIZED, reason="the requested package cannot be edited by the authenticated user")
+ public class PkgAuthorizationFailure extends RuntimeException {} + +} ======================================= --- /dev/null+++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/pkg/model/BadPkgScreenshotException.java Wed Feb 12 10:07:08 2014 UTC
@@ -0,0 +1,9 @@ +/* + * Copyright 2013, Andrew Lindesay + * Distributed under the terms of the MIT License. + */ + +package org.haikuos.haikudepotserver.pkg.model; + +public class BadPkgScreenshotException extends Exception { +} ======================================= --- /dev/null+++ /haikudepotserver-webapp/src/main/webapp/img/favicon.ico Wed Feb 12 10:07:08 2014 UTC
Binary file, no diff available. ======================================= --- /dev/null+++ /haikudepotserver-webapp/src/test/resources/sample-320x240.png Wed Feb 12 10:07:08 2014 UTC
Binary file, no diff available. =======================================--- /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/pkg/RemoveIconRequest.java Sat Jan 18 09:59:17 2014 UTC
+++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2014, Andrew Lindesay - * Distributed under the terms of the MIT License. - */ - -package org.haikuos.haikudepotserver.api1.model.pkg; - -public class RemoveIconRequest { - - /**- * <p>This is the name of the package that you wish to reset the icon for.</p>
- */ - - public String name; - - public RemoveIconRequest() { - } - - - public RemoveIconRequest(String name) { - - if(null==name || 0==name.length()) {- throw new IllegalArgumentException("the name must be supplied when removing the icon for a package");
- } - - this.name = name; - } -} =======================================--- /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/pkg/RemoveIconResult.java Thu Dec 5 09:23:22 2013 UTC
+++ /dev/null @@ -1,4 +0,0 @@ -package org.haikuos.haikudepotserver.api1.model.pkg; - -public class RemoveIconResult { -} =======================================--- /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/PkgApi.java Thu Dec 5 09:23:22 2013 UTC +++ /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/PkgApi.java Wed Feb 12 10:07:08 2014 UTC
@@ -1,5 +1,5 @@ /* - * Copyright 2013, Andrew Lindesay + * Copyright 2013-2014, Andrew Lindesay * Distributed under the terms of the MIT License. */ @@ -34,6 +34,31 @@ * <p>This request will remove any icons from the package.</p> */- RemoveIconResult removeIcon(RemoveIconRequest request) throws ObjectNotFoundException; + RemovePkgIconResult removePkgIcon(RemovePkgIconRequest request) throws ObjectNotFoundException;
+ + /**+ * <p>This method will return an ordered list of the screenshots that are available for this package. It will + * throw an {@link org.haikuos.haikudepotserver.api1.support.ObjectNotFoundException} in the case where the
+ * nominated package is not able to be found.</p> + */ ++ GetPkgScreenshotsResult getPkgScreenshots(GetPkgScreenshotsRequest getPkgScreenshotsRequest) throws ObjectNotFoundException;
+ + /**+ * <p>This method will remove the nominated screenshot from the package. If the screenshot is not able to be
+ * found using the code supplied, the method will throw an instance of+ * {@link org.haikuos.haikudepotserver.api1.support.ObjectNotFoundException}.</p>
+ */ ++ RemovePkgScreenshotResult removePkgScreenshot(RemovePkgScreenshotRequest removePkgScreenshotRequest) throws ObjectNotFoundException;
+ + /**+ * <p>This method will reorder the screenshots related to the nominated package. If any of the screenshots are + * not accounted for, they will be ordered at the end in an indeterminate manner. If the package is not able to be
+ * found given the name supplied, an instance of+ * {@link org.haikuos.haikudepotserver.api1.support.ObjectNotFoundException} will be thrown.</p>
+ */ ++ ReorderPkgScreenshotsResult reorderPkgScreenshots(ReorderPkgScreenshotsRequest reorderPkgScreenshotsRequest) throws ObjectNotFoundException;
} =======================================--- /haikudepotserver-docs/src/main/latex/docs/part-api.tex Sun Feb 9 10:38:52 2014 UTC +++ /haikudepotserver-docs/src/main/latex/docs/part-api.tex Wed Feb 12 10:07:08 2014 UTC
@@ -166,7 +166,7 @@ \end{itemize} \item Expected HTTP Status Codes \begin{itemize} - \item {\bf 200} : The icon is provided in the response + \item {\bf 200} : The icon is provided in the response (for GET) \item {\bf 415} : The path did not include ".png" or the size is invalid \item {\bf 400} : The package name was not supplied \item {\bf 404} : The package was not found @@ -200,6 +200,57 @@ An example URL is; \framebox{\tt http://localhost:8080/pkgicon/apr.png?size=32} + +\subsubsection{Get Screenshot Image} ++This API is able to produce an image for a screenshot that can be identified by its code. The request will return a {\tt Last-Modified} header. The timestamps here will correlate to the {\it modifyTimestamp} that is provided in API responses such as {\tt GetPkResult} and {\tt SearchPkgsResult}. The value for {\it modifyTimestamp} will be at millisecond resolution, but the HTTP headers will be at second resolution. Requests for screenshot image should be accompanied by a target width and height. These values must be within a range of 1..1500. The image will maintain its aspect ratio as it is scaled.
+ +\begin{itemize} +\item HTTP Method : GET, HEAD +\item Path : /pkgscreenshot/$<$screenshotcode$>$.png +\item Response Content-Type : image/png +\item Query Parameters + \begin{itemize}+ \item {\bf tw} : An integer that describes the width that the image should be scaled to + \item {\bf th} : An integer that describes the height that the image should be scaled to
+ \end{itemize} +\item Expected HTTP Status Codes + \begin{itemize} + \item {\bf 200} : The icon is provided in the response (for GET)+ \item {\bf 415} : The path did not include ".png" or the target width or height is invalid
+ \item {\bf 400} : The screenshot code was not supplied + \item {\bf 404} : The screenshot was not found + \end{itemize} +\end{itemize} + +An example URL is; ++\framebox{\tt http://localhost:8080/pkgscreenshot/a78hw20fh2p20fh122jd92.png?tw=640\&th=480}
+ +\subsubsection{Put Screenshot Image} ++This API is able to store an image for a screenshot for the nominated package. The screenshot will be ordered last. The payload of the PUT must be a PNG image.
+ +\begin{itemize} +\item HTTP Method : PUT +\item Path : /pkgscreenshot/$<$pkgname$>$.png +\item Expected HTTP Status Codes + \begin{itemize} + \item {\bf 200} : The screenshot image was stored+ \item {\bf 415} : The path did not include ".png" or the size of the image is invalid or the payload is not PNG image data. + \item {\bf 404} : The package identified in the path was not able to be found
+ \item {\bf 400} : The package name was not supplied + \end{itemize} +\item Specific Response Headers + \begin{itemize}+ \item {\bf X-HaikuDepotServer-ScreenshotCode} : Supplies the code of the newly created screenshot.
+ \end{itemize} +\end{itemize} + +An example URL is; + +\framebox{\tt http://localhost:8080/pkgscreenshot/apr.png} + ======================================= --- /haikudepotserver-parent/pom.xml Sat Feb 8 09:19:06 2014 UTC +++ /haikudepotserver-parent/pom.xml Wed Feb 12 10:07:08 2014 UTC @@ -91,6 +91,13 @@ <version>7.0.39</version> </dependency> + <!-- MEDIA --> + <dependency> + <groupId>org.imgscalr</groupId> + <artifactId>imgscalr-lib</artifactId> + <version>4.2</version> + </dependency> + <!-- SPRING --> <dependency> <groupId>org.springframework</groupId> ======================================= --- /haikudepotserver-webapp/pom.xml Fri Jan 31 10:26:48 2014 UTC +++ /haikudepotserver-webapp/pom.xml Wed Feb 12 10:07:08 2014 UTC @@ -112,6 +112,12 @@ <artifactId>tomcat-jdbc</artifactId> </dependency> + <!-- MEDIA --> + <dependency> + <groupId>org.imgscalr</groupId> + <artifactId>imgscalr-lib</artifactId> + </dependency> + <!-- SPRING --> <dependency> <groupId>org.springframework</groupId> =======================================--- /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/api1/PkgApiImpl.java Sat Feb 8 09:19:06 2014 UTC +++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/api1/PkgApiImpl.java Wed Feb 12 10:07:08 2014 UTC
@@ -247,7 +247,7 @@ } @Override- public RemoveIconResult removeIcon(RemoveIconRequest request) throws ObjectNotFoundException { + public RemovePkgIconResult removePkgIcon(RemovePkgIconRequest request) throws ObjectNotFoundException {
Preconditions.checkNotNull(request); Preconditions.checkState(!Strings.isNullOrEmpty(request.name)); @@ -278,7 +278,96 @@logger.info("did remove icons for pkg {}",pkgOptional.get().getName());
- return new RemoveIconResult(); + return new RemovePkgIconResult(); + } + + @Override+ public GetPkgScreenshotsResult getPkgScreenshots(GetPkgScreenshotsRequest getPkgScreenshotsRequest) throws ObjectNotFoundException {
+ Preconditions.checkNotNull(getPkgScreenshotsRequest); + Preconditions.checkNotNull(getPkgScreenshotsRequest.pkgName); + + final ObjectContext context = serverRuntime.getContext();+ Optional<Pkg> pkgOptional = Pkg.getByName(context, getPkgScreenshotsRequest.pkgName);
+ + if(!pkgOptional.isPresent()) {+ throw new ObjectNotFoundException(Pkg.class.getSimpleName(), getPkgScreenshotsRequest.pkgName);
+ } + + GetPkgScreenshotsResult result = new GetPkgScreenshotsResult(); + result.items = Lists.transform( + pkgOptional.get().getSortedPkgScreenshots(),+ new Function<PkgScreenshot, GetPkgScreenshotsResult.PkgScreenshot>() {
+ @Override+ public GetPkgScreenshotsResult.PkgScreenshot apply(PkgScreenshot pkgScreenshot) { + GetPkgScreenshotsResult.PkgScreenshot rs = new GetPkgScreenshotsResult.PkgScreenshot();
+ rs.code = pkgScreenshot.getCode(); + return rs; + } + } + ); + + return result; + } + + @Override+ public RemovePkgScreenshotResult removePkgScreenshot(RemovePkgScreenshotRequest removePkgScreenshotRequest) throws ObjectNotFoundException {
+ Preconditions.checkNotNull(removePkgScreenshotRequest); + Preconditions.checkNotNull(removePkgScreenshotRequest.code); + + final ObjectContext context = serverRuntime.getContext();+ Optional<PkgScreenshot> screenshotOptional = PkgScreenshot.getByCode(context, removePkgScreenshotRequest.code);
+ + if(!screenshotOptional.isPresent()) {+ throw new ObjectNotFoundException(PkgScreenshot.class.getSimpleName(), removePkgScreenshotRequest.code);
+ } + + User authUser = obtainAuthenticatedUser(context); + Pkg pkg = screenshotOptional.get().getPkg(); ++ if(!authorizationService.check(context, authUser, pkg, Permission.PKG_EDITSCREENSHOT)) {
+ throw new AuthorizationFailureException(); + } ++ pkg.removeToManyTarget(Pkg.PKG_SCREENSHOTS_PROPERTY, screenshotOptional.get(), true);
++ Optional<PkgScreenshotImage> image = screenshotOptional.get().getPkgScreenshotImage();
+ + if(image.isPresent()) { + context.deleteObjects(image.get()); + } + + context.deleteObjects(screenshotOptional.get()); + context.commitChanges(); ++ logger.info("did remove the screenshot {} on package {}", removePkgScreenshotRequest.code, pkg.getName());
+ + return new RemovePkgScreenshotResult(); } + @Override+ public ReorderPkgScreenshotsResult reorderPkgScreenshots(ReorderPkgScreenshotsRequest reorderPkgScreenshotsRequest) throws ObjectNotFoundException {
+ Preconditions.checkNotNull(reorderPkgScreenshotsRequest); + Preconditions.checkNotNull(reorderPkgScreenshotsRequest.pkgName); + Preconditions.checkNotNull(reorderPkgScreenshotsRequest.codes); + + final ObjectContext context = serverRuntime.getContext();+ Optional<Pkg> pkgOptional = Pkg.getByName(context, reorderPkgScreenshotsRequest.pkgName);
+ + if(!pkgOptional.isPresent()) {+ throw new ObjectNotFoundException(Pkg.class.getSimpleName(), reorderPkgScreenshotsRequest.pkgName);
+ } + + User authUser = obtainAuthenticatedUser(context); ++ if(!authorizationService.check(context, authUser, pkgOptional.get(), Permission.PKG_EDITSCREENSHOT)) {
+ throw new AuthorizationFailureException(); + } ++ pkgOptional.get().reorderPkgScreenshots(reorderPkgScreenshotsRequest.codes);
+ context.commitChanges(); ++ logger.info("did reorder the screenshots on package {}", pkgOptional.get().getName());
+ + return null; + } } =======================================--- /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/Pkg.java Sun Feb 9 10:00:28 2014 UTC +++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/Pkg.java Wed Feb 12 10:07:08 2014 UTC
@@ -7,7 +7,9 @@ import com.google.common.base.Optional; import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; import org.apache.cayenne.ObjectContext; import org.apache.cayenne.exp.ExpressionFactory; import org.apache.cayenne.query.SelectQuery; @@ -16,6 +18,8 @@ import org.haikuos.haikudepotserver.dataobjects.auto._Pkg;import org.haikuos.haikudepotserver.dataobjects.support.CreateAndModifyTimestamped;
+import java.util.Collections; +import java.util.Comparator; import java.util.Date; import java.util.List; import java.util.regex.Pattern; @@ -90,5 +94,68 @@ public Date getModifyTimestampSecondAccuracy() {return new java.util.Date((getModifyTimestamp().getTime() / 1000) * 1000);
} + + public List<PkgScreenshot> getSortedPkgScreenshots() {+ List<PkgScreenshot> screenshots = Lists.newArrayList(getPkgScreenshots());
+ Collections.sort(screenshots); + return screenshots; + } + + public Optional<Integer> getHighestPkgScreenshotOrdering() { + List<PkgScreenshot> screenshots = getSortedPkgScreenshots(); + + if(screenshots.isEmpty()) { + return Optional.absent(); + } ++ return Optional.of(screenshots.get(screenshots.size()-1).getOrdering());
+ } + + /**+ * <p>This method will re-order the screenshots according to the set of codes present in the supplied list. + * If the same code appears twice in the list, an {@link java.lang.IllegalArgumentException} will be + * thrown. Any screenshots that are not mentioned in the list will be indeterminately ordered at the end
+ * of the list.</p> + */ + + public void reorderPkgScreenshots(final List<String> codes) { + Preconditions.checkNotNull(codes); + + // first check that there are no duplicates. + if(ImmutableSet.copyOf(codes).size() != codes.size()) {+ throw new IllegalArgumentException("the codes supplied contain duplicates which would interfere with the ordering");
+ } ++ List<PkgScreenshot> screenshots = Lists.newArrayList(getPkgScreenshots());
+ Collections.sort( + screenshots, + new Comparator<PkgScreenshot>() { + @Override+ public int compare(PkgScreenshot o1, PkgScreenshot o2) {
+ int o1i = codes.indexOf(o1.getCode()); + int o2i = codes.indexOf(o2.getCode()); + + if(-1==o1i && -1==o2i) { + return o1.getCode().compareTo(o2.getCode()); + } + + if(-1==o1i) { + o1i = Integer.MAX_VALUE; + } + + if(-1==o2i) { + o2i = Integer.MAX_VALUE; + } + + return Integer.compare(o1i,o2i); + } + } + ); + + for(int i=0;i<screenshots.size();i++) { + PkgScreenshot pkgScreenshot = screenshots.get(i); + pkgScreenshot.setOrdering(i+1); + } + } } =======================================--- /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/PkgScreenshot.java Wed Dec 11 08:25:33 2013 UTC +++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/PkgScreenshot.java Wed Feb 12 10:07:08 2014 UTC
@@ -1,5 +1,5 @@ /* - * Copyright 2013, Andrew Lindesay + * Copyright 2013-2014, Andrew Lindesay * Distributed under the terms of the MIT License. */ @@ -16,7 +16,7 @@ import java.util.List; -public class PkgScreenshot extends _PkgScreenshot {+public class PkgScreenshot extends _PkgScreenshot implements Comparable<PkgScreenshot> {
public static Optional<PkgScreenshot> getByCode(ObjectContext context, String code) {
Preconditions.checkNotNull(context); @@ -28,4 +28,24 @@ null)); } + /**+ * <p>As there should be only one of these, if there are two then this method will throw an
+ * {@link IllegalStateException}.</p> + */ + + public Optional<PkgScreenshotImage> getPkgScreenshotImage() { + List<PkgScreenshotImage> images = getPkgScreenshotImages(); + + switch(images.size()) { + case 0: return Optional.absent(); + case 1: return Optional.of(images.get(0)); + default:+ throw new IllegalStateException("more than one pkg icon image found on an icon image");
+ } + } + + @Override + public int compareTo(PkgScreenshot o) { + return getOrdering().compareTo(o.getOrdering()); + } } =======================================--- /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/pkg/PkgService.java Sun Feb 2 09:15:35 2014 UTC +++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/pkg/PkgService.java Wed Feb 12 10:07:08 2014 UTC
@@ -20,23 +20,24 @@ import org.haikuos.haikudepotserver.dataobjects.*; import org.haikuos.haikudepotserver.dataobjects.Pkg; import org.haikuos.haikudepotserver.dataobjects.PkgUrlType; -import org.haikuos.haikudepotserver.dataobjects.PkgVersion; import org.haikuos.haikudepotserver.pkg.model.BadPkgIconException; +import org.haikuos.haikudepotserver.pkg.model.BadPkgScreenshotException; import org.haikuos.haikudepotserver.pkg.model.PkgSearchSpecification; import org.haikuos.haikudepotserver.support.Closeables; import org.haikuos.haikudepotserver.support.ImageHelper; import org.haikuos.haikudepotserver.support.cayenne.LikeHelper; -import org.haikuos.pkg.model.*; +import org.imgscalr.Scalr; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.io.*; import java.util.Collections; import java.util.Comparator; import java.util.List; +import java.util.UUID; /** * <p>This service undertakes non-trivial operations on packages.</p> @@ -47,6 +48,8 @@protected static Logger logger = LoggerFactory.getLogger(PkgService.class);
+ protected static int SCREENSHOT_SIDE_LIMIT = 1500; + private ImageHelper imageHelper = new ImageHelper(); // ------------------------------ @@ -278,6 +281,106 @@logger.info("the icon {}px for package {} has been updated", size.width, pkg.getName());
} + + // ------------------------------ + // SCREENSHOT + + /**+ * <p>This method will write the package's screenshot to the output stream. It will constrain the output to the
+ * size given by scaling the image. The output is a PNG image.</p> + */ + + public void writePkgScreenshotImage( + OutputStream output, + ObjectContext context, + PkgScreenshot screenshot, + int targetWidth, + int targetHeight) throws IOException { + + Preconditions.checkNotNull(output); + Preconditions.checkNotNull(context); + Preconditions.checkNotNull(screenshot); + Preconditions.checkState(targetHeight > 0); + Preconditions.checkState(targetWidth > 0); ++ Optional<PkgScreenshotImage> pkgScreenshotImageOptional = screenshot.getPkgScreenshotImage();
+ + if(!pkgScreenshotImageOptional.isPresent()) {+ throw new IllegalStateException("the screenshot "+screenshot.getCode()+" is missing a screenshot image");
+ } ++ if(!pkgScreenshotImageOptional.get().getMediaType().getCode().equals(com.google.common.net.MediaType.PNG.toString())) { + throw new IllegalStateException("the screenshot system only supports png images at the present time");
+ } + + byte[] data = pkgScreenshotImageOptional.get().getData(); + ImageHelper.Size size = imageHelper.derivePngSize(data); + + // check to see if the screenshot needs to be resized to fit. + if(size.width > targetWidth || size.height > targetHeight) {+ ByteArrayInputStream imageInputStream = new ByteArrayInputStream(data);
+ BufferedImage bufferedImage = ImageIO.read(imageInputStream);+ BufferedImage scaledBufferedImage = Scalr.resize(bufferedImage, targetWidth, targetHeight);
+ ImageIO.write(scaledBufferedImage, "PNG", output); + } + else { + output.write(data); + } + } + + /**+ * <p>This method will write the PNG data supplied in the input to the package as a screenshot. Note that the icon + * must comply with necessary characteristics. If it is not compliant then an images of + * {@link org.haikuos.haikudepotserver.pkg.model.BadPkgScreenshotException} will be thrown.</p>
+ */ + + public PkgScreenshot storePkgScreenshotImage( + InputStream input, + ObjectContext context, + Pkg pkg) throws IOException, BadPkgScreenshotException { + + Preconditions.checkNotNull(input); + Preconditions.checkNotNull(context); + Preconditions.checkNotNull(pkg); + + byte[] pngData = ByteStreams.toByteArray(input); + ImageHelper.Size size = imageHelper.derivePngSize(pngData); ++ // check that the file roughly looks like PNG and the size is something
+ // reasonable. ++ if(size.height > SCREENSHOT_SIDE_LIMIT || size.width > SCREENSHOT_SIDE_LIMIT) { + logger.warn("attempt to store a screenshot image that is too large; "+size.toString());
+ } ++ MediaType png = MediaType.getByCode(context, com.google.common.net.MediaType.PNG.toString()).get();
++ // now we need to know the largest ordering so we can add this one at the end of the orderings
+ // such that it is the next one in the list. + + int ordering = 1;+ Optional<Integer> highestExistingScreenshotOrdering = pkg.getHighestPkgScreenshotOrdering();
+ + if(highestExistingScreenshotOrdering.isPresent()) {+ ordering = highestExistingScreenshotOrdering.get().intValue() + 1;
+ } + + PkgScreenshot screenshot = context.newObject(PkgScreenshot.class); + screenshot.setCode(UUID.randomUUID().toString()); + screenshot.setOrdering(new Integer(ordering));+ pkg.addToManyTarget(Pkg.PKG_SCREENSHOTS_PROPERTY, screenshot, true);
++ PkgScreenshotImage screenshotImage = context.newObject(PkgScreenshotImage.class);
+ screenshotImage.setMediaType(png); + screenshotImage.setData(pngData);+ screenshot.addToManyTarget(PkgScreenshot.PKG_SCREENSHOT_IMAGES_PROPERTY, screenshotImage, true);
+ + pkg.setModifyTimestamp(new java.util.Date()); ++ logger.info("a screenshot #{} has been added to package {} ({})", ordering, pkg.getName(), screenshot.getCode());
+ + return screenshot; + } // ------------------------------ // IMPORT =======================================--- /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/pkg/controller/PkgIconController.java Sat Feb 8 09:19:06 2014 UTC +++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/pkg/controller/PkgIconController.java Wed Feb 12 10:07:08 2014 UTC
@@ -149,7 +149,7 @@ } /**- * <p>This handler will take-up an HTTP PUT that provides an con image for the package.</p> + * <p>This handler will take-up an HTTP PUT that provides an icon image for the package.</p>
*/@RequestMapping(value = "/{"+KEY_PKGNAME+"}.{"+KEY_FORMAT+"}", method = RequestMethod.PUT)
=======================================--- /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/security/AuthorizationService.java Sun Feb 9 10:00:28 2014 UTC +++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/security/AuthorizationService.java Wed Feb 12 10:07:08 2014 UTC
@@ -149,6 +149,7 @@return null!=authenticatedUser && authenticatedUser.getIsRoot();
case PKG_EDITICON: + case PKG_EDITSCREENSHOT:return null!=authenticatedUser && authenticatedUser.getIsRoot();
default: =======================================--- /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/security/model/Permission.java Sun Feb 9 10:00:28 2014 UTC +++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/security/model/Permission.java Wed Feb 12 10:07:08 2014 UTC
@@ -19,7 +19,8 @@ USER_CHANGEPASSWORD(TargetType.USER), USER_LIST(null), - PKG_EDITICON(TargetType.PKG); + PKG_EDITICON(TargetType.PKG), + PKG_EDITSCREENSHOT(TargetType.PKG); private TargetType requiredTargetType; =======================================--- /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/support/ImageHelper.java Wed Nov 27 09:58:54 2013 UTC +++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/support/ImageHelper.java Wed Feb 12 10:07:08 2014 UTC
@@ -79,6 +79,10 @@ public boolean areSides(int s) { return s==width && s==height; } + + public String toString() { + return String.format("{%d,%d}",width,height); + } } } =======================================--- /haikudepotserver-webapp/src/main/resources/HaikuDepot.map.xml Wed Dec 11 08:25:33 2013 UTC +++ /haikudepotserver-webapp/src/main/resources/HaikuDepot.map.xml Wed Feb 12 10:07:08 2014 UTC
@@ -71,7 +71,7 @@ </db-key-generator> </db-entity> <db-entity name="pkg_screenshot_image" schema="haikudepot"> - <db-attribute name="data" type="BLOB" isMandatory="true"/> + <db-attribute name="data" type="VARBINARY" isMandatory="true"/><db-attribute name="id" type="BIGINT" isPrimaryKey="true" isMandatory="true"/>
<db-attribute name="media_type_id" type="BIGINT" isMandatory="true"/> <db-attribute name="pkg_screenshot_id" type="BIGINT" isMandatory="true"/> =======================================--- /haikudepotserver-webapp/src/main/resources/spring/servlet-context.xml Sat Feb 1 07:54:02 2014 UTC +++ /haikudepotserver-webapp/src/main/resources/spring/servlet-context.xml Wed Feb 12 10:07:08 2014 UTC
@@ -20,6 +20,7 @@ <mvc:resources mapping="/js/**" location="/js/" /> <mvc:resources mapping="/css/**" location="/css/" /> <mvc:resources mapping="/img/**" location="/img/" /> + <mvc:resources mapping="favicon.ico" location="/img/favicon.ico" /> <beanclass="org.springframework.web.servlet.view.InternalResourceViewResolver">
=======================================--- /haikudepotserver-webapp/src/main/webapp/js/app/controller/more.html Sat Feb 8 09:19:06 2014 UTC +++ /haikudepotserver-webapp/src/main/webapp/js/app/controller/more.html Wed Feb 12 10:07:08 2014 UTC
@@ -14,6 +14,9 @@ <li> <a href="" ng-click="goHome()">Home</a> </li> + <li ng-show="canViewUser()">+ <a href="" ng-click="goViewUser()">View currently authenticated user</a>
+ </li> <li show-if-permission="'REPOSITORY_LIST'"> <a href="" ng-click="goListRepositories()">Repositories</a> </li> =======================================--- /haikudepotserver-webapp/src/main/webapp/js/app/controller/morecontroller.js Sun Feb 2 09:15:35 2014 UTC +++ /haikudepotserver-webapp/src/main/webapp/js/app/controller/morecontroller.js Wed Feb 12 10:07:08 2014 UTC
@@ -25,6 +25,14 @@ // ------------------- // USER + $scope.canViewUser = function() { + return !!userState.user(); + } + + $scope.goViewUser = function() {+ $location.path('/viewuser/'+userState.user().nickname).search({});
+ } + function refreshAuthorization() { function disallowAll() { =======================================--- /haikudepotserver-webapp/src/main/webapp/js/app/controller/runtimeinformation.html Fri Jan 31 10:26:48 2014 UTC +++ /haikudepotserver-webapp/src/main/webapp/js/app/controller/runtimeinformation.html Wed Feb 12 10:07:08 2014 UTC
@@ -17,8 +17,6 @@ <h1>Versions</h1> - <p>The application has been running for some time.</p> - <table class="table-general"> <thead> <th>Component</th> =======================================--- /haikudepotserver-webapp/src/main/webapp/js/app/controller/viewpkgcontroller.js Sat Feb 8 09:19:06 2014 UTC +++ /haikudepotserver-webapp/src/main/webapp/js/app/controller/viewpkgcontroller.js Wed Feb 12 10:07:08 2014 UTC
@@ -81,7 +81,7 @@ $scope.goRemoveIcon = function() { jsonRpc.call( constants.ENDPOINT_API_V1_PKG, - "removeIcon", + "removePkgIcon", [{ name: $routeParams.name }] ).then( function(result) { =======================================--- /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/api1/PkgApiIT.java Sat Feb 8 09:19:06 2014 UTC +++ /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/api1/PkgApiIT.java Wed Feb 12 10:07:08 2014 UTC
@@ -6,6 +6,9 @@ package org.haikuos.haikudepotsever.api1; import com.google.common.base.Optional; +import com.google.common.base.Predicate; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; import junit.framework.Assert; import org.apache.cayenne.ObjectContext; import org.fest.assertions.Assertions; @@ -13,6 +16,7 @@ import org.haikuos.haikudepotserver.api1.model.pkg.*; import org.haikuos.haikudepotserver.api1.support.ObjectNotFoundException; import org.haikuos.haikudepotserver.dataobjects.Pkg; +import org.haikuos.haikudepotserver.dataobjects.PkgScreenshot; import org.haikuos.haikudepotserver.pkg.PkgService; import org.haikuos.haikudepotserver.support.Closeables; import org.haikuos.haikudepotsever.api1.support.AbstractIntegrationTest; @@ -21,6 +25,7 @@ import javax.annotation.Resource; import java.io.InputStream; +import java.util.List; public class PkgApiIT extends AbstractIntegrationTest { @@ -141,7 +146,7 @@ } // ------------------------------------ - pkgApi.removeIcon(new RemoveIconRequest("pkg1")); + pkgApi.removePkgIcon(new RemovePkgIconRequest("pkg1")); // ------------------------------------ { @@ -151,5 +156,96 @@ } } + /**+ * <p>This test depends on the sample package pkg1 having some screenshots associated with it.</p>
+ */ + + @Test + public void testGetPkgScreenshots() throws ObjectNotFoundException {+ IntegrationTestSupportService.StandardTestData data = integrationTestSupportService.createStandardTestData();
+ + // ------------------------------------+ GetPkgScreenshotsResult result = pkgApi.getPkgScreenshots(new GetPkgScreenshotsRequest(data.pkg1.getName()));
+ // ------------------------------------ ++ Assertions.assertThat(result.items.size()).isEqualTo(data.pkg1.getPkgScreenshots().size()); + List<PkgScreenshot> sortedScreenshots = data.pkg1.getSortedPkgScreenshots();
+ + for(int i=0;i<sortedScreenshots.size();i++) { + PkgScreenshot pkgScreenshot = sortedScreenshots.get(i);+ GetPkgScreenshotsResult.PkgScreenshot apiPkgScreenshot = result.items.get(i); + Assertions.assertThat(pkgScreenshot.getCode()).isEqualTo(apiPkgScreenshot.code);
+ } + } + + /**+ * <p>This test depends on the sample package pkg1 having some screenshots associated with it.</p>
+ */ + + @Test + public void testRemovePkgScreenshot() throws Exception { + setAuthenticatedUserToRoot(); ++ IntegrationTestSupportService.StandardTestData data = integrationTestSupportService.createStandardTestData(); + List<PkgScreenshot> sortedScreenshotsBefore = data.pkg1.getSortedPkgScreenshots();
+ + if(sortedScreenshotsBefore.size() < 2) {+ throw new IllegalStateException("the test cannot run without more than two screenshots");
+ } + + final String code1 = sortedScreenshotsBefore.get(1).getCode(); + + // ------------------------------------ + pkgApi.removePkgScreenshot(new RemovePkgScreenshotRequest(code1)); + // ------------------------------------ + + ObjectContext context = serverRuntime.getContext();+ Optional<Pkg> pkgOptional = Pkg.getByName(context, data.pkg1.getName()); + List<PkgScreenshot> sortedScreenshotsAfter = data.pkg1.getSortedPkgScreenshots();
++ Assertions.assertThat(sortedScreenshotsAfter.size()).isEqualTo(sortedScreenshotsBefore.size()-1);
++ Assertions.assertThat(Iterables.tryFind(sortedScreenshotsAfter, new Predicate<PkgScreenshot>() {
+ @Override + public boolean apply(PkgScreenshot pkgScreenshot) { + return pkgScreenshot.getCode().equals(code1); + } + }).isPresent()).isFalse(); + } + + /**+ * <p>This test assumes that the test data has a pkg1 with three screenshots associated with it.</p>
+ */ + + @Test + public void testReorderPkgScreenshots() throws Exception { + setAuthenticatedUserToRoot(); ++ IntegrationTestSupportService.StandardTestData data = integrationTestSupportService.createStandardTestData(); + List<PkgScreenshot> sortedScreenshotsBefore = data.pkg1.getSortedPkgScreenshots();
+ + if(3 != sortedScreenshotsBefore.size()) {+ throw new IllegalStateException("the test requires that pkg1 has three screenshots associated with it");
+ } + + // ------------------------------------ + pkgApi.reorderPkgScreenshots(new ReorderPkgScreenshotsRequest( + data.pkg1.getName(), + ImmutableList.of( + sortedScreenshotsBefore.get(2).getCode(), + sortedScreenshotsBefore.get(0).getCode() + ) + )); + // ------------------------------------ + + ObjectContext context = serverRuntime.getContext();+ Optional<Pkg> pkgOptional = Pkg.getByName(context, data.pkg1.getName()); + List<PkgScreenshot> sortedScreenshotsAfter = data.pkg1.getSortedPkgScreenshots();
+ + Assertions.assertThat(sortedScreenshotsAfter.size()).isEqualTo(3);+ Assertions.assertThat(sortedScreenshotsAfter.get(0).getCode()).isEqualTo(sortedScreenshotsBefore.get(2).getCode()); + Assertions.assertThat(sortedScreenshotsAfter.get(1).getCode()).isEqualTo(sortedScreenshotsBefore.get(0).getCode()); + Assertions.assertThat(sortedScreenshotsAfter.get(2).getCode()).isEqualTo(sortedScreenshotsBefore.get(1).getCode());
+ } } =======================================--- /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/api1/support/IntegrationTestSupportService.java Sun Feb 2 09:38:27 2014 UTC +++ /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/api1/support/IntegrationTestSupportService.java Wed Feb 12 10:07:08 2014 UTC
@@ -8,9 +8,12 @@ import org.apache.cayenne.ObjectContext; import org.apache.cayenne.configuration.server.ServerRuntime; import org.haikuos.haikudepotserver.dataobjects.*; +import org.haikuos.haikudepotserver.pkg.PkgService; +import org.haikuos.haikudepotserver.support.Closeables; import org.springframework.stereotype.Service; import javax.annotation.Resource; +import java.io.InputStream; /*** <p>This class is designed to help out with creating some common test data that can be re-used between tests.</p>
@@ -22,6 +25,9 @@ @Resource ServerRuntime serverRuntime; + @Resource + PkgService pkgService; + private ObjectContext objectContext = null; public ObjectContext getObjectContext() { @@ -31,6 +37,21 @@ return objectContext; } ++ private PkgScreenshot addPkgScreenshot(ObjectContext objectContext, Pkg pkg) {
+ InputStream inputStream = null; + + try {+ inputStream = IntegrationTestSupportService.class.getResourceAsStream("/sample-320x240.png"); + return pkgService.storePkgScreenshotImage(inputStream, objectContext, pkg);
+ } + catch(Exception e) {+ throw new IllegalStateException("an issue has arisen loading a sample screenshot into a test package",e);
+ } + finally { + Closeables.closeQuietly(inputStream); + } + } public StandardTestData createStandardTestData() { @@ -49,6 +70,10 @@ result.pkg1.setActive(true); result.pkg1.setName("pkg1"); + addPkgScreenshot(context,result.pkg1); + addPkgScreenshot(context,result.pkg1); + addPkgScreenshot(context,result.pkg1); + result.pkg1Version1 = context.newObject(PkgVersion.class); result.pkg1Version1.setActive(Boolean.FALSE); result.pkg1Version1.setArchitecture(x86); ============================================================================== Revision: b52dfe053b09 Author: Andrew Lindesay <apl@xxxxxxxxxxxxxx> Date: Wed Feb 12 10:37:01 2014 UTC Log: + integration test for the pkg screenshot controller http://code.google.com/p/haiku-depot-web-app/source/detail?r=b52dfe053b09 Added:/haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/pkg/controller/PkgScreenshotControllerIT.java
======================================= --- /dev/null+++ /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/pkg/controller/PkgScreenshotControllerIT.java Wed Feb 12 10:37:01 2014 UTC
@@ -0,0 +1,127 @@ +/* + * Copyright 2014, Andrew Lindesay + * Distributed under the terms of the MIT License. + */ + +package org.haikuos.haikudepotsever.pkg.controller; + +import com.google.common.base.Optional; +import com.google.common.io.ByteStreams; +import com.google.common.net.MediaType; +import org.apache.cayenne.ObjectContext; +import org.fest.assertions.Assertions; +import org.haikuos.haikudepotserver.dataobjects.PkgScreenshot; +import org.haikuos.haikudepotserver.pkg.controller.PkgScreenshotController; +import org.haikuos.haikudepotserver.support.Closeables; +import org.haikuos.haikudepotserver.support.ImageHelper; +import org.haikuos.haikudepotsever.api1.support.AbstractIntegrationTest;+import org.haikuos.haikudepotsever.api1.support.IntegrationTestSupportService;
+import org.junit.Test; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; + +import javax.annotation.Resource; +import java.io.IOException; +import java.io.InputStream; + +public class PkgScreenshotControllerIT extends AbstractIntegrationTest { + + @Resource + PkgScreenshotController pkgScreenshotController; + + @Resource + IntegrationTestSupportService integrationTestSupportService; + + /**+ * <p>This will return an image that can be used as a sample screenshot.</p>
+ */ + + private byte[] getScreenshotData() throws IOException { + InputStream inputStream = null; + + try {+ inputStream = this.getClass().getResourceAsStream("/sample-320x240.png");
+ return ByteStreams.toByteArray(inputStream); + } + finally { + Closeables.closeQuietly(inputStream); + } + } + + @Test + public void testPut() throws Exception { + + setAuthenticatedUserToRoot(); ++ IntegrationTestSupportService.StandardTestData data = integrationTestSupportService.createStandardTestData();
+ byte[] imageData = getScreenshotData(); + + MockHttpServletRequest request = new MockHttpServletRequest(); + MockHttpServletResponse response = new MockHttpServletResponse(); + request.setContent(imageData); + + // ------------------------------------ + pkgScreenshotController.put(request, response, "png", "pkg1"); + // ----------------------------------- + + // the header should contain the image code+ String code = response.getHeader(PkgScreenshotController.HEADER_SCREENSHOTCODE);
+ Assertions.assertThat(code).isNotEmpty(); + + ObjectContext context = serverRuntime.getContext();+ Optional<PkgScreenshot> screenshotOptional = PkgScreenshot.getByCode(context, code);
+ Assertions.assertThat(screenshotOptional.isPresent()).isTrue();+ Assertions.assertThat(screenshotOptional.get().getPkg().getName()).isEqualTo("pkg1"); + Assertions.assertThat(screenshotOptional.get().getPkgScreenshotImage().get().getData()).isEqualTo(imageData);
+ + } + + @Test + public void testGet_noScaling() throws Exception { ++ IntegrationTestSupportService.StandardTestData data = integrationTestSupportService.createStandardTestData();
+ byte[] imageData = getScreenshotData(); + + MockHttpServletRequest request = new MockHttpServletRequest(); + MockHttpServletResponse response = new MockHttpServletResponse(); + + // ------------------------------------ + pkgScreenshotController.fetchGet( + request, response, + 640, 480, + "png", + data.pkg1.getSortedPkgScreenshots().get(0).getCode()); + // ----------------------------------- ++ Assertions.assertThat(response.getContentType()).isEqualTo(MediaType.PNG.toString()); + Assertions.assertThat(response.getContentAsByteArray()).isEqualTo(imageData);
+ + } + + @Test + public void testGet_scaling() throws Exception { ++ IntegrationTestSupportService.StandardTestData data = integrationTestSupportService.createStandardTestData();
+ byte[] imageData = getScreenshotData(); + + MockHttpServletRequest request = new MockHttpServletRequest(); + MockHttpServletResponse response = new MockHttpServletResponse(); + + // ------------------------------------ + pkgScreenshotController.fetchGet( + request, response, + 160, 120, + "png", + data.pkg1.getSortedPkgScreenshots().get(0).getCode()); + // ----------------------------------- ++ Assertions.assertThat(response.getContentType()).isEqualTo(MediaType.PNG.toString());
+ + ImageHelper imageHelper = new ImageHelper();+ ImageHelper.Size size = imageHelper.derivePngSize(response.getContentAsByteArray());
+ Assertions.assertThat(size.width).isEqualTo(160); + Assertions.assertThat(size.height).isEqualTo(120); + + } + +} ============================================================================== Revision: 79eb8a4e32f8 Author: Andrew Lindesay <apl@xxxxxxxxxxxxxx> Date: Fri Feb 14 08:42:33 2014 UTC Log: + integration test for the pkg icon controller http://code.google.com/p/haiku-depot-web-app/source/detail?r=79eb8a4e32f8 Added:/haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/pkg/controller/PkgIconControllerIT.java
Modified:/haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/pkg/PkgService.java /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/api1/PkgApiIT.java /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/api1/support/AbstractIntegrationTest.java /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/api1/support/IntegrationTestSupportService.java /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/pkg/controller/PkgScreenshotControllerIT.java
======================================= --- /dev/null+++ /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/pkg/controller/PkgIconControllerIT.java Fri Feb 14 08:42:33 2014 UTC
@@ -0,0 +1,90 @@ +/* + * Copyright 2014, Andrew Lindesay + * Distributed under the terms of the MIT License. + */ + +package org.haikuos.haikudepotsever.pkg.controller; + +import com.google.common.base.Optional; +import com.google.common.net.MediaType; +import org.apache.cayenne.ObjectContext; +import org.fest.assertions.Assertions; +import org.haikuos.haikudepotserver.dataobjects.Pkg; +import org.haikuos.haikudepotserver.pkg.controller.PkgIconController; +import org.haikuos.haikudepotsever.api1.support.AbstractIntegrationTest;+import org.haikuos.haikudepotsever.api1.support.IntegrationTestSupportService;
+import org.junit.Test; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; + +import javax.annotation.Resource; +import java.io.IOException; + +public class PkgIconControllerIT extends AbstractIntegrationTest { + + @Resource + PkgIconController pkgIconController; + + @Resource + IntegrationTestSupportService integrationTestSupportService; + + private byte[] getIconData() throws IOException { + return getResourceData("/sample-32x32.png"); + } + + /**+ * <p>This test works knowing that the test package pkg1 will have a PNG image pre-loaded for it.</p>
+ */ + + @Test + public void testGet() throws Exception { ++ IntegrationTestSupportService.StandardTestData data = integrationTestSupportService.createStandardTestData();
+ byte[] imageData = getIconData(); + + MockHttpServletRequest request = new MockHttpServletRequest(); + MockHttpServletResponse response = new MockHttpServletResponse(); + + // ------------------------------------ + pkgIconController.fetchGet( + request, response, + 32, + "png", + "pkg1"); + // ----------------------------------- ++ Assertions.assertThat(response.getContentType()).isEqualTo(MediaType.PNG.toString()); + Assertions.assertThat(response.getContentAsByteArray()).isEqualTo(imageData);
+ + } + + @Test + public void testPut() throws Exception { + + setAuthenticatedUserToRoot(); ++ IntegrationTestSupportService.StandardTestData data = integrationTestSupportService.createStandardTestData();
+ byte[] imageData = getIconData(); + + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setContent(imageData); + MockHttpServletResponse response = new MockHttpServletResponse(); + + // ------------------------------------ + pkgIconController.put( + request,response, + 32, + "png", + "pkg2"); + // ------------------------------------ + + { + ObjectContext context = serverRuntime.getContext(); + Optional<Pkg> pkgOptional = Pkg.getByName(context,"pkg2");+ Optional<org.haikuos.haikudepotserver.dataobjects.MediaType> mediaTypeOptional = org.haikuos.haikudepotserver.dataobjects.MediaType.getByCode(context,MediaType.PNG.toString()); + Assertions.assertThat(pkgOptional.get().getPkgIcon(mediaTypeOptional.get(),32).get().getPkgIconImage().get().getData()).isEqualTo(imageData);
+ } + + } + +} =======================================--- /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/pkg/PkgService.java Wed Feb 12 10:07:08 2014 UTC +++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/pkg/PkgService.java Fri Feb 14 08:42:33 2014 UTC
@@ -223,8 +223,9 @@ if(pkgIconImageOptional.isPresent()) { output.write(pkgIconImageOptional.get().getData()); } - - writeGenericIconImage(output, size); + else { + writeGenericIconImage(output, size); + } } /** =======================================--- /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/api1/PkgApiIT.java Wed Feb 12 10:07:08 2014 UTC +++ /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/api1/PkgApiIT.java Fri Feb 14 08:42:33 2014 UTC
@@ -72,7 +72,7 @@ GetPkgResult result = pkgApi.getPkg(request); // ------------------------------------ - Assertions.assertThat(result.hasIcon).isFalse();+ Assertions.assertThat(result.hasIcon).isTrue(); // icons are preloaded in the test data
Assertions.assertThat(result.name).isEqualTo("pkg1"); Assertions.assertThat(result.versions.size()).isEqualTo(1);Assertions.assertThat(result.versions.get(0).architectureCode).isEqualTo("x86");
@@ -108,8 +108,7 @@ } /**- * <p>This test will first of all load an icon in for a package and will then use the API to remove it; checking
- * that the icon is there before and is not there afterwards.</p>+ * <p>This test knows that an icon exists for pkg1 and then removes it.</p>
*/ @Test @@ -118,31 +117,11 @@ setAuthenticatedUserToRoot();IntegrationTestSupportService.StandardTestData data = integrationTestSupportService.createStandardTestData();
- InputStream sample32InputStream = null; - - try {- sample32InputStream = getClass().getResourceAsStream("/sample-32x32.png");
- - if(null==sample32InputStream) {- throw new IllegalStateException("unable to find the test input stream for the icon image");
- } - - pkgService.storePkgIconImage( - sample32InputStream, - 32, - integrationTestSupportService.getObjectContext(), - data.pkg1); -- integrationTestSupportService.getObjectContext().commitChanges();
- } - finally { - Closeables.closeQuietly(sample32InputStream); - } { ObjectContext objectContext = serverRuntime.getContext();Optional<Pkg> pkgOptionalBefore = Pkg.getByName(objectContext, "pkg1"); - Assertions.assertThat(pkgOptionalBefore.get().getPkgIcons().size()).isEqualTo(1); + Assertions.assertThat(pkgOptionalBefore.get().getPkgIcons().size()).isEqualTo(2); // 16 and 32 px sizes
} // ------------------------------------ =======================================--- /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/api1/support/AbstractIntegrationTest.java Sat Jan 18 09:59:17 2014 UTC +++ /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/api1/support/AbstractIntegrationTest.java Fri Feb 14 08:42:33 2014 UTC
@@ -6,6 +6,7 @@ package org.haikuos.haikudepotsever.api1.support; import com.google.common.base.Optional; +import com.google.common.io.ByteStreams; import org.apache.cayenne.ObjectContext; import org.apache.cayenne.configuration.server.ServerRuntime; import org.haikuos.haikudepotserver.dataobjects.User; @@ -22,6 +23,8 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import javax.annotation.Resource; +import java.io.IOException; +import java.io.InputStream; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; @@ -57,6 +60,23 @@ @Resource AuthenticationService authenticationService; + protected byte[] getResourceData(String path) throws IOException { + InputStream inputStream = null; + + try { + inputStream = this.getClass().getResourceAsStream(path); + + if(null==inputStream) {+ throw new IllegalStateException("unable to find the test resource; "+path);
+ } + + return ByteStreams.toByteArray(inputStream); + } + finally { + Closeables.closeQuietly(inputStream); + } + } + /*** <p>Before each test is run, we want to remove all of the database objects and then re-populate
* them back again.</p> =======================================--- /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/api1/support/IntegrationTestSupportService.java Wed Feb 12 10:07:08 2014 UTC +++ /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/api1/support/IntegrationTestSupportService.java Fri Feb 14 08:42:33 2014 UTC
@@ -52,6 +52,26 @@ Closeables.closeQuietly(inputStream); } } ++ private void addPkgIcon(ObjectContext objectContext, Pkg pkg, int size) {
+ InputStream inputStream = null; + + try {+ inputStream = this.getClass().getResourceAsStream(String.format("/sample-%dx%d.png",size,size)); + pkgService.storePkgIconImage(inputStream, size, objectContext, pkg);
+ } + catch(Exception e) {+ throw new IllegalStateException("an issue has arisen loading an icon",e);
+ } + finally { + Closeables.closeQuietly(inputStream); + } + } + + private void addPkgIcons(ObjectContext objectContext, Pkg pkg) { + addPkgIcon(objectContext, pkg, 16); + addPkgIcon(objectContext, pkg, 32); + } public StandardTestData createStandardTestData() { @@ -73,6 +93,7 @@ addPkgScreenshot(context,result.pkg1); addPkgScreenshot(context,result.pkg1); addPkgScreenshot(context,result.pkg1); + addPkgIcons(context, result.pkg1); result.pkg1Version1 = context.newObject(PkgVersion.class); result.pkg1Version1.setActive(Boolean.FALSE); =======================================--- /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/pkg/controller/PkgScreenshotControllerIT.java Wed Feb 12 10:37:01 2014 UTC +++ /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/pkg/controller/PkgScreenshotControllerIT.java Fri Feb 14 08:42:33 2014 UTC
@@ -37,15 +37,7 @@ */ private byte[] getScreenshotData() throws IOException { - InputStream inputStream = null; - - try {- inputStream = this.getClass().getResourceAsStream("/sample-320x240.png");
- return ByteStreams.toByteArray(inputStream); - } - finally { - Closeables.closeQuietly(inputStream); - } + return getResourceData("/sample-320x240.png"); } @Test