How to return a object from Spring Flux flatmap operation - spring

I am looking to return a Mono.just(file.getAbsolutePath()) after I have saved the file. The following is my code:
public Mono<String> save(Mono<FilePart> filePartMono) {
Mono<String> monoString = filePartMono.flatMap(filePart -> {
File file = new File(filePart.filename());
if (file.exists()) {
file.delete();
LOG.info("existing file deleted: {}", file.getAbsolutePath());
}
Mono<Void> mono = filePart.transferTo(file);
LOG.info("file saved: {}", file.getAbsolutePath());
return Mono.just(file.getAbsolutePath());
}).thenReturn("hello");
return monoString;
Right now I am returning a "hello". Is there a way I can return the file.getAbsolutePath() instead of the string out of my save() method?

I think it can be done like this:
public Mono<String> save(Mono<FilePart> filePartMono) {
return filePartMono.flatMap(filePart -> {
File file = new File(filePart.filename());
if (file.exists()) {
file.delete();
log.info("existing file deleted: {}", file.getAbsolutePath());
}
return filePart.transferTo(file)
.doOnNext(v -> {
log.info("file saved: {}", file.getAbsolutePath());
}).thenReturn(file.getAbsolutePath());
});
}

Related

Problem with JWT verification in Next.js and Spring Boot

I am trying to make protected routes in Next.js using middleware:
import { type NextRequest, NextResponse } from 'next/server';
import { verifyAuth } from '#helpers/auth/verifyJWT';
export const config = {
matcher: ['/dash', '/auth/:path*'],
};
export async function middleware(req: NextRequest) {
const verifiedToken = await verifyAuth(req).catch((err) => {
console.error(err.message);
});
if (!verifiedToken) {
if (req.nextUrl.pathname.startsWith('/dash')) {
return NextResponse.redirect(new URL('/auth/login', req.url));
} else {
return NextResponse.next();
}
} else {
if (req.nextUrl.pathname.startsWith('/auth')) {
return NextResponse.redirect(new URL('/dash', req.url));
} else {
return NextResponse.next();
}
}
}
Here is verifyAuth method:
import type { NextRequest } from 'next/server';
import { jwtVerify } from 'jose';
import { USER_TOKEN, getJwtSecretKey } from '../constatns';
interface UserJwtPayload {
jti: string;
iat: number;
}
export class AuthError extends Error {}
export async function verifyAuth(req: NextRequest) {
const token = req.cookies.get(USER_TOKEN)?.value;
if (!token) throw new AuthError('Missing user token ');
try {
const verified = await jwtVerify(
token,
new TextEncoder().encode(getJwtSecretKey()),
);
return verified.payload as UserJwtPayload;
} catch (err) {
throw new AuthError('Your token has expired.');
}
}
Problem is that I always get the message: Token is expired.
I am using Spring Boot for backend part, here is my SpringSecurity part for signing token:
public String generateToken(
Map<String, Object> extraClaims,
UserDetails userDetails
) {
return Jwts
.builder()
.setClaims(extraClaims)
.setSubject(userDetails.getUsername())
.setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 24))
.signWith(getSignInKey(), SignatureAlgorithm.HS256)
.compact();
}
private Key getSignInKey() {
byte[] keyBytes = Decoders.BASE64.decode(SECRET_KEY);
return Keys.hmacShaKeyFor(keyBytes);
}
So I tried many libraries to code/encode my secret key in the Nextjs app but same error.
I am not sure how to do it.
Is there any other solution?

How to mock callbacks with mockito correctly?

