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

View file

@ -5,74 +5,79 @@ test("parses empty path", () => {
expect(
parsePath("/"),
).toMatchObject(
{},
{
count: 0,
valid: true,
},
)
})
test("parses instance path", () => {
expect(
parsePath("/i/https:api:sophon:steffo:eu:"),
parsePath("/i/https:api:sophon:steffo:eu:/"),
).toMatchObject(
{
instance: "https:api:sophon:steffo:eu:",
count: 1,
valid: true,
},
)
})
test("parses username path", () => {
test("parses logged in path", () => {
expect(
parsePath("/i/https:api:sophon:steffo:eu:/u/steffo"),
parsePath("/i/https:api:sophon:steffo:eu:/l/logged-in/"),
).toMatchObject(
{
instance: "https:api:sophon:steffo:eu:",
userName: "steffo",
},
)
})
test("parses userid path", () => {
expect(
parsePath("/i/https:api:sophon:steffo:eu:/u/1"),
).toMatchObject(
{
instance: "https:api:sophon:steffo:eu:",
userId: "1",
loggedIn: "logged-in",
count: 2,
valid: true,
},
)
})
test("parses research group path", () => {
expect(
parsePath("/i/https:api:sophon:steffo:eu:/g/testers"),
parsePath("/i/https:api:sophon:steffo:eu:/l/logged-in/g/testers/"),
).toMatchObject(
{
instance: "https:api:sophon:steffo:eu:",
loggedIn: "logged-in",
researchGroup: "testers",
count: 3,
valid: true,
},
)
})
test("parses research project path", () => {
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(
{
instance: "https:api:sophon:steffo:eu:",
loggedIn: "logged-in",
researchGroup: "testers",
researchProject: "test",
count: 4,
valid: true,
},
)
})
test("parses research project path", () => {
test("parses notebook path", () => {
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(
{
instance: "https:api:sophon:steffo:eu:",
loggedIn: "logged-in",
researchGroup: "testers",
researchProject: "test",
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).
*/
loggedIn?: boolean,
loggedIn?: string,
/**
* The number of pages that separate this to the website root.
*/
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.
* @param path - The path to split.
*/
export function parsePath(path: string): ParsedPath {
let result: ParsedPath = {
count: 0,
}
result.instance = path.match(/[/]i[/]([^/]+)/)?.[1]
result.researchGroup = path.match(/[/]g[/]([A-Za-z0-9_-]+)/)?.[1]
result.researchProject = path.match(/[/]p[/]([A-Za-z0-9_-]+)/)?.[1]
result.notebook = path.match(/[/]n[/]([A-Za-z0-9_-]+)/)?.[1]
result.loggedIn = Boolean(path.match(/[/]l[/]logged-in/))
if(result.instance) {
result.count += 1
}
if(result.researchGroup) {
result.count += 1
}
if(result.researchProject) {
result.count += 1
}
if(result.notebook) {
result.count += 1
}
if(result.loggedIn) {
result.count += 1
}
return result
return parsePathSegment({
path,
parsed: {count: 0, valid: true},
regex: INSTANCE_REGEX,
key: "instance",
next: [
(path, parsed) => {
return parsePathSegment({
path,
parsed,
regex: AUTHORIZATION_REGEX,
key: "loggedIn",
next: [
(path, parsed) => {
return parsePathSegment({
path,
parsed,
regex: GROUP_REGEX,
key: "researchGroup",
next: [
(path, parsed) => {
return parsePathSegment({
path,
parsed,
regex: PROJECT_REGEX,
key: "researchProject",
next: [
(path, parsed) => {
return parsePathSegment({
path,
parsed,
regex: NOTEBOOK_REGEX,
key: "notebook",
next: [],
})
},
],
})
},
],
})
},
],
})
},
]
})
}