Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ POSTGRESQL_USER=dbuser
POSTGRESQL_PASSWORD=
POSTGRESQL_DB=shape-docs
REPOSITORY_NAME_SUFFIX=-openapi
HIDDEN_REPOSITORIES=
GITHUB_WEBHOOK_SECRET=preshared secret also put in app configuration in GitHub
GITHUB_WEBHOK_REPOSITORY_ALLOWLIST=
GITHUB_WEBHOK_REPOSITORY_DISALLOWLIST=
Expand Down
125 changes: 125 additions & 0 deletions __test__/projects/FilteringGitHubRepositoryDataSource.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import { FilteringGitHubRepositoryDataSource } from "../../src/features/projects/domain"

test("It returns all repositories when no hidden repositories are provided", async () => {
const sut = new FilteringGitHubRepositoryDataSource({
hiddenRepositories: [],
dataSource: {
async getRepositories() {
return [{
owner: "acme",
name: "foo-openapi",
defaultBranchRef: {
id: "12345678",
name: "main"
},
branches: [],
tags: []
}, {
owner: "acme",
name: "bar-openapi",
defaultBranchRef: {
id: "12345678",
name: "bar"
},
branches: [],
tags: []
}]
}
}
})
const repositories = await sut.getRepositories()
expect(repositories.length).toEqual(2)
})

test("It removes hidden repository", async () => {
const sut = new FilteringGitHubRepositoryDataSource({
hiddenRepositories: ["acme/foo-openapi"],
dataSource: {
async getRepositories() {
return [{
owner: "acme",
name: "foo-openapi",
defaultBranchRef: {
id: "12345678",
name: "main"
},
branches: [],
tags: []
}, {
owner: "acme",
name: "bar-openapi",
defaultBranchRef: {
id: "12345678",
name: "bar"
},
branches: [],
tags: []
}]
}
}
})
const repositories = await sut.getRepositories()
expect(repositories.length).toEqual(1)
})

test("It returns unmodified list when hidden repository was not found", async () => {
const sut = new FilteringGitHubRepositoryDataSource({
hiddenRepositories: ["acme/baz-openapi"],
dataSource: {
async getRepositories() {
return [{
owner: "acme",
name: "foo-openapi",
defaultBranchRef: {
id: "12345678",
name: "main"
},
branches: [],
tags: []
}, {
owner: "acme",
name: "bar-openapi",
defaultBranchRef: {
id: "12345678",
name: "bar"
},
branches: [],
tags: []
}]
}
}
})
const repositories = await sut.getRepositories()
expect(repositories.length).toEqual(2)
})

test("It removes multiple hidden repositories", async () => {
const sut = new FilteringGitHubRepositoryDataSource({
hiddenRepositories: ["acme/foo-openapi", "acme/bar-openapi"],
dataSource: {
async getRepositories() {
return [{
owner: "acme",
name: "foo-openapi",
defaultBranchRef: {
id: "12345678",
name: "main"
},
branches: [],
tags: []
}, {
owner: "acme",
name: "bar-openapi",
defaultBranchRef: {
id: "12345678",
name: "bar"
},
branches: [],
tags: []
}]
}
}
})
const repositories = await sut.getRepositories()
expect(repositories.length).toEqual(0)
})
26 changes: 26 additions & 0 deletions __test__/utils/splitOwnerAndRepository.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { splitOwnerAndRepository } from "@/common"

test("It returns undefined when string includes no slash", async () => {
const result = splitOwnerAndRepository("foo")
expect(result).toBeUndefined()
})

test("It returns undefined when repository is empty", async () => {
const result = splitOwnerAndRepository("foo/")
expect(result).toBeUndefined()
})

test("It returns undefined when owner is empty", async () => {
const result = splitOwnerAndRepository("/foo")
expect(result).toBeUndefined()
})

test("It splits owner and repository", async () => {
const result = splitOwnerAndRepository("acme/foo")
expect(result).toEqual({ owner: "acme", repository: "foo" })
})

