Propagate / delegate path value to child routes akka http - scala

I want to have a base route that receive a IntNumber and make some checks with the database to know if the values is correct or not and then If the value is correct I want to propagate the value to child routes.
Base Route
class BaseServiceRoute extends PathDirectives with SecurityDirectives {
val baseUserRoute = pathPrefix("user" / IntNumber)
}
How can I make some checks for the value IntNumber and the delegate the value to the child route? Directives?
Child Route
class CategoryServiceRoute(implicit executionContext: ExecutionContext) extends BaseServiceRoute {
val route = baseUserRoute { userId =>
pathPrefix("category") {
pathEndOrSingleSlash {
get {
complete(s"$userId")
}
} ~
path(LongNumber) { categoryId =>
get {
complete(s"$categoryId")
} ~
post {
complete("Hello category post")
}
}
}
}
}
Thanks

The best-practice suggestion would be just to nest the routes so that you can still access the value from the outer route like this:
pathPrefix("user" / IntNumber) { userId =>
pathPrefix("category") {
pathEndOrSingleSlash {
get {
complete(s"$userId")
}
}
}
}
However, you seem to want to separate routes into multiple parts which is perfectly fine. In that case just make the child route a def:
def childRoute(userId: Int): Route =
pathPrefix("category") {
pathEndOrSingleSlash {
get {
complete(s"$userId")
}
}
}
and then use it like this:
baseUserRoute(childRoute)

Related

Akka HTTP Route Handling [duplicate]

This question already has an answer here:
Akka-HTTP route not found?
(1 answer)
Closed 1 year ago.
I have the following route setup
val routes = protectedRoute("wallet") { user =>
concat {
path("wallet" / "deposit") {
get {
completeEitherT(walletService.deposit(user._id, "dollars", 50))
}
}
path("wallet") {
get {
completeEitherT(walletService.getForUser(user._id))
}
}
}
}
The completeEitherT function is as follows
def completeEitherT[T](t: EitherT[Future, DatabaseError, T])(implicit marsh: ToResponseMarshaller[T]): Route = {
onSuccess(t.value) {
case Left(x: DatabaseErrors.NotFound) =>
logger.error(x.toString)
complete(StatusCodes.NotFound)
case Left(error) => complete(StatusCodes.InternalServerError)
case Right(value) => complete(value)
}
}
The protectedRoute Directive is:
def protectedRoute(permissions: String*): Directive1[User] = new Directive1[User] {
override def tapply(f: (Tuple1[User]) => Route): Route = {
headerValueByName("Authorization") { jwt =>
Jwt.decode(jwt, jwtSecret, List(algo)) match {
case Failure(_) =>
logger.error("Unauthorized access from jwtToken:", jwt)
complete(StatusCodes.Unauthorized)
case Success(value) =>
val user = JsonParser(value.content).convertTo[User]
if (user.roles.flatMap(_.permissions).exists(permissions.contains)) f(Tuple1(user))
else complete(StatusCodes.Forbidden)
}
}
}
}
The issue that I am having is that whichever path is defined first results in a 404 when called via Postman.
The requested resource could not be found.
This is not a response from completeEitherT as it does not log the error
How do I define two or more paths inside the same directive?
Please mark as duplicate, I could not find this on Google Search but SO showed this as related.
Answer
Essentially I am missing a ~ between the paths
val routes = protectedRoute("wallet") { user =>
concat {
path("wallet" / "deposit") {
get {
completeEitherT(walletService.deposit(user._id, "dollars", 50))
}
} ~
path("wallet") {
get {
completeEitherT(walletService.getForUser(user._id))
}
}
}
}

Type 'Object' provides no match for the signature '(artistsongs: string): string'.ts(2322)

artistSelected(item: any) {
console.log(item.artist);
this.songsServices.getSongsListArtist(item.artist).subscribe(
result => {
this.songsServices.artistList = result;
this.route.navigate(['./artistsongs']);
},
error => {
console.log('There is an error');
}
);
}
Service
getSongsListArtist(album: string) {
const completeUrl = `${environment.API_URL}?album=${album}`;
return this.httpClient.get(encodeURI(completeUrl));
}
artistList(artistsongs: string) {
return artistsongs;
}
I am facing an error in this. Please sole me how to pass the object from one ts file to another ts file by using service.
artistList(artistsongs: string) {
return artistsongs;
}
change to:
artistList(artistsongs: any) {
return artistsongs;
}
As i understand when you call getSongsListArtist it will return an array object or an object not a string only. You can use typeof() to check this.httpClient.get(encodeURI(completeUrl)) return what kind of type.

