1
Fork 0
mirror of https://github.com/Steffo99/sophon.git synced 2024-12-22 06:44:21 +00:00

Implement a new path parser

This commit is contained in:
Steffo 2021-11-08 18:38:30 +01:00 committed by Stefano Pigozzi
parent 4b6a0fd5db
commit a8e78c29d7
3 changed files with 140 additions and 58 deletions

View file

@ -5,17 +5,17 @@ import { InstanceEncoder } from "./InstanceEncoder"
test("encodes pathless URL", () => { test("encodes pathless URL", () => {
expect( expect(
InstanceEncoder.encode(new URL("https://api.sophon.steffo.eu")), InstanceEncoder.encode(new URL("https://api.sophon.steffo.eu/")),
).toEqual( ).toEqual(
"https:api.sophon.steffo.eu", "https:api.sophon.steffo.eu:",
) )
}) })
test("encodes URL with port number", () => { test("encodes URL with port number", () => {
expect( expect(
InstanceEncoder.encode(new URL("http://localhost:30033")), InstanceEncoder.encode(new URL("http://localhost:30033/")),
).toEqual( ).toEqual(
"http:localhost%3A30033", "http:localhost%3A30033:",
) )
}) })
@ -43,23 +43,23 @@ test("does not encode URL with %3A in path", () => {
test("decodes pathless URL", () => { test("decodes pathless URL", () => {
expect( expect(
InstanceEncoder.decode("https:api.sophon.steffo.eu"), InstanceEncoder.decode("https:api.sophon.steffo.eu:").toString(),
).toEqual( ).toEqual(
"https://api.sophon.steffo.eu", "https://api.sophon.steffo.eu/",
) )
}) })
test("decodes URL with port number", () => { test("decodes URL with port number", () => {
expect( expect(
InstanceEncoder.decode("http:localhost%3A30033"), InstanceEncoder.decode("http:localhost%3A30033:").toString(),
).toEqual( ).toEqual(
"http://localhost:30033", "http://localhost:30033/",
) )
}) })
test("decodes URL with simple path", () => { test("decodes URL with simple path", () => {
expect( expect(
InstanceEncoder.decode("https:steffo.eu:sophon:api:"), InstanceEncoder.decode("https:steffo.eu:sophon:api:").toString(),
).toEqual( ).toEqual(
"https://steffo.eu/sophon/api/", "https://steffo.eu/sophon/api/",
) )
@ -67,7 +67,7 @@ test("decodes URL with simple path", () => {
test("decodes URL with colon in path", () => { test("decodes URL with colon in path", () => {
expect( expect(
InstanceEncoder.decode("https:steffo.eu:sophon%3Aapi:"), InstanceEncoder.decode("https:steffo.eu:sophon%3Aapi:").toString(),
).toEqual( ).toEqual(
"https://steffo.eu/sophon:api/", "https://steffo.eu/sophon:api/",
) )

View file

@ -5,74 +5,79 @@ test("parses empty path", () => {
expect( expect(
parsePath("/"), parsePath("/"),
).toMatchObject( ).toMatchObject(
{}, {
count: 0,
valid: true,
},
) )
}) })
test("parses instance path", () => { test("parses instance path", () => {
expect( expect(
parsePath("/i/https:api:sophon:steffo:eu:"), parsePath("/i/https:api:sophon:steffo:eu:/"),
).toMatchObject( ).toMatchObject(
{ {
instance: "https:api:sophon:steffo:eu:", instance: "https:api:sophon:steffo:eu:",
count: 1,
valid: true,
}, },
) )
}) })
test("parses username path", () => { test("parses logged in path", () => {
expect( expect(
parsePath("/i/https:api:sophon:steffo:eu:/u/steffo"), parsePath("/i/https:api:sophon:steffo:eu:/l/logged-in/"),
).toMatchObject( ).toMatchObject(
{ {
instance: "https:api:sophon:steffo:eu:", instance: "https:api:sophon:steffo:eu:",
userName: "steffo", loggedIn: "logged-in",
}, count: 2,
) valid: true,
})
test("parses userid path", () => {
expect(
parsePath("/i/https:api:sophon:steffo:eu:/u/1"),
).toMatchObject(
{
instance: "https:api:sophon:steffo:eu:",
userId: "1",
}, },
) )
}) })
test("parses research group path", () => { test("parses research group path", () => {
expect( expect(
parsePath("/i/https:api:sophon:steffo:eu:/g/testers"), parsePath("/i/https:api:sophon:steffo:eu:/l/logged-in/g/testers/"),
).toMatchObject( ).toMatchObject(
{ {
instance: "https:api:sophon:steffo:eu:", instance: "https:api:sophon:steffo:eu:",
loggedIn: "logged-in",
researchGroup: "testers", researchGroup: "testers",
count: 3,
valid: true,
}, },
) )
}) })
test("parses research project path", () => { test("parses research project path", () => {
expect( expect(
parsePath("/i/https:api:sophon:steffo:eu:/g/testers/p/test"), parsePath("/i/https:api:sophon:steffo:eu:/l/logged-in/g/testers/p/test/"),
).toMatchObject( ).toMatchObject(
{ {
instance: "https:api:sophon:steffo:eu:", instance: "https:api:sophon:steffo:eu:",
loggedIn: "logged-in",
researchGroup: "testers", researchGroup: "testers",
researchProject: "test", researchProject: "test",
count: 4,
valid: true,
}, },
) )
}) })
test("parses research project path", () => { test("parses notebook path", () => {
expect( expect(
parsePath("/i/https:api:sophon:steffo:eu:/g/testers/p/test/n/testerino"), parsePath("/i/https:api:sophon:steffo:eu:/l/logged-in/g/testers/p/test/n/testerino/"),
).toMatchObject( ).toMatchObject(
{ {
instance: "https:api:sophon:steffo:eu:", instance: "https:api:sophon:steffo:eu:",
loggedIn: "logged-in",
researchGroup: "testers", researchGroup: "testers",
researchProject: "test", researchProject: "test",
notebook: "testerino", notebook: "testerino",
count: 5,
valid: true,
}, },
) )
}) })

View file

@ -25,44 +25,121 @@ export interface ParsedPath {
/** /**
* Passed the login page (either by browsing as guest or by logging in). * Passed the login page (either by browsing as guest or by logging in).
*/ */
loggedIn?: boolean, loggedIn?: string,
/** /**
* The number of pages that separate this to the website root. * The number of pages that separate this to the website root.
*/ */
count: number, count: number,
/**
* Whether the path is valid or not.
*/
valid: boolean,
} }
const INSTANCE_REGEX = /^[/]i[/](?<value>[^\\\n]+?)(?<rest>[/].*?)?$/
const AUTHORIZATION_REGEX = /^[/]l[/](?<value>logged-in)(?<rest>[/].*?)?$/
const GROUP_REGEX = /^[/]g[/](?<value>[^\\\n]+?)(?<rest>[/].*?)?$/
const PROJECT_REGEX = /^[/]p[/](?<value>[^\\\n]+?)(?<rest>[/].*?)?$/
const NOTEBOOK_REGEX = /^[/]n[/](?<value>[^\\\n]+?)(?<rest>[/].*?)?$/
interface ParsePathSegmentConfig {
path: string,
parsed: ParsedPath,
regex: RegExp,
key: keyof ParsedPath,
next: ((path: string, parsed: ParsedPath) => ParsedPath)[],
}
function parsePathSegment({path, parsed, regex, key, next}: ParsePathSegmentConfig): ParsedPath {
// If the path is empty, return
if(!path) {
return parsed
}
// Try matching the regex
const match = path.match(regex)
// If the match fails, it means the path is invalid
if(!match || !match.groups) {
parsed.valid = Boolean(path)
return parsed
}
// Unpack the groups
const {value, rest} = match.groups
parsed[key] = value as never // WHAT?
parsed.count += 1
const results = next.map((func) => {
return func(rest, parsed)
}).reduce((a, b) => {
return {...a, ...b}
}, {})
return {
...parsed,
...results,
}
}
/** /**
* Split the URL path into various components. * Split the URL path into various components.
* @param path - The path to split. * @param path - The path to split.
*/ */
export function parsePath(path: string): ParsedPath { export function parsePath(path: string): ParsedPath {
let result: ParsedPath = { return parsePathSegment({
count: 0, path,
} parsed: {count: 0, valid: true},
regex: INSTANCE_REGEX,
result.instance = path.match(/[/]i[/]([^/]+)/)?.[1] key: "instance",
result.researchGroup = path.match(/[/]g[/]([A-Za-z0-9_-]+)/)?.[1] next: [
result.researchProject = path.match(/[/]p[/]([A-Za-z0-9_-]+)/)?.[1] (path, parsed) => {
result.notebook = path.match(/[/]n[/]([A-Za-z0-9_-]+)/)?.[1] return parsePathSegment({
result.loggedIn = Boolean(path.match(/[/]l[/]logged-in/)) path,
parsed,
if(result.instance) { regex: AUTHORIZATION_REGEX,
result.count += 1 key: "loggedIn",
} next: [
if(result.researchGroup) { (path, parsed) => {
result.count += 1 return parsePathSegment({
} path,
if(result.researchProject) { parsed,
result.count += 1 regex: GROUP_REGEX,
} key: "researchGroup",
if(result.notebook) { next: [
result.count += 1 (path, parsed) => {
} return parsePathSegment({
if(result.loggedIn) { path,
result.count += 1 parsed,
} regex: PROJECT_REGEX,
key: "researchProject",
return result next: [
(path, parsed) => {
return parsePathSegment({
path,
parsed,
regex: NOTEBOOK_REGEX,
key: "notebook",
next: [],
})
},
],
})
},
],
})
},
],
})
},
]
})
} }