test("It splits owner and repository for repository name containing a slash", async () => {
const result = splitOwnerAndRepository("acme/foo/bar")
expect(result).toEqual({ owner: "acme", repository: "foo/bar" })
})
1 change: 1 addition & 0 deletions src/common/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ export { default as env } from "./env"
export { default as getBaseFilename } from "./getBaseFilename"
export { default as isMac } from "./isMac"
export { default as useKeyboardShortcut } from "./useKeyboardShortcut"
export { default as splitOwnerAndRepository } from "./splitOwnerAndRepository"
16 changes: 16 additions & 0 deletions src/common/utils/splitOwnerAndRepository.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Split full repository names into owner and repository.
// shapehq/foo becomes { owner: "shapehq", "repository": "foo" }
const splitOwnerAndRepository = (str: string) => {
const index = str.indexOf("/")
if (index === -1) {
return undefined
}
const owner = str.substring(0, index)
const repository = str.substring(index + 1)
if (owner.length == 0 || repository.length == 0) {
return undefined
}
return { owner, repository }
}

export default splitOwnerAndRepository
18 changes: 11 additions & 7 deletions src/composition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
} from "@/features/projects/data"
import {
CachingProjectDataSource,
FilteringGitHubRepositoryDataSource,
ProjectRepository
} from "@/features/projects/domain"
import {
Expand Down Expand Up @@ -158,13 +159,16 @@ export const projectRepository = new ProjectRepository({

export const projectDataSource = new CachingProjectDataSource({
dataSource: new GitHubProjectDataSource({
repositoryDataSource: new GitHubRepositoryDataSource({
loginsDataSource: new GitHubLoginDataSource({
graphQlClient: userGitHubClient
}),
graphQlClient: userGitHubClient,
repositoryNameSuffix: env.getOrThrow("REPOSITORY_NAME_SUFFIX"),
projectConfigurationFilename: env.getOrThrow("SHAPE_DOCS_PROJECT_CONFIGURATION_FILENAME")
repositoryDataSource: new FilteringGitHubRepositoryDataSource({
hiddenRepositories: listFromCommaSeparatedString(env.get("HIDDEN_REPOSITORIES")),
dataSource: new GitHubRepositoryDataSource({
loginsDataSource: new GitHubLoginDataSource({
graphQlClient: userGitHubClient
}),
graphQlClient: userGitHubClient,
repositoryNameSuffix: env.getOrThrow("REPOSITORY_NAME_SUFFIX"),
projectConfigurationFilename: env.getOrThrow("SHAPE_DOCS_PROJECT_CONFIGURATION_FILENAME")
})
}),
repositoryNameSuffix: env.getOrThrow("REPOSITORY_NAME_SUFFIX")
}),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import IGitHubRepositoryDataSource, {
GitHubRepository
} from "./IGitHubRepositoryDataSource"
import { splitOwnerAndRepository } from "@/common"

export default class FilteringGitHubRepositoryDataSource implements IGitHubRepositoryDataSource {
private readonly dataSource: IGitHubRepositoryDataSource
private readonly rawHiddenRepositories: string[]

constructor(config: {
dataSource: IGitHubRepositoryDataSource,
hiddenRepositories: string[]
}) {
this.dataSource = config.dataSource
this.rawHiddenRepositories = config.hiddenRepositories
}

async getRepositories(): Promise<GitHubRepository[]> {
const repositories = await this.dataSource.getRepositories()
const hiddenRepositories = this.rawHiddenRepositories
.map(splitOwnerAndRepository)
.filter(e => e !== undefined)
return repositories.filter(repository => {
const hiddenMatch = hiddenRepositories.find(e =>
e.owner == repository.owner && e.repository == repository.name
)
return hiddenMatch === undefined
})
}
}
1 change: 1 addition & 0 deletions src/features/projects/domain/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export { default as CachingProjectDataSource } from "./CachingProjectDataSource"
export { default as FilteringGitHubRepositoryDataSource } from "./FilteringGitHubRepositoryDataSource"
export { default as getSelection } from "./getSelection"
export type { default as IGitHubLoginDataSource } from "./IGitHubLoginDataSource"
export type { default as IGitHubRepositoryDataSource } from "./IGitHubRepositoryDataSource"
Expand Down