How to navigate through a paginated list until find a text with Serenity-JS?

I need to assert that an element I created was added to a list, but it is added at the end of it and it is paginated.
I'm thinking of navigating through each page by calling another task this way:
export class CheckItem implements Task {
static afterCreated(): CheckItem {
return new CheckItem();
}
performAs(actor: PerformsTasks & UsesAbilities): PromiseLike<void> {
return TakeNotes.as(actor).read('item-name')
.then((itemName: string) => actor
.attemptsTo(
NavigateThroughItemList.untilFinds(itemName),
));
}
}
I need to implement NavigateThrougItemList.
Ok, I found the solution by using a recursive call. Maybe there is a better solution for this, but this one worked for me.
export class NavigateThroughItemList implements Task {
static untilFinds(itemName: string): NavigateThroughItemList {
return new NavigateThroughItemList(itemName);
}
constructor(private itemName: string) {
}
#step('{0} navigates through the list until #itemName is found')
performAs(actor: PerformsTasks): PromiseLike<void> {
return actor.attemptsTo(
See.if(Text.ofAll(ItemList.items), include(this.itemName)),
).catch(() => actor
.attemptsTo(
See.if(
WebElement.of(ItemList.nextButton),
isEnabled(),
),
)
.then(() => actor
.attemptsTo(
Click.on(ItemList.nextButton),
))
// Looping until it finds the item
.then(() => this.performAs(actor)));
}
}

Capture exception thrown in prototype (Typescript)

Excellent Angular 2/Material Design framework, Teradata Covalent, provides a RESTService abstract class that wraps REST api calls here:
https://teradata.github.io/covalent/#/components/http
Code to incorporate the extension is easy, as follows:
export class CustomRESTService extends RESTService<any> {
constructor(private _http: Http /* or HttpInterceptorService */) {
super(_http, {
baseUrl: 'www.api.com',
path: '/path/to/endpoint',
headers: new Headers(),
dynamicHeaders: () => new Headers(),
transform: (res: Response): any => res.json(),
});
}
}
The "update" method in the RESTService abstract class is shown here:
public update(id: string | number, obj: T, transform?: IRestTransform): Observable<any> {
let requestOptions: RequestOptionsArgs = this.buildRequestOptions();
let request: Observable<Response> = this.http.patch(this.buildUrl(id), obj, requestOptions);
return request.map((res: Response) => {
if (res.status === 200) {
if (transform) {
return transform(res);
}
return this.transform(res);
} else {
return res;
}
}).catch((error: Response) => {
return new Observable<any>((subscriber: Subscriber<any>) => {
try {
subscriber.error(this.transform(error));
} catch (err) {
subscriber.error(error);
}
});
});
}
My question is: if the update method of the abstract class throws an exception, how can that be captured in the CustomRESTService class? I.e., what Typescript code might one use to display an error in the UI?
Thank you.
First thing's first - Why would you want to catch it inside the rest client and not inside the app's logic?
Assuming you have some good reason for doing that (some other infrastructure code that you're running in the CustomRESTClient class), I would override the update function and implement error handling there.
A simple example without observables:
abstract class Base {
update(n:number):number {
return n;
}
test():bool;
}
class Child extends Base {
update(n:number):number {
return super.update(n)*2;
}
test():bool {
return true;
}
}

How to create Custom Directive by composing multiple directives in spray

I would like to write a custom directive for all get, post,put and delete requests, because all requests needs authorization. To keep code DRY, I want to abstract those boilerplate(I have to authorize more than 100 requests). I could handle all Get Requests as follows
def method1 = Try("hi") // controller methods
def method2 = Try("hello")
path("getData1") {
handleGetRequest(method1)
}
path("getData2") {
handleGetRequest(method2)
}
def customGetDirective: Directive0 = {
headerValueByName("token").flatMap { token =>
authorize(checkAuthorization(token, ""))
get.hflatMap {x=>
respondWithMediaType(`application/json`)
}
}
}
def handleGetRequest(fn: => Try[_]) = {
customGetDirective { ctx=>
val result = fn
val json = //result can be coverted to json
ctx.complete(json)
}
}
I want to write a similar Generic directive for POST request, so that I could use
path("postData1") {
handlePostRequest(saveToDb)
}
def handlePostRequest(fn: => Try[_]) = {
customPostDirective { (ctx, JsonData)=>
//.......
ctx.complete(json)
}
}
but, I dont know how to get RequestContext and JsonData as tuple
I can write like this,
entity(as[String]) { jsonData =>
customPostDirective{ ctx=>
}
}
If I get a tuple it will be more useful.
Thank you.