diff --git a/back/pom.xml b/back/pom.xml index 4c48520..b1c3a32 100644 --- a/back/pom.xml +++ b/back/pom.xml @@ -26,7 +26,7 @@ kangaroo-and-rabbit archidata - 0.1.2 + 0.1.3 diff --git a/back/src/org/kar/karideo/WebLauncher.java b/back/src/org/kar/karideo/WebLauncher.java index 85b9a22..7bd983b 100755 --- a/back/src/org/kar/karideo/WebLauncher.java +++ b/back/src/org/kar/karideo/WebLauncher.java @@ -6,7 +6,6 @@ import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory; import org.glassfish.jersey.media.multipart.MultiPartFeature; import org.glassfish.jersey.server.ResourceConfig; import org.kar.karideo.api.*; -import org.kar.karideo.model.Data; import org.kar.karideo.model.Media; import org.kar.karideo.model.Season; import org.kar.karideo.model.Series; @@ -14,13 +13,14 @@ import org.kar.karideo.model.Type; import org.kar.archidata.GlobalConfiguration; import org.kar.archidata.SqlWrapper; import org.kar.archidata.UpdateJwtPublicKey; +import org.kar.archidata.api.DataResource; import org.kar.archidata.db.DBConfig; import org.kar.archidata.filter.AuthenticationFilter; import org.kar.archidata.filter.CORSFilter; import org.kar.archidata.filter.OptionFilter; +import org.kar.archidata.model.Data; import org.kar.archidata.model.User; import org.kar.archidata.util.ConfigBaseVariable; -import org.kar.archidata.util.JWTWrapper; import org.glassfish.jersey.jackson.JacksonFeature; import javax.ws.rs.core.UriBuilder; diff --git a/back/src/org/kar/karideo/annotation/PermitTokenInURI.java b/back/src/org/kar/karideo/annotation/PermitTokenInURI.java deleted file mode 100644 index 44c9537..0000000 --- a/back/src/org/kar/karideo/annotation/PermitTokenInURI.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.kar.karideo.annotation; - -import javax.ws.rs.NameBinding; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.ElementType.TYPE; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -@NameBinding -@Retention(RUNTIME) -@Target({METHOD}) -public @interface PermitTokenInURI { -} diff --git a/back/src/org/kar/karideo/api/DataResource.java b/back/src/org/kar/karideo/api/DataResource.java deleted file mode 100644 index af56056..0000000 --- a/back/src/org/kar/karideo/api/DataResource.java +++ /dev/null @@ -1,426 +0,0 @@ -package org.kar.karideo.api; - -import org.glassfish.jersey.media.multipart.FormDataContentDisposition; -import org.glassfish.jersey.media.multipart.FormDataParam; -import org.kar.karideo.annotation.PermitTokenInURI; -import org.kar.archidata.filter.GenericContext; -import org.kar.karideo.model.Data; -import org.kar.archidata.SqlWrapper; -import org.kar.archidata.annotation.security.RolesAllowed; -import org.kar.archidata.util.ConfigBaseVariable; - -import javax.imageio.ImageIO; -import javax.ws.rs.*; -import javax.ws.rs.core.CacheControl; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.HttpHeaders; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.SecurityContext; -import javax.ws.rs.core.StreamingOutput; -import java.awt.*; -import java.awt.image.BufferedImage; -import java.io.*; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.nio.file.StandardCopyOption; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.Date; - - -// https://stackoverflow.com/questions/35367113/jersey-webservice-scalable-approach-to-download-file-and-reply-to-client -// https://gist.github.com/aitoroses/4f7a2b197b732a6a691d - -@Path("/data") -@Produces({MediaType.APPLICATION_JSON}) -public class DataResource { - private final static int CHUNK_SIZE = 1024 * 1024; // 1MB chunks - private final static int CHUNK_SIZE_IN = 50 * 1024 * 1024; // 1MB chunks - /** - * Upload some datas - */ - private static long tmpFolderId = 1; - - private static void createFolder(String path) throws IOException { - if (!Files.exists(java.nio.file.Path.of(path))) { - //Log.print("Create folder: " + path); - Files.createDirectories(java.nio.file.Path.of(path)); - } - } - - public static long getTmpDataId() { - return tmpFolderId++; - } - - public static String getTmpFileInData(long tmpFolderId) { - String filePath = ConfigBaseVariable.getTmpDataFolder() + File.separator + tmpFolderId; - try { - createFolder(ConfigBaseVariable.getTmpDataFolder() + File.separator); - } catch (IOException e) { - e.printStackTrace(); - } - return filePath; - } - - public static String getFileData(long tmpFolderId) { - String filePath = ConfigBaseVariable.getMediaDataFolder() + File.separator + tmpFolderId + File.separator + "data"; - try { - createFolder(ConfigBaseVariable.getMediaDataFolder() + File.separator + tmpFolderId + File.separator); - } catch (IOException e) { - e.printStackTrace(); - } - return filePath; - } - - public static Data getWithSha512(String sha512) { - System.out.println("find sha512 = " + sha512); - try { - return SqlWrapper.getWhere(Data.class, "sha512", "=", sha512); - } catch (Exception e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - return null; - } - - public static Data getWithId(long id) { - System.out.println("find id = " + id); - try { - return SqlWrapper.get(Data.class, id); - } catch (Exception e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - return null; - } - - public static Data createNewData(long tmpUID, String originalFileName, String sha512) throws IOException { - // determine mime type: - Data injectedData = new Data(); - String mimeType = ""; - String extension = originalFileName.substring(originalFileName.lastIndexOf('.') + 1); - switch (extension.toLowerCase()) { - case "jpg": - case "jpeg": - mimeType = "image/jpeg"; - break; - case "png": - mimeType = "image/png"; - break; - case "webp": - mimeType = "image/webp"; - break; - case "mka": - mimeType = "audio/x-matroska"; - break; - case "mkv": - mimeType = "video/x-matroska"; - break; - case "webm": - mimeType = "video/webm"; - break; - default: - throw new IOException("Can not find the mime type of data input: '" + extension + "'"); - } - injectedData.mimeType = mimeType; - injectedData.sha512 = sha512; - String tmpPath = getTmpFileInData(tmpUID); - injectedData.size = Files.size(Paths.get(tmpPath)); - - try { - injectedData = SqlWrapper.insert(injectedData); - } catch (Exception e) { - // TODO Auto-generated catch block - e.printStackTrace(); - return null; - } - String mediaPath = getFileData(injectedData.id); - System.out.println("src = " + tmpPath); - System.out.println("dst = " + mediaPath); - Files.move(Paths.get(tmpPath), Paths.get(mediaPath), StandardCopyOption.ATOMIC_MOVE); - System.out.println("Move done"); - return injectedData; - } - - static String saveTemporaryFile(InputStream uploadedInputStream, long idData) { - return saveFile(uploadedInputStream, DataResource.getTmpFileInData(idData)); - } - - static void removeTemporaryFile(long idData) { - String filepath = DataResource.getTmpFileInData(idData); - if (Files.exists(Paths.get(filepath))) { - try { - Files.delete(Paths.get(filepath)); - } catch (IOException e) { - System.out.println("can not delete temporary file : " + Paths.get(filepath)); - e.printStackTrace(); - } - } - } - - // save uploaded file to a defined location on the server - static String saveFile(InputStream uploadedInputStream, String serverLocation) { - String out = ""; - try { - OutputStream outpuStream = new FileOutputStream(new File( - serverLocation)); - int read = 0; - byte[] bytes = new byte[CHUNK_SIZE_IN]; - MessageDigest md = MessageDigest.getInstance("SHA-512"); - - outpuStream = new FileOutputStream(new File(serverLocation)); - while ((read = uploadedInputStream.read(bytes)) != -1) { - //System.out.println("write " + read); - md.update(bytes, 0, read); - outpuStream.write(bytes, 0, read); - } - System.out.println("Flush input stream ... " + serverLocation); - System.out.flush(); - outpuStream.flush(); - outpuStream.close(); - // create the end of sha512 - byte[] sha512Digest = md.digest(); - // convert in hexadecimal - out = bytesToHex(sha512Digest); - uploadedInputStream.close(); - } catch (IOException ex) { - System.out.println("Can not write in temporary file ... "); - ex.printStackTrace(); - } catch (NoSuchAlgorithmException ex) { - System.out.println("Can not find sha512 algorithms"); - ex.printStackTrace(); - } - return out; - } - - public static String bytesToHex(byte[] bytes) { - StringBuilder sb = new StringBuilder(); - for (byte b : bytes) { - sb.append(String.format("%02x", b)); - } - return sb.toString(); - } - - - public Data getSmall(Long id) { - try { - return SqlWrapper.get(Data.class, id); - } catch (Exception e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - return null; - } - - @POST - @Path("/upload/") - @Consumes({MediaType.MULTIPART_FORM_DATA}) - @RolesAllowed("ADMIN") - public Response uploadFile(@Context SecurityContext sc, @FormDataParam("file") InputStream fileInputStream, @FormDataParam("file") FormDataContentDisposition fileMetaData) { - GenericContext gc = (GenericContext) sc.getUserPrincipal(); - System.out.println("==================================================="); - System.out.println("== DATA uploadFile " + (gc==null?"null":gc.user)); - System.out.println("==================================================="); - //public NodeSmall uploadFile(final FormDataMultiPart form) { - System.out.println("Upload file: "); - String filePath = ConfigBaseVariable.getTmpDataFolder() + File.separator + tmpFolderId++; - try { - createFolder(ConfigBaseVariable.getTmpDataFolder() + File.separator); - } catch (IOException e) { - e.printStackTrace(); - } - saveFile(fileInputStream, filePath); - return Response.ok("Data uploaded successfully !!").build(); - //return null; - } - - @GET - @Path("{id}") - @PermitTokenInURI - @RolesAllowed("USER") - @Produces(MediaType.APPLICATION_OCTET_STREAM) - public Response retriveDataId(@Context SecurityContext sc, @QueryParam(HttpHeaders.AUTHORIZATION) String token, @HeaderParam("Range") String range, @PathParam("id") Long id) throws Exception { - GenericContext gc = (GenericContext) sc.getUserPrincipal(); - //System.out.println("==================================================="); - System.out.println("== DATA retriveDataId ? id=" + id + " user=" + (gc==null?"null":gc.user)); - //System.out.println("==================================================="); - Data value = getSmall(id); - if (value == null) { - Response.status(404). - entity("media NOT FOUND: " + id). - type("text/plain"). - build(); - } - return buildStream(ConfigBaseVariable.getMediaDataFolder() + File.separator + id + File.separator + "data", range, value.mimeType); - } - - @GET - @Path("thumbnail/{id}") - @RolesAllowed("USER") - @PermitTokenInURI - @Produces(MediaType.APPLICATION_OCTET_STREAM) - //@CacheMaxAge(time = 10, unit = TimeUnit.DAYS) - public Response retriveDataThumbnailId(@Context SecurityContext sc, @QueryParam(HttpHeaders.AUTHORIZATION) String token, @HeaderParam("Range") String range, @PathParam("id") Long id) throws Exception { - GenericContext gc = (GenericContext) sc.getUserPrincipal(); - //System.out.println("==================================================="); - //System.out.println("== DATA retriveDataThumbnailId ? " + (gc==null?"null":gc.user)); - //System.out.println("==================================================="); - Data value = getSmall(id); - if (value == null) { - return Response.status(404). - entity("media NOT FOUND: " + id). - type("text/plain"). - build(); - } - String filePathName = ConfigBaseVariable.getMediaDataFolder() + File.separator + id + File.separator + "data"; - if ( value.mimeType.contentEquals("image/jpeg") - || value.mimeType.contentEquals("image/png") - // || value.mimeType.contentEquals("image/webp") - ) { - // reads input image - //System.out.println("Read path: " + filePathName); - File inputFile = new File(filePathName); - if (!inputFile.exists()) { - return Response.status(500). - entity("Internal Error: Media is NOT FOUNDABLE: " + id). - type("text/plain"). - build(); - } - BufferedImage inputImage = ImageIO.read(inputFile); - int scaledWidth = 250; - int scaledHeight = (int)((float)inputImage.getHeight() / (float)inputImage.getWidth() * (float) scaledWidth); - // creates output image - BufferedImage outputImage = new BufferedImage(scaledWidth, - scaledHeight, inputImage.getType()); - - // scales the input image to the output image - Graphics2D g2d = outputImage.createGraphics(); - g2d.drawImage(inputImage, 0, 0, scaledWidth, scaledHeight, null); - g2d.dispose(); - // create the output stream: - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - try { - // TODO: check how to remove buffer file !!! here, it is not needed at all... - ImageIO.write( outputImage, "JPG", baos); - } catch (IOException e) { - e.printStackTrace(); - return Response.status(500). - entity("Internal Error: resize fail: " + e.getMessage()). - type("text/plain"). - build(); - } - byte[] imageData = baos.toByteArray(); - //Response.ok(new ByteArrayInputStream(imageData)).build(); - Response.ResponseBuilder out = Response.ok(imageData) - .header(HttpHeaders.CONTENT_LENGTH, imageData.length); - out.type("image/jpeg"); - // TODO: move this in a decorator !!! - CacheControl cc = new CacheControl(); - cc.setMaxAge(3600); - cc.setNoCache(false); - out.cacheControl(cc); - return out.build(); - } - return buildStream(filePathName, range, value.mimeType); - } - //@Secured - @GET - @Path("{id}/{name}") - @PermitTokenInURI - @RolesAllowed("USER") - @Produces(MediaType.APPLICATION_OCTET_STREAM) - public Response retriveDataFull(@Context SecurityContext sc, @QueryParam(HttpHeaders.AUTHORIZATION) String token, @HeaderParam("Range") String range, @PathParam("id") Long id, @PathParam("name") String name) throws Exception { - GenericContext gc = (GenericContext) sc.getUserPrincipal(); - //System.out.println("==================================================="); - System.out.println("== DATA retriveDataFull ? id=" + id + " user=" + (gc==null?"null":gc.user)); - //System.out.println("==================================================="); - Data value = getSmall(id); - if (value == null) { - Response.status(404). - entity("media NOT FOUND: " + id). - type("text/plain"). - build(); - } - return buildStream(ConfigBaseVariable.getMediaDataFolder() + File.separator + id + File.separator + "data", range, value.mimeType); - } - - /** - * Adapted from http://stackoverflow.com/questions/12768812/video-streaming-to-ipad-does-not-work-with-tapestry5/12829541#12829541 - * - * @param range range header - * @return Streaming output - * @throws Exception IOException if an error occurs in streaming. - */ - private Response buildStream(final String filename, final String range, String mimeType) throws Exception { - File file = new File(filename); - //System.out.println("request range : " + range); - // range not requested : Firefox does not send range headers - if (range == null) { - final StreamingOutput output = new StreamingOutput() { - @Override - public void write(OutputStream out) { - try (FileInputStream in = new FileInputStream(file)) { - byte[] buf = new byte[1024 * 1024]; - int len; - while ((len = in.read(buf)) != -1) { - try { - out.write(buf, 0, len); - out.flush(); - //System.out.println("---- wrote " + len + " bytes file ----"); - } catch (IOException ex) { - System.out.println("remote close connection"); - break; - } - } - } catch (IOException ex) { - throw new InternalServerErrorException(ex); - } - } - }; - Response.ResponseBuilder out = Response.ok(output) - .header(HttpHeaders.CONTENT_LENGTH, file.length()); - if (mimeType != null) { - out.type(mimeType); - } - return out.build(); - - } - - String[] ranges = range.split("=")[1].split("-"); - final long from = Long.parseLong(ranges[0]); - - //System.out.println("request range : " + ranges.length); - //Chunk media if the range upper bound is unspecified. Chrome, Opera sends "bytes=0-" - long to = CHUNK_SIZE + from; - if (ranges.length == 1) { - to = file.length() - 1; - } else { - if (to >= file.length()) { - to = (long) (file.length() - 1); - } - } - final String responseRange = String.format("bytes %d-%d/%d", from, to, file.length()); - //System.out.println("responseRange : " + responseRange); - final RandomAccessFile raf = new RandomAccessFile(file, "r"); - raf.seek(from); - - final long len = to - from + 1; - final MediaStreamer streamer = new MediaStreamer(len, raf); - Response.ResponseBuilder out = Response.ok(streamer) - .status(Response.Status.PARTIAL_CONTENT) - .header("Accept-Ranges", "bytes") - .header("Content-Range", responseRange) - .header(HttpHeaders.CONTENT_LENGTH, streamer.getLenth()) - .header(HttpHeaders.LAST_MODIFIED, new Date(file.lastModified())); - if (mimeType != null) { - out.type(mimeType); - } - return out.build(); - } - - public static void undelete(Long id) throws Exception { - SqlWrapper.unsetDelete(Data.class, id); - } - -} diff --git a/back/src/org/kar/karideo/api/MediaStreamer.java b/back/src/org/kar/karideo/api/MediaStreamer.java deleted file mode 100644 index 6fb3ce8..0000000 --- a/back/src/org/kar/karideo/api/MediaStreamer.java +++ /dev/null @@ -1,56 +0,0 @@ -package org.kar.karideo.api; - -import javax.ws.rs.InternalServerErrorException; -import javax.ws.rs.WebApplicationException; -import javax.ws.rs.core.StreamingOutput; -import java.io.IOException; -import java.io.OutputStream; -import java.io.RandomAccessFile; - -public class MediaStreamer implements StreamingOutput { - private final int CHUNK_SIZE = 1024 * 1024; // 1MB chunks - final byte[] buf = new byte[CHUNK_SIZE]; - private long length; - private RandomAccessFile raf; - - public MediaStreamer(long length, RandomAccessFile raf) throws IOException { - //System.out.println("request stream of " + length / 1024 + " data"); - if (length<0) { - throw new IOException("Wrong size of the file to stream: " + length); - } - this.length = length; - this.raf = raf; - } - - @Override - public void write(OutputStream outputStream) { - try { - while (length != 0) { - int read = raf.read(buf, 0, buf.length > length ? (int) length : buf.length); - try { - outputStream.write(buf, 0, read); - } catch (IOException ex) { - System.out.println("remote close connection"); - break; - } - length -= read; - } - } catch (IOException ex) { - throw new InternalServerErrorException(ex); - } catch (WebApplicationException ex) { - throw new InternalServerErrorException(ex); - } finally { - try { - raf.close(); - } catch (IOException ex) { - ex.printStackTrace(); - throw new InternalServerErrorException(ex); - } - } - } - - public long getLenth() { - return length; - } - -} diff --git a/back/src/org/kar/karideo/api/SeasonResource.java b/back/src/org/kar/karideo/api/SeasonResource.java index 88498f6..fda1981 100644 --- a/back/src/org/kar/karideo/api/SeasonResource.java +++ b/back/src/org/kar/karideo/api/SeasonResource.java @@ -94,8 +94,8 @@ public class SeasonResource { out.name = name; out.parentId = seriesId; out = SqlWrapper.insert(out); - return out; } + return out; } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); diff --git a/back/src/org/kar/karideo/api/SeriesResource.java b/back/src/org/kar/karideo/api/SeriesResource.java index fe1f04f..bf25895 100644 --- a/back/src/org/kar/karideo/api/SeriesResource.java +++ b/back/src/org/kar/karideo/api/SeriesResource.java @@ -95,8 +95,8 @@ public class SeriesResource { out.name = name; out.parentId = typeId; out = SqlWrapper.insert(out); - return out; } + return out; } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); diff --git a/back/src/org/kar/karideo/api/TypeResource.java b/back/src/org/kar/karideo/api/TypeResource.java index fece840..4e020de 100644 --- a/back/src/org/kar/karideo/api/TypeResource.java +++ b/back/src/org/kar/karideo/api/TypeResource.java @@ -98,8 +98,8 @@ public class TypeResource { out = new Type(); out.name = name; out = SqlWrapper.insert(out); - return out; } + return out; } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); diff --git a/back/src/org/kar/karideo/api/VideoResource.java b/back/src/org/kar/karideo/api/VideoResource.java index f97fcce..eca2110 100644 --- a/back/src/org/kar/karideo/api/VideoResource.java +++ b/back/src/org/kar/karideo/api/VideoResource.java @@ -2,16 +2,16 @@ package org.kar.karideo.api; import org.glassfish.jersey.media.multipart.FormDataContentDisposition; import org.glassfish.jersey.media.multipart.FormDataParam; -import org.kar.karideo.WebLauncher; -import org.kar.archidata.db.DBEntry; +import org.kar.archidata.model.Data; import org.kar.archidata.util.DataTools; -import org.kar.karideo.model.Data; import org.kar.karideo.model.Media; import org.kar.karideo.model.Season; import org.kar.karideo.model.Series; import org.kar.karideo.model.Type; import org.kar.archidata.SqlWrapper; import org.kar.archidata.annotation.security.RolesAllowed; +import org.kar.archidata.api.DataResource; + import javax.ws.rs.*; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; @@ -43,6 +43,7 @@ public class VideoResource { @RolesAllowed("ADMIN") @Consumes(MediaType.APPLICATION_JSON) public Media put(@PathParam("id") Long id, String jsonRequest) throws Exception { + System.out.println("update video " + id + " ==> '" + jsonRequest + "'"); SqlWrapper.update(Media.class, id, jsonRequest); return SqlWrapper.get(Media.class, id); } diff --git a/back/src/org/kar/karideo/model/Data.java b/back/src/org/kar/karideo/model/Data.java deleted file mode 100644 index 7302e1a..0000000 --- a/back/src/org/kar/karideo/model/Data.java +++ /dev/null @@ -1,31 +0,0 @@ -package org.kar.karideo.model; - -import java.sql.ResultSet; -import java.sql.SQLException; - -import org.kar.archidata.annotation.SQLComment; -import org.kar.archidata.annotation.SQLIfNotExists; -import org.kar.archidata.annotation.SQLLimitSize; -import org.kar.archidata.annotation.SQLNotNull; -import org.kar.archidata.annotation.SQLTableName; -import org.kar.archidata.model.GenericTable; - -import com.fasterxml.jackson.databind.annotation.JsonSerialize; - -@SQLTableName ("data") -@SQLIfNotExists -@JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL) -public class Data extends GenericTable { - - @SQLNotNull - @SQLLimitSize(128) - @SQLComment("Sha512 of the data") - public String sha512; - @SQLNotNull - @SQLLimitSize(128) - @SQLComment("Mime -type of the media") - public String mimeType; - @SQLNotNull - @SQLComment("Size in Byte of the data") - public Long size; -} diff --git a/back/src/org/kar/karideo/model/DataGetToken.java b/back/src/org/kar/karideo/model/DataGetToken.java deleted file mode 100644 index edb0fa8..0000000 --- a/back/src/org/kar/karideo/model/DataGetToken.java +++ /dev/null @@ -1,8 +0,0 @@ -package org.kar.karideo.model; - -public class DataGetToken { - public String login; - public String method; - public String time; - public String password; -} diff --git a/front/src/common/service/http-wrapper.ts b/front/src/common/service/http-wrapper.ts index f9515bc..54b18ab 100644 --- a/front/src/common/service/http-wrapper.ts +++ b/front/src/common/service/http-wrapper.ts @@ -168,7 +168,10 @@ export class HttpWrapperService { server = environment.defaultServer; } const basePage = environment.server[server]; - let addressServerRest = `${basePage }/`; + let addressServerRest = basePage; + if (!basePage.endsWith("/")) { + addressServerRest = `${basePage}/`; + } let options = inputOptions; if(isNullOrUndefined(options)) { options = {}; @@ -177,7 +180,11 @@ export class HttpWrapperService { if (isArrayOfs(api, isString, isNumber, isBoolean)) { for (let iii=0; iii { + console.log(`Put on ${urlPath}`); return new Promise((resolve, reject) => { this.request({ endPoint: urlPath,