Skip to content
Merged
202 changes: 188 additions & 14 deletions __test__/projects/GitHubProjectDataSource.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,8 @@ test("It maps projects including branches and tags", async () => {
id: "openapi.yml",
name: "openapi.yml",
url: "/api/blob/acme/foo-openapi/openapi.yml?ref=12345678",
editURL: "https://github.com/acme/foo-openapi/edit/main/openapi.yml"
editURL: "https://github.com/acme/foo-openapi/edit/main/openapi.yml",
isDefault: false
}],
url: "https://github.com/acme/foo-openapi/tree/main",
isDefault: true
Expand All @@ -98,7 +99,8 @@ test("It maps projects including branches and tags", async () => {
id: "openapi.yml",
name: "openapi.yml",
url: "/api/blob/acme/foo-openapi/openapi.yml?ref=12345678",
editURL: "https://github.com/acme/foo-openapi/edit/1.0/openapi.yml"
editURL: "https://github.com/acme/foo-openapi/edit/1.0/openapi.yml",
isDefault: false
}],
url: "https://github.com/acme/foo-openapi/tree/1.0",
isDefault: false
Expand Down Expand Up @@ -195,17 +197,20 @@ test("It supports multiple OpenAPI specifications on a branch", async () => {
id: "foo-service.yml",
name: "foo-service.yml",
url: "/api/blob/acme/foo-openapi/foo-service.yml?ref=12345678",
editURL: "https://github.com/acme/foo-openapi/edit/main/foo-service.yml"
editURL: "https://github.com/acme/foo-openapi/edit/main/foo-service.yml",
isDefault: false
}, {
id: "bar-service.yml",
name: "bar-service.yml",
url: "/api/blob/acme/foo-openapi/bar-service.yml?ref=12345678",
editURL: "https://github.com/acme/foo-openapi/edit/main/bar-service.yml"
editURL: "https://github.com/acme/foo-openapi/edit/main/bar-service.yml",
isDefault: false
}, {
id: "baz-service.yml",
name: "baz-service.yml",
url: "/api/blob/acme/foo-openapi/baz-service.yml?ref=12345678",
editURL: "https://github.com/acme/foo-openapi/edit/main/baz-service.yml"
editURL: "https://github.com/acme/foo-openapi/edit/main/baz-service.yml",
isDefault: false
}],
url: "https://github.com/acme/foo-openapi/tree/main",
isDefault: true
Expand All @@ -216,7 +221,8 @@ test("It supports multiple OpenAPI specifications on a branch", async () => {
id: "openapi.yml",
name: "openapi.yml",
url: "/api/blob/acme/foo-openapi/openapi.yml?ref=12345678",
editURL: "https://github.com/acme/foo-openapi/edit/1.0/openapi.yml"
editURL: "https://github.com/acme/foo-openapi/edit/1.0/openapi.yml",
isDefault: false
}],
url: "https://github.com/acme/foo-openapi/tree/1.0",
isDefault: false
Expand Down Expand Up @@ -749,11 +755,13 @@ test("It adds remote versions from the project configuration", async () => {
specifications: [{
id: "huey",
name: "Huey",
url: `/api/remotes/${base64RemoteConfigEncoder.encode({ url: "https://example.com/huey.yml" })}`
url: `/api/remotes/${base64RemoteConfigEncoder.encode({ url: "https://example.com/huey.yml" })}`,
isDefault: false
}, {
id: "dewey",
name: "Dewey",
url: `/api/remotes/${base64RemoteConfigEncoder.encode({ url: "https://example.com/dewey.yml" })}`
url: `/api/remotes/${base64RemoteConfigEncoder.encode({ url: "https://example.com/dewey.yml" })}`,
isDefault: false
}]
}, {
id: "bobby",
Expand All @@ -762,7 +770,8 @@ test("It adds remote versions from the project configuration", async () => {
specifications: [{
id: "louie",
name: "Louie",
url: `/api/remotes/${base64RemoteConfigEncoder.encode({ url: "https://example.com/louie.yml" })}`
url: `/api/remotes/${base64RemoteConfigEncoder.encode({ url: "https://example.com/louie.yml" })}`,
isDefault: false
}]
}])
})
Expand Down Expand Up @@ -816,7 +825,8 @@ test("It modifies ID of remote version if the ID already exists", async () => {
id: "openapi.yml",
name: "openapi.yml",
url: "/api/blob/acme/foo-openapi/openapi.yml?ref=12345678",
editURL: "https://github.com/acme/foo-openapi/edit/bar/openapi.yml"
editURL: "https://github.com/acme/foo-openapi/edit/bar/openapi.yml",
isDefault: false
}]
}, {
id: "bar1",
Expand All @@ -825,7 +835,8 @@ test("It modifies ID of remote version if the ID already exists", async () => {
specifications: [{
id: "baz",
name: "Baz",
url: `/api/remotes/${base64RemoteConfigEncoder.encode({ url: "https://example.com/baz.yml" })}`
url: `/api/remotes/${base64RemoteConfigEncoder.encode({ url: "https://example.com/baz.yml" })}`,
isDefault: false
}]
}, {
id: "bar2",
Expand All @@ -834,7 +845,8 @@ test("It modifies ID of remote version if the ID already exists", async () => {
specifications: [{
id: "hello",
name: "Hello",
url: `/api/remotes/${base64RemoteConfigEncoder.encode({ url: "https://example.com/hello.yml" })}`
url: `/api/remotes/${base64RemoteConfigEncoder.encode({ url: "https://example.com/hello.yml" })}`,
isDefault: false
}]
}])
})
Expand Down Expand Up @@ -877,7 +889,8 @@ test("It lets users specify the ID of a remote version", async () => {
specifications: [{
id: "baz",
name: "Baz",
url: `/api/remotes/${base64RemoteConfigEncoder.encode({ url: "https://example.com/baz.yml" })}`
url: `/api/remotes/${base64RemoteConfigEncoder.encode({ url: "https://example.com/baz.yml" })}`,
isDefault: false
}]
}])
})
Expand Down Expand Up @@ -920,7 +933,168 @@ test("It lets users specify the ID of a remote specification", async () => {
specifications: [{
id: "some-spec",
name: "Baz",
url: `/api/remotes/${base64RemoteConfigEncoder.encode({ url: "https://example.com/baz.yml" })}`
url: `/api/remotes/${base64RemoteConfigEncoder.encode({ url: "https://example.com/baz.yml" })}`,
isDefault: false
}]
}])
})

test("It sets isDefault on the correct specification based on defaultSpecificationName in config", async () => {
const sut = new GitHubProjectDataSource({
repositoryNameSuffix: "-openapi",
repositoryDataSource: {
async getRepositories() {
return [{
owner: "acme",
name: "foo-openapi",
defaultBranchRef: {
id: "12345678",
name: "main"
},
configYml: {
text: `
defaultSpecificationName: bar-service.yml
remoteVersions:
- name: Bar
specifications:
- id: some-spec
name: Baz
url: https://example.com/baz.yml
`
},
branches: [{
id: "12345678",
name: "main",
files: [
{ name: "foo-service.yml" },
{ name: "bar-service.yml" },
{ name: "baz-service.yml" }
]
}],
tags: []
}]
}
},
encryptionService: noopEncryptionService,
remoteConfigEncoder: base64RemoteConfigEncoder
})
const projects = await sut.getProjects()
const specs = projects[0].versions[0].specifications
expect(specs.find(s => s.name === "bar-service.yml")!.isDefault).toBe(true)
expect(specs.find(s => s.name === "foo-service.yml")!.isDefault).toBe(false)
expect(specs.find(s => s.name === "baz-service.yml")!.isDefault).toBe(false)
expect(projects[0].versions[1].specifications.find(s => s.name === "Baz")!.isDefault).toBe(false)
})

test("It sets a remote specification as the default if specified", async () => {
const sut = new GitHubProjectDataSource({
repositoryNameSuffix: "-openapi",
repositoryDataSource: {
async getRepositories() {
return [{
owner: "acme",
name: "foo-openapi",
defaultBranchRef: {
id: "12345678",
name: "main"
},
configYaml: {
text: `
defaultSpecificationName: Baz
remoteVersions:
- name: Bar
specifications:
- id: some-spec
name: Baz
url: https://example.com/baz.yml
- id: another-spec
name: Qux
url: https://example.com/qux.yml
`
},
branches: [],
tags: []
}]
}
},
encryptionService: noopEncryptionService,
remoteConfigEncoder: base64RemoteConfigEncoder
})
const projects = await sut.getProjects()
const remoteSpecs = projects[0].versions[0].specifications
expect(remoteSpecs.find(s => s.id === "some-spec")!.isDefault).toBe(true)
expect(remoteSpecs.find(s => s.id === "another-spec")!.isDefault).toBe(false)
})


test("It sets isDefault to false for all specifications if defaultSpecificationName is not set", async () => {
const sut = new GitHubProjectDataSource({
repositoryNameSuffix: "-openapi",
repositoryDataSource: {
async getRepositories() {
return [{
owner: "acme",
name: "foo-openapi",
defaultBranchRef: {
id: "12345678",
name: "main"
},
configYml: {
text: ``
},
branches: [{
id: "12345678",
name: "main",
files: [
{ name: "foo-service.yml" },
{ name: "bar-service.yml" },
{ name: "baz-service.yml" }
]
}],
tags: []
}]
}
},
encryptionService: noopEncryptionService,
remoteConfigEncoder: base64RemoteConfigEncoder
})
const projects = await sut.getProjects()
const specs = projects[0].versions[0].specifications
expect(specs.every(s => s.isDefault === false)).toBe(true)
})