I have a method like this:
override fun functionToBeMocked(
param: Param,
onSuccess: (param: Param) -> Unit,
onError: (param: Param, errorMessage: String) -> Unit
) {
val response = factory.request(param)
.exchange()
.block()
if (response?.statusCode()?.is2xxSuccessful == true {
onSuccess(param)
} else if (response?.statusCode()?.isError == true) {
val status = response.rawStatusCode()
val body = response.bodyToMono(String::class.java).block()
val errorMessage = "$status : $body"
onError(param, errorMessage)
} else {
return
}
}
I want to test the service which calls this method with the given onSuccess and onError functions. How can I mock functionToBeMocked() to just return onSuccess(param) or onError(param)?
Current test:
#Test
fun test() {
val failure = ParamDoc(param.id, param)
whenever(repo.findAll()).thenReturn(listOf(failure))
// This method call should just execute onSuccess() or onError depending on the testcase
// whenever(mockedService.functionToBeMocked).thenAnswer.. (?)
underTest.functionToBeTested()
// verify(..)
}
Update: request function in factory:
fun request(param: Param): WebClient.RequestHeadersSpec<*> {
val client = WebClient
.builder()
.defaultHeader("API-KEY", config.apiKey)
.baseUrl(config.baseUrl)
.build()
return client
.post()
.uri("/service/test")
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(Dto.fromParam(param))
}

Webclient retryWhen and onErrorResume mutually exclusive?

I am trying to implement a retry on specific exception but I could not make it work using the following:
return client
.sendWebhook(request, url)
.exchangeToMono(
response -> {
final HttpStatus status = response.statusCode();
return response
.bodyToMono(String.class)
.defaultIfEmpty(StringUtils.EMPTY)
.map(
body -> {
if (status.is2xxSuccessful()) {
log.info("HTTP_SUCCESS[{}][{}] body[{}]", functionName, company, body);
return ResponseEntity.ok().body(body);
} else {
log.warn(
format(
"HTTP_ERROR[%s][%s] status[%s] body[%s]",
functionName, company, status, body));
return status.is4xxClientError()
? ResponseEntity.badRequest().body(body)
: ResponseEntity.internalServerError().body(body);
}
});
})
.retryWhen(
Retry.backoff(1, Duration.ofSeconds(1))
.filter(
err -> {
if (err instanceof PrematureCloseException) {
log.warn("PrematureCloseException detected retrying.");
return true;
}
return false;
}))
.onErrorResume(
ex -> {
log.warn(
format(
"HTTP_ERROR[%s][%s] errorInternal[%s]",
functionName, company, ex.getMessage()));
return Mono.just(ResponseEntity.internalServerError().body(ex.getMessage()));
});
It seems that the retry is never getting called on PrematureCloseException.
Resolved, it was not working because of rootCause
Retry.backoff(3, Duration.ofMillis(500))
.filter(
ex -> {
if (ExceptionUtils.getRootCause(ex) instanceof PrematureCloseException) {
log.info(
"HTTP_RETRY[{}][{}] PrematureClose detected retrying", functionName, company);
return true;
}
return false;
});

Spring webClient generate NullPointException after going through the filter

I want to do exception handling. When StatusCode 200, but not the desired body.
To make the processing global I am using filter.
below is the code.
public WebClient webClient() {
return WebClient.builder()
.filter(ExchangeFilterFunction.ofResponseProcessor(response -> {
// statusCode 400 || 500
if (response.statusCode().isError()) {
return response.bodyToMono(String.class)
.flatMap(body ->
Mono.error(new ExternalClientException(body))
)
);
}
// statusCode 200
return response.bodyToMono(String.class)
.flatMap(body -> {
if (YanoljaConstants.EMPTY_BODY.contains(body)) {
return Mono.error(new ExternalClientException(body))
.cast(ClientResponse.class);
}
return Mono.just(response); // normal
});
}))
.build();
}
And the actual client code is shown below.
public Foo getPlace(int no) {
return Objects.requireNonNull(contentsApiV2Client.get()
.uri(uriBuilder -> uriBuilder.path("/path").build(no))
.retrieve()
.bodyToMono(new ParameterizedTypeReference<Foo>() {
})
.block());
}
When using this, NullPointerException occurs when responding normally after going through the filter(return Mono#just(response)).
I want clear this issue.
For reference, the below code works normally.
public WebClient webClient() {
return WebClient.builder()
.filter(ExchangeFilterFunction.ofResponseProcessor(response -> {
// statusCode 400 || 500
if (response.statusCode().isError()) {
return response.bodyToMono(String.class)
.flatMap(body ->
Mono.error(new ExternalClientException(body))
)
);
}
return Mono.just(response); // normal
}))
.build();
}
Thank you. waiting for your answer!
Solve!
public WebClient webClient() {
return WebClient.builder()
.filter(ExchangeFilterFunction.ofResponseProcessor(response -> {
// statusCode 400 || 500
if (response.statusCode().isError()) {
return response.bodyToMono(String.class)
.flatMap(body ->
Mono.error(new ExternalClientException(body))
)
);
}
// statusCode 200
return response.bodyToMono(String.class)
.flatMap(body -> {
if (YanoljaConstants.EMPTY_BODY.contains(body)) {
return Mono.error(new ExternalClientException(body))
.cast(ClientResponse.class);
}
return Mono.just(ClientResponse.create(HttpStatus.OK)
.header(HttpHeaders.CONTENT_TYPE, "application/json")
.body(body)
.build()); // normal
});
}))
.build();
}
I realized that running the monoTobody method consumes it and the value disappears. So I solved it by creating a new ClientResponse.
Thank you.

