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:
parent
4b6a0fd5db
commit
a8e78c29d7
3 changed files with 140 additions and 58 deletions
|
@ -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/",
|
||||
)
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
)
|
||||
})
|
||||
|
|
|
@ -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: [],
|
||||
})
|
||||
},
|
||||
],
|
||||
})
|
||||
},
|
||||
],
|
||||
})
|
||||
},
|
||||
],
|
||||
})
|
||||
},
|
||||
]
|
||||
})
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue