-
-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: built-in package resolution (#364)
* initial implementation No unit-tests yet * rename service * add js doc * use node-fetch Easier to mock * add tests for check-url * add jsdoc * add tests for unity package check * use existing error types * add tests for CheckIsBuiltInPackage * add jsdoc * drop old internal-package logic * add deps e2e tests * fix incorrect built-in version resolution Used the wrong property * add add e2e test
- Loading branch information
1 parent
32631a1
commit 64e37d9
Showing
12 changed files
with
396 additions
and
42 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import { AsyncResult, Err, Ok, Result } from "ts-results-es"; | ||
import { GenericNetworkError } from "./common-errors"; | ||
import fetch from "node-fetch"; | ||
|
||
/** | ||
* Function for checking if an url exists. | ||
* @param url The url to check. | ||
*/ | ||
export type CheckUrlExists = ( | ||
url: string | ||
) => AsyncResult<boolean, GenericNetworkError>; | ||
|
||
/** | ||
* Makes a {@link CheckUrlExists} function. | ||
*/ | ||
export function makeCheckUrlExists(): CheckUrlExists { | ||
return (url) => { | ||
return new AsyncResult( | ||
Result.wrapAsync(() => fetch(url, { method: "HEAD" })) | ||
) | ||
.mapErr(() => new GenericNetworkError()) | ||
.andThen((reponse) => { | ||
if (reponse.status === 200) return Ok(true); | ||
else if (reponse.status === 404) return Ok(false); | ||
else return Err(new GenericNetworkError()); | ||
}); | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
import { DomainName } from "../domain/domain-name"; | ||
import { SemanticVersion } from "../domain/semantic-version"; | ||
import { AsyncResult } from "ts-results-es"; | ||
import { | ||
CheckIsUnityPackage, | ||
CheckIsUnityPackageError, | ||
} from "./unity-package-check"; | ||
import { FetchPackument } from "../io/packument-io"; | ||
import { unityRegistryUrl } from "../domain/registry-url"; | ||
import { recordKeys } from "../utils/record-utils"; | ||
import { AsyncOk } from "../utils/result-utils"; | ||
import { | ||
GenericNetworkError, | ||
RegistryAuthenticationError, | ||
} from "../io/common-errors"; | ||
import { FetchAllPackumentsError } from "../io/all-packuments-io"; | ||
|
||
/** | ||
* Error which may occur when checking whether a package is built-in. | ||
*/ | ||
export type CheckIsBuiltInPackageError = | ||
| CheckIsUnityPackageError | ||
| FetchAllPackumentsError; | ||
|
||
/** | ||
* Function for checking whether a specific package version is built-in. | ||
* @param packageName The packages name. | ||
* @param version The specific version to check. | ||
* @returns A boolean indicating whether the package version is built-in. | ||
*/ | ||
export type CheckIsBuiltInPackage = ( | ||
packageName: DomainName, | ||
version: SemanticVersion | ||
) => AsyncResult<boolean, CheckIsBuiltInPackageError>; | ||
|
||
/** | ||
* Makes a {@link CheckIsBuiltInPackage} function. | ||
*/ | ||
export function makeCheckIsBuiltInPackage( | ||
checkIsUnityPackage: CheckIsUnityPackage, | ||
fetchPackument: FetchPackument | ||
): CheckIsBuiltInPackage { | ||
function checkExistsOnUnityRegistry( | ||
packageName: DomainName, | ||
version: SemanticVersion | ||
): AsyncResult<boolean, GenericNetworkError> { | ||
return fetchPackument({ url: unityRegistryUrl, auth: null }, packageName) | ||
.map((maybePackument) => { | ||
if (maybePackument === null) return false; | ||
const versions = recordKeys(maybePackument.versions); | ||
return versions.includes(version); | ||
}) | ||
.mapErr((error) => { | ||
if (error instanceof RegistryAuthenticationError) | ||
throw new Error( | ||
"Authentication with Unity registry failed, even though it does not require authentication." | ||
); | ||
|
||
return error; | ||
}); | ||
} | ||
|
||
return (packageName, version) => { | ||
return checkIsUnityPackage(packageName).andThen((isUnityPackage) => { | ||
if (!isUnityPackage) return AsyncOk(false); | ||
return checkExistsOnUnityRegistry(packageName, version).map( | ||
(existsOnUnityRegistry) => !existsOnUnityRegistry | ||
); | ||
}); | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import { DomainName } from "../domain/domain-name"; | ||
import { AsyncResult } from "ts-results-es"; | ||
import { CheckUrlExists } from "../io/check-url"; | ||
import { GenericNetworkError } from "../io/common-errors"; | ||
|
||
/** | ||
* Error which may occur when checking whether a package is a Unity package. | ||
*/ | ||
export type CheckIsUnityPackageError = GenericNetworkError; | ||
|
||
/** | ||
* Function for checking whether a package is an official Unity package. | ||
* @param packageName The name of the package. | ||
* @returns A boolean indicating whether the package is a Unity package. | ||
*/ | ||
export type CheckIsUnityPackage = ( | ||
packageName: DomainName | ||
) => AsyncResult<boolean, CheckIsUnityPackageError>; | ||
|
||
/** | ||
* Makes a {@link CheckIsUnityPackage} function. | ||
*/ | ||
export function makeCheckIsUnityPackage( | ||
checkUrlExists: CheckUrlExists | ||
): CheckIsUnityPackage { | ||
return (packageName) => { | ||
// A package is an official Unity package if it has a documentation page | ||
const url = `https://docs.unity3d.com/Manual/${packageName}.html`; | ||
return checkUrlExists(url); | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import { prepareHomeDirectory } from "./setup/directories"; | ||
import { runOpenupm } from "./run"; | ||
import { ResultCodes } from "../../src/cli/result-codes"; | ||
|
||
describe("print package dependencies", () => { | ||
it("should print built-in dependencies", async () => { | ||
// See https://github.com/openupm/openupm-cli/issues/133 | ||
const homeDir = await prepareHomeDirectory(); | ||
|
||
const output = await runOpenupm(homeDir, [ | ||
"deps", | ||
"com.unity.polyspatial@0.7.1", | ||
]); | ||
|
||
expect(output.code).toEqual(ResultCodes.Ok); | ||
expect(output.stdErr).toEqual( | ||
expect.arrayContaining([ | ||
// Taken from https://packages.unity.com/com.unity.polyspatial | ||
expect.stringContaining("com.unity.nuget.newtonsoft-json@3.0.2"), | ||
expect.stringContaining("com.unity.render-pipelines.universal@14.0.1"), | ||
expect.stringContaining("com.unity.collections@2.1.4"), | ||
expect.stringContaining("com.unity.textmeshpro@3.0.6"), | ||
expect.stringContaining("com.unity.xr.core-utils@2.4.0-exp.3"), | ||
expect.stringContaining("com.unity.ext.flatsharp@0.10.1"), | ||
expect.stringContaining("com.unity.modules.particlesystem@1.0.0"), | ||
expect.stringContaining("com.unity.inputsystem@1.4.4"), | ||
expect.stringContaining("com.unity.modules.video@1.0.0"), | ||
expect.stringContaining("com.unity.ugui@1.0.0"), | ||
]) | ||
); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import { makeCheckUrlExists } from "../../src/io/check-url"; | ||
import fetch, { Response } from "node-fetch"; | ||
import { GenericNetworkError } from "../../src/io/common-errors"; | ||
|
||
jest.mock("node-fetch"); | ||
|
||
describe("check url exists", () => { | ||
function makeDependencies() { | ||
const checkUrlExists = makeCheckUrlExists(); | ||
return { checkUrlExists } as const; | ||
} | ||
|
||
it("should be true if url responds with 200", async () => { | ||
jest.mocked(fetch).mockResolvedValue({ | ||
status: 200, | ||
} as Response); | ||
const { checkUrlExists } = makeDependencies(); | ||
|
||
const result = await checkUrlExists("https://some.url.com").promise; | ||
|
||
expect(result).toBeOk((actual) => expect(actual).toBeTruthy()); | ||
}); | ||
|
||
it("should be false if url responds with 404", async () => { | ||
jest.mocked(fetch).mockResolvedValue({ | ||
status: 404, | ||
} as Response); | ||
const { checkUrlExists } = makeDependencies(); | ||
|
||
const result = await checkUrlExists("https://some.url.com").promise; | ||
|
||
expect(result).toBeOk((actual) => expect(actual).toBeFalsy()); | ||
}); | ||
|
||
it.each([100, 201, 301, 401, 500])( | ||
"should be fail for other status codes (%d)", | ||
async (statusCode) => { | ||
jest.mocked(fetch).mockResolvedValue({ | ||
status: statusCode, | ||
} as Response); | ||
const { checkUrlExists } = makeDependencies(); | ||
|
||
const result = await checkUrlExists("https://some.url.com").promise; | ||
|
||
expect(result).toBeError((actual) => | ||
expect(actual).toBeInstanceOf(GenericNetworkError) | ||
); | ||
} | ||
); | ||
|
||
it("should be fail if request fails", async () => { | ||
jest.mocked(fetch).mockRejectedValueOnce(new Error("Network bad")); | ||
const { checkUrlExists } = makeDependencies(); | ||
|
||
const result = await checkUrlExists("https://some.url.com").promise; | ||
|
||
expect(result).toBeError((actual) => | ||
expect(actual).toBeInstanceOf(GenericNetworkError) | ||
); | ||
}); | ||
}); |
Oops, something went wrong.