Kendo-editor: How to insert image in kendo editor?

How to insert Image from local machine to Kendo Editor? I am unable to perform this action.
You should use image-Browser in Kendo-Editor, by default Kendo-Editor store link of insert image, in your case you upload image from local machine.
You must create custom controller to upload you image to server and return image link.
I have mention below code, I hope this code is usefull for you.
Create controller with name ImageBrowser
public class ImageBrowserController : Controller
{
private const string contentFolderRoot = "~/";
private const string prettyName = "ServerPathForImage/";
private const string DefaultFilter = "*.png,*.gif,*.jpg,*.jpeg";
private const int ThumbnailHeight = 80;
private const int ThumbnailWidth = 80;
private readonly DirectoryBrowser directoryBrowser;
private readonly ThumbnailCreator thumbnailCreator;
public ImageBrowserController()
{
directoryBrowser = new DirectoryBrowser();
thumbnailCreator = new ThumbnailCreator();
}
public string ContentPath
{
get
{
return Path.Combine(contentFolderRoot, prettyName);
}
}
private string ToAbsolute(string virtualPath)
{
return VirtualPathUtility.ToAbsolute(virtualPath);
}
private string CombinePaths(string basePath, string relativePath)
{
return VirtualPathUtility.Combine(VirtualPathUtility.AppendTrailingSlash(basePath), relativePath);
}
public virtual bool AuthorizeRead(string path)
{
return CanAccess(path);
}
protected virtual bool CanAccess(string path)
{
return path.StartsWith(ToAbsolute(ContentPath), StringComparison.OrdinalIgnoreCase);
}
private string NormalizePath(string path)
{
if (string.IsNullOrEmpty(path))
{
return ToAbsolute(ContentPath);
}
return CombinePaths(ToAbsolute(ContentPath), path);
}
public virtual JsonResult Read(string path)
{
path = NormalizePath(path);
if (AuthorizeRead(path))
{
try
{
directoryBrowser.Server = Server;
var result = directoryBrowser
.GetContent(path, DefaultFilter)
.Select(f => new
{
name = f.Name,
type = f.Type == EntryType.File ? "f" : "d",
size = f.Size
});
return Json(result, JsonRequestBehavior.AllowGet);
}
catch (DirectoryNotFoundException)
{
throw new HttpException(404, "File Not Found");
}
}
throw new HttpException(403, "Forbidden");
}
public virtual bool AuthorizeThumbnail(string path)
{
return CanAccess(path);
}
[OutputCache(Duration = 3600, VaryByParam = "path")]
public virtual ActionResult Thumbnail(string path)
{
path = NormalizePath(path);
if (AuthorizeThumbnail(path))
{
var physicalPath = Server.MapPath(path);
if (System.IO.File.Exists(physicalPath))
{
Response.AddFileDependency(physicalPath);
return CreateThumbnail(physicalPath);
}
else
{
throw new HttpException(404, "File Not Found");
}
}
else
{
throw new HttpException(403, "Forbidden");
}
}
private FileContentResult CreateThumbnail(string physicalPath)
{
using (var fileStream = System.IO.File.OpenRead(physicalPath))
{
var desiredSize = new ImageSize
{
Width = ThumbnailWidth,
Height = ThumbnailHeight
};
const string contentType = "image/png";
return File(thumbnailCreator.Create(fileStream, desiredSize, contentType), contentType);
}
}
[AcceptVerbs(HttpVerbs.Post)]
public virtual ActionResult Destroy(string path, string name, string type)
{
path = NormalizePath(path);
if (!string.IsNullOrEmpty(name) && !string.IsNullOrEmpty(type))
{
path = CombinePaths(path, name);
if (type.ToLowerInvariant() == "f")
{
DeleteFile(path);
}
else
{
DeleteDirectory(path);
}
return Json(null);
}
throw new HttpException(404, "File Not Found");
}
public virtual bool AuthorizeDeleteFile(string path)
{
return CanAccess(path);
}
public virtual bool AuthorizeDeleteDirectory(string path)
{
return CanAccess(path);
}
protected virtual void DeleteFile(string path)
{
if (!AuthorizeDeleteFile(path))
{
throw new HttpException(403, "Forbidden");
}
var physicalPath = Server.MapPath(path);
if (System.IO.File.Exists(physicalPath))
{
System.IO.File.Delete(physicalPath);
}
}
protected virtual void DeleteDirectory(string path)
{
if (!AuthorizeDeleteDirectory(path))
{
throw new HttpException(403, "Forbidden");
}
var physicalPath = Server.MapPath(path);
if (Directory.Exists(physicalPath))
{
Directory.Delete(physicalPath, true);
}
}
public virtual bool AuthorizeCreateDirectory(string path, string name)
{
return CanAccess(path);
}
[AcceptVerbs(HttpVerbs.Post)]
public virtual ActionResult Create(string path, FileBrowserEntry entry)
{
path = NormalizePath(path);
var name = entry.Name;
if (!string.IsNullOrEmpty(name) && AuthorizeCreateDirectory(path, name))
{
var physicalPath = Path.Combine(Server.MapPath(path), name);
if (!Directory.Exists(physicalPath))
{
Directory.CreateDirectory(physicalPath);
}
return Json(null);
}
throw new HttpException(403, "Forbidden");
}
public virtual bool AuthorizeUpload(string path, HttpPostedFileBase file)
{
return CanAccess(path) && IsValidFile(file.FileName);
}
private bool IsValidFile(string fileName)
{
var extension = Path.GetExtension(fileName);
var allowedExtensions = DefaultFilter.Split(',');
return allowedExtensions.Any(e => e.EndsWith(extension, StringComparison.InvariantCultureIgnoreCase));
}
[AcceptVerbs(HttpVerbs.Post)]
public virtual ActionResult Upload(string path, HttpPostedFileBase file)
{
path = NormalizePath(path);
var fileName = Path.GetFileName(file.FileName);
if (AuthorizeUpload(path, file))
{
file.SaveAs(Path.Combine(Server.MapPath(path), fileName));
return Json(new
{
size = file.ContentLength,
name = fileName,
type = "f"
}, "text/plain");
}
throw new HttpException(403, "Forbidden");
}
[OutputCache(Duration = 360, VaryByParam = "path")]
public ActionResult Image(string path)
{
path = NormalizePath(path);
if (AuthorizeImage(path))
{
var physicalPath = Server.MapPath(path);
if (System.IO.File.Exists(physicalPath))
{
const string contentType = "image/png";
return File(System.IO.File.OpenRead(physicalPath), contentType);
}
}
throw new HttpException(403, "Forbidden");
}
public virtual bool AuthorizeImage(string path)
{
return CanAccess(path) && IsValidFile(Path.GetExtension(path));
}
}
In view you need to create text-area
<textarea class="classicEditor" name="Contents" id="classicEditor"></textarea>
Now you need to bind kendo-editor with text-area in javascript
<script type="text/javascript">
$(document).ready(function () {
$("#classicEditor").kendoEditor({
imageBrowser: {
transport: {
read: "#Url.Action("Read", "ImageBrowser")",
destroy: {
url: "#Url.Action("Destroy", "ImageBrowser")",
type: "POST"
},
create: {
url: "#Url.Action("Create", "ImageBrowser")",
type: "POST"
},
thumbnailUrl: "#Url.Action("Thumbnail", "ImageBrowser")",
uploadUrl: "#Url.Action("Upload", "ImageBrowser")",
imageUrl: "/ImageBrowser/Image?path={0}"
}
},
tools: [
"bold",
"italic",
"underline",
"strikethrough",
"justifyLeft",
"justifyCenter",
"justifyRight",
"justifyFull",
"VerticalAlignTop",
"Vertical-AlignTop",
"Verticaltop",
"insertUnorderedList",
"insertOrderedList",
"indent",
"outdent",
"insertImage",
"subscript",
"superscript",
"createTable",
"addRowAbove",
"addRowBelow",
"addColumnLeft",
"addColumnRight",
"deleteRow",
"deleteColumn",
"viewHtml",
"formatting",
"cleanFormatting",
"fontName",
"fontSize",
"foreColor",
"backColor",
"print"
]
});
})
</script>
If you have facing layout problem of image-browser, you need to resolve with the help of below code.
<style>
.k-window {
width: 430px !important;
}
.k-imagebrowser {
margin-left: 25px !important;
}
</style>
You should use File and image browser which let's you upload images to your server and then use them. As descrbied here : http://demos.telerik.com/kendo-ui/editor/imagebrowser

Resources