test("It silently ignores defaultSpecificationName if no matching spec is found", async () => {
const sut = new GitHubProjectDataSource({
repositoryNameSuffix: "-openapi",
repositoryDataSource: {
async getRepositories() {
return [{
owner: "acme",
name: "foo-openapi",
defaultBranchRef: {
id: "12345678",
name: "main"
},
configYml: {
text: `defaultSpecificationName: non-existent.yml`
},
branches: [{
id: "12345678",
name: "main",
files: [
{ name: "foo-service.yml" },
{ name: "bar-service.yml" },
{ name: "baz-service.yml" }
]
}],
tags: []
}]
}
},
encryptionService: noopEncryptionService,
remoteConfigEncoder: base64RemoteConfigEncoder
})
const projects = await sut.getProjects()
const specs = projects[0].versions[0].specifications
expect(specs.every(s => s.isDefault === false)).toBe(true)
})
17 changes: 15 additions & 2 deletions src/features/projects/data/GitHubProjectDataSource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ export default class GitHubProjectDataSource implements IProjectDataSource {
).filter(version => {
return version.specifications.length > 0
})
.map(version => this.setDefaultSpecification(version, config?.defaultSpecificationName));
const defaultName = repository.name.replace(new RegExp(this.repositoryNameSuffix + "$"), "")
return {
id: `${repository.owner}-${defaultName}`,
Expand Down Expand Up @@ -130,7 +131,8 @@ export default class GitHubProjectDataSource implements IProjectDataSource {
path: file.name,
ref: ref.id
}),
editURL: `https://github.com/${ownerName}/${repositoryName}/edit/${ref.name}/${file.name}`
editURL: `https://github.com/${ownerName}/${repositoryName}/edit/${ref.name}/${file.name}`,
isDefault: false // initial value
}
})
return {
Expand Down Expand Up @@ -187,7 +189,8 @@ export default class GitHubProjectDataSource implements IProjectDataSource {
return {
id: this.makeURLSafeID((e.id || e.name).toLowerCase()),
name: e.name,
url: `/api/remotes/${encodedRemoteConfig}`
url: `/api/remotes/${encodedRemoteConfig}`,
isDefault: false // initial value
};
})
versions.push({
Expand Down Expand Up @@ -246,4 +249,14 @@ export default class GitHubProjectDataSource implements IProjectDataSource {
return undefined
}
}

private setDefaultSpecification(version: Version, defaultSpecificationName?: string): Version {
return {
...version,
specifications: version.specifications.map(spec => ({
...spec,
isDefault: spec.name == defaultSpecificationName
}))
}
}
}
2 changes: 1 addition & 1 deletion src/features/projects/data/useProjectSelection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export default function useProjectSelection() {
},
selectProject: (project: Project) => {
const version = project.versions[0]
const specification = version.specifications[0]
const specification = version.specifications.find(spec => spec.isDefault) || version.specifications[0]
NProgress.start()
projectNavigator.navigate(
project.owner,
Expand Down
1 change: 1 addition & 0 deletions src/features/projects/domain/IProjectConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export const ProjectConfigRemoteVersionSchema = z.object({
export const IProjectConfigSchema = z.object({
name: z.coerce.string().optional(),
image: z.string().optional(),
defaultSpecificationName: z.string().optional(),
remoteVersions: ProjectConfigRemoteVersionSchema.array().optional()
})

Expand Down
3 changes: 2 additions & 1 deletion src/features/projects/domain/OpenApiSpecification.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ export const OpenApiSpecificationSchema = z.object({
id: z.string(),
name: z.string(),
url: z.string(),
editURL: z.string().optional()
editURL: z.string().optional(),
isDefault: z.boolean()
})

type OpenApiSpecification = z.infer<typeof OpenApiSpecificationSchema>
Expand Down
4 changes: 2 additions & 2 deletions src/features/projects/domain/ProjectNavigator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ export default class ProjectNavigator {
if (candidateSpecification) {
this.router.push(`/${project.owner}/${project.name}/${newVersion.id}/${candidateSpecification.id}`)
} else {
const firstSpecification = newVersion.specifications[0]
this.router.push(`/${project.owner}/${project.name}/${newVersion.id}/${firstSpecification.id}`)
const defaultOrFirstSpecification = newVersion.specifications.find(spec => spec.isDefault) || newVersion.specifications[0]
this.router.push(`/${project.owner}/${project.name}/${newVersion.id}/${defaultOrFirstSpecification.id}`)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ export default function getProjectSelectionFromPath({
if (specificationId && !didMoveSpecificationIdToVersionId) {
specification = version.specifications.find(e => e.id == specificationId)
} else if (version.specifications.length > 0) {
specification = version.specifications[0]
specification = version.specifications.find(spec => spec.isDefault) || version.specifications[0]
}
return { project, version, specification }
}
Expand Down
Loading