Reputation: 2209
I have an Angular application that browses a virtual filesystem. Paths are accessed by (e.g.) /my/endpoint/path/to/file.ext
. I'd like to be able to correctly handle paths containing '..' so that (e.g.) path/to/some/directory/../../file.ext
resolves to path/to/file.ext
. I can handle resolving the former path to the latter, but I don't know how to navigate there. I've put what I've got so far (various items omitted for clarity) below. Am I on the right track by calling navigate
in iif()
? What should I be returning there to make sure switchMap()
gets the string it expects?
The goal is that, when the URL http://myapp.app/my/endpoint/path/to/some/directory/../../blah
is accessed, the component lists the contents of path/to/blah
and the browser URL is http://myapp.app/my/endpoint/path/to/blah
.
class MyComponent implements OnInit {
constructor(
private router: Router,
private log: LoggingService,
private backend: BackendService,
private route: ActivatedRoute,
private fileSaverService: FileSaverService
) {}
ngOnInit(): void {
this.log.debug('URL: ', this.url);
this.path$
.pipe(
mergeMap((path) => {
return iif(
() => path.split('/').includes('..'),
(path) => {
const newPath = resolvePath(path.split('/'));
// Not sure what to return here so that switchMap() gets a path
return from(this.router.navigate(newPath));
},
of(path)
);
}),
switchMap((path) => this.listFiles(path)),
catchError((err) => {
this.log.error('Error listing files', err);
return throwError(() => err);
}),
mergeMap((result) => {
const found = result.entries.find(
({ type, path }) => type === 'file' && path === result.path
);
return iif(
() => !!found,
this.getFile(found),
of(result.entries)
);
})
)
.subscribe((result) => {
if (result) {
this.contents = result;
} else {
this.log.debug('Got file', { result });
}
});
}
}
const resolvePath = (segments: string[]): string[] => {
const index = segments.indexOf('..');
if (index > -1) {
const left = segments.slice(0, index - 1);
const right = resolvePath(segments.slice(index + 1));
return [...left, ...right];
} else {
return segments;
}
}
EDIT: If I'm understanding @Tortilla correctly, here's what it should look like.
const navigate$ = this.path$.pipe(
map((path) => path.split('/')),
filter((segments) => segments.includes('..')),
map((segments) => {
const newpath = resolvePath(segments);
this.log.debug(`NAVIGATE TO ${newpath}`);
// await this.router.navigate([path])
return newpath.join('/');
})
);
const noNavigate$ = this.path$.pipe(
filter((path) => !path.split('/').includes('..'))
);
const result$ = merge(
navigate$, noNavigate$
)
result$.pipe(...) // as above
Upvotes: 0
Views: 194
Reputation: 568
First of all I think you are not using iif
correctly. It is not the same as simple if. Here is a great explanation along with the comment about defer
.
And as for your issue itself, Router.navigate doesn't return path. So just go with this:
this.router.navigate(newPath);
return of(newPath);
But again take a closer look at iif
. I would suggest to use simple if or ternary operator instead.
Upvotes: 1