latchkey 3.2.2 → 3.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/dist/src/cliCommands.d.ts.map +1 -1
- package/dist/src/cliCommands.js +91 -7
- package/dist/src/cliCommands.js.map +1 -1
- package/dist/src/configDataStore.d.ts +18 -0
- package/dist/src/configDataStore.d.ts.map +1 -1
- package/dist/src/configDataStore.js +10 -0
- package/dist/src/configDataStore.js.map +1 -1
- package/dist/src/serviceRegistry.d.ts.map +1 -1
- package/dist/src/serviceRegistry.js +25 -2
- package/dist/src/serviceRegistry.js.map +1 -1
- package/dist/src/services/core/base.d.ts +25 -3
- package/dist/src/services/core/base.d.ts.map +1 -1
- package/dist/src/services/core/base.js +32 -10
- package/dist/src/services/core/base.js.map +1 -1
- package/dist/src/services/core/loginFlows/base.d.ts +75 -0
- package/dist/src/services/core/loginFlows/base.d.ts.map +1 -0
- package/dist/src/services/core/loginFlows/base.js +47 -0
- package/dist/src/services/core/loginFlows/base.js.map +1 -0
- package/dist/src/services/core/loginFlows/cookieCapture.d.ts +55 -0
- package/dist/src/services/core/loginFlows/cookieCapture.d.ts.map +1 -0
- package/dist/src/services/core/loginFlows/cookieCapture.js +244 -0
- package/dist/src/services/core/loginFlows/cookieCapture.js.map +1 -0
- package/dist/src/services/core/loginFlows/registry.d.ts +30 -0
- package/dist/src/services/core/loginFlows/registry.d.ts.map +1 -0
- package/dist/src/services/core/loginFlows/registry.js +56 -0
- package/dist/src/services/core/loginFlows/registry.js.map +1 -0
- package/dist/src/services/core/registered.d.ts +38 -2
- package/dist/src/services/core/registered.d.ts.map +1 -1
- package/dist/src/services/core/registered.js +37 -2
- package/dist/src/services/core/registered.js.map +1 -1
- package/dist/src/services/index.d.ts +3 -0
- package/dist/src/services/index.d.ts.map +1 -1
- package/dist/src/services/index.js +3 -0
- package/dist/src/services/index.js.map +1 -1
- package/dist/src/version.d.ts +1 -1
- package/dist/src/version.js +1 -1
- package/dist/tests/cli.test.js +178 -2
- package/dist/tests/cli.test.js.map +1 -1
- package/dist/tests/cookieCapture.test.d.ts +10 -0
- package/dist/tests/cookieCapture.test.d.ts.map +1 -0
- package/dist/tests/cookieCapture.test.js +221 -0
- package/dist/tests/cookieCapture.test.js.map +1 -0
- package/dist/tests/serviceRegistry.test.js +29 -8
- package/dist/tests/serviceRegistry.test.js.map +1 -1
- package/dist/tests/servicesAgainstRecordings.test.js +5 -9
- package/dist/tests/servicesAgainstRecordings.test.js.map +1 -1
- package/package.json +1 -1
|
@@ -3,10 +3,46 @@
|
|
|
3
3
|
* with a custom name and base API URL. Used for self-hosted instances.
|
|
4
4
|
*
|
|
5
5
|
* When no family service is provided, the registered service acts as a
|
|
6
|
-
* generic service that
|
|
6
|
+
* generic service that supports `latchkey auth set` for credentials, plus —
|
|
7
|
+
* if a login URL and a login flow were given — a browser login of its own.
|
|
7
8
|
*/
|
|
8
9
|
import { ApiCredentialStatus, type ApiCredentials } from '../../apiCredentials/base.js';
|
|
9
10
|
import { Service, type ServiceSession } from './base.js';
|
|
11
|
+
import type { LoginFlow } from './loginFlows/base.js';
|
|
12
|
+
/**
|
|
13
|
+
* Where a registered service gets its browser login, if anywhere. The two
|
|
14
|
+
* sources are mutually exclusive — a family service already brings its own
|
|
15
|
+
* login and credential shape — so they are alternatives rather than fields
|
|
16
|
+
* that happen not to be combined. Omitting the options entirely leaves the
|
|
17
|
+
* service with `latchkey auth set` only.
|
|
18
|
+
*
|
|
19
|
+
* The `never` markers are what make the exclusion stick: a plain union of the
|
|
20
|
+
* two shapes would still accept an object carrying both, because excess
|
|
21
|
+
* property checking against a union permits any property known to some member.
|
|
22
|
+
*/
|
|
23
|
+
export type RegisteredServiceOptions = {
|
|
24
|
+
/** Built-in service to use as a template, for self-hosted instances. */
|
|
25
|
+
readonly familyService: Service;
|
|
26
|
+
/** Page opened by `latchkey auth browser`, for families that support it. */
|
|
27
|
+
readonly loginUrl?: string;
|
|
28
|
+
readonly loginFlow?: never;
|
|
29
|
+
} | {
|
|
30
|
+
/** Generic browser login, already configured with its parameters. */
|
|
31
|
+
readonly loginFlow: LoginFlow;
|
|
32
|
+
/** Page the flow starts from. A flow has nowhere to begin without one. */
|
|
33
|
+
readonly loginUrl: string;
|
|
34
|
+
readonly familyService?: never;
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Build options from values that are each independently optional, as they come
|
|
38
|
+
* out of the CLI and out of config.json.
|
|
39
|
+
*
|
|
40
|
+
* This is the one place where a family service and a login flow can meet, so it
|
|
41
|
+
* is also where their precedence is decided: the family wins, since its login
|
|
42
|
+
* and credential shape are what the rest of the service is built on. Callers
|
|
43
|
+
* that construct options directly are held to the exclusion by the type.
|
|
44
|
+
*/
|
|
45
|
+
export declare function buildRegisteredServiceOptions(familyService: Service | undefined, loginUrl: string | undefined, loginFlow: LoginFlow | undefined): RegisteredServiceOptions | undefined;
|
|
10
46
|
export declare class RegisteredService extends Service {
|
|
11
47
|
readonly name: string;
|
|
12
48
|
readonly displayName: string;
|
|
@@ -15,7 +51,7 @@ export declare class RegisteredService extends Service {
|
|
|
15
51
|
readonly info: string;
|
|
16
52
|
readonly credentialCheckCurlArguments: readonly string[];
|
|
17
53
|
private readonly familyService;
|
|
18
|
-
constructor(name: string, baseApiUrl: string,
|
|
54
|
+
constructor(name: string, baseApiUrl: string, options?: RegisteredServiceOptions);
|
|
19
55
|
getSession?(appNamePrefix: string): ServiceSession;
|
|
20
56
|
checkApiCredentials(): Promise<ApiCredentialStatus>;
|
|
21
57
|
getAccount(): Promise<string | null>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registered.d.ts","sourceRoot":"","sources":["../../../../src/services/core/registered.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"registered.d.ts","sourceRoot":"","sources":["../../../../src/services/core/registered.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,mBAAmB,EAAE,KAAK,cAAc,EAAE,MAAM,8BAA8B,CAAC;AACxF,OAAO,EAAE,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,WAAW,CAAC;AACzD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAEtD;;;;;;;;;;GAUG;AACH,MAAM,MAAM,wBAAwB,GAChC;IACE,wEAAwE;IACxE,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;IAChC,4EAA4E;IAC5E,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,CAAC,EAAE,KAAK,CAAC;CAC5B,GACD;IACE,qEAAqE;IACrE,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;IAC9B,0EAA0E;IAC1E,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,aAAa,CAAC,EAAE,KAAK,CAAC;CAChC,CAAC;AAEN;;;;;;;;GAQG;AACH,wBAAgB,6BAA6B,CAC3C,aAAa,EAAE,OAAO,GAAG,SAAS,EAClC,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,SAAS,EAAE,SAAS,GAAG,SAAS,GAC/B,wBAAwB,GAAG,SAAS,CAQtC;AAED,qBAAa,iBAAkB,SAAQ,OAAO;IAC5C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;IACxC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,4BAA4B,EAAE,SAAS,MAAM,EAAE,CAAC;IAEzD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAsB;gBAExC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,wBAAwB;IAsCvE,UAAU,CAAC,CAAC,aAAa,EAAE,MAAM,GAAG,cAAc;IAElD,mBAAmB,IAAI,OAAO,CAAC,mBAAmB,CAAC;IAM5D,UAAU,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAIpC,qBAAqB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM;IAOzC,oBAAoB,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,GAAG,cAAc;CAM7E"}
|
|
@@ -3,10 +3,29 @@
|
|
|
3
3
|
* with a custom name and base API URL. Used for self-hosted instances.
|
|
4
4
|
*
|
|
5
5
|
* When no family service is provided, the registered service acts as a
|
|
6
|
-
* generic service that
|
|
6
|
+
* generic service that supports `latchkey auth set` for credentials, plus —
|
|
7
|
+
* if a login URL and a login flow were given — a browser login of its own.
|
|
7
8
|
*/
|
|
8
9
|
import { ApiCredentialStatus } from '../../apiCredentials/base.js';
|
|
9
10
|
import { Service } from './base.js';
|
|
11
|
+
/**
|
|
12
|
+
* Build options from values that are each independently optional, as they come
|
|
13
|
+
* out of the CLI and out of config.json.
|
|
14
|
+
*
|
|
15
|
+
* This is the one place where a family service and a login flow can meet, so it
|
|
16
|
+
* is also where their precedence is decided: the family wins, since its login
|
|
17
|
+
* and credential shape are what the rest of the service is built on. Callers
|
|
18
|
+
* that construct options directly are held to the exclusion by the type.
|
|
19
|
+
*/
|
|
20
|
+
export function buildRegisteredServiceOptions(familyService, loginUrl, loginFlow) {
|
|
21
|
+
if (familyService !== undefined) {
|
|
22
|
+
return { familyService, loginUrl };
|
|
23
|
+
}
|
|
24
|
+
if (loginFlow !== undefined && loginUrl !== undefined) {
|
|
25
|
+
return { loginFlow, loginUrl };
|
|
26
|
+
}
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
10
29
|
export class RegisteredService extends Service {
|
|
11
30
|
name;
|
|
12
31
|
displayName;
|
|
@@ -15,8 +34,16 @@ export class RegisteredService extends Service {
|
|
|
15
34
|
info;
|
|
16
35
|
credentialCheckCurlArguments;
|
|
17
36
|
familyService;
|
|
18
|
-
constructor(name, baseApiUrl,
|
|
37
|
+
constructor(name, baseApiUrl, options) {
|
|
19
38
|
super();
|
|
39
|
+
const familyService = options?.familyService;
|
|
40
|
+
const loginUrl = options?.loginUrl;
|
|
41
|
+
// The options type rules out a flow without a page to open, and a flow
|
|
42
|
+
// alongside a family service, for TypeScript callers. This repeats the
|
|
43
|
+
// checks because latchkey is also consumed as a library from JavaScript.
|
|
44
|
+
const usableLoginFlow = options?.loginFlow !== undefined && loginUrl !== undefined && familyService === undefined
|
|
45
|
+
? { flow: options.loginFlow, loginUrl }
|
|
46
|
+
: undefined;
|
|
20
47
|
this.name = name;
|
|
21
48
|
this.displayName = name;
|
|
22
49
|
this.baseApiUrls = [baseApiUrl];
|
|
@@ -26,6 +53,11 @@ export class RegisteredService extends Service {
|
|
|
26
53
|
if (familyService !== undefined) {
|
|
27
54
|
this.info = `Self-hosted ${familyService.displayName} instance. ${familyService.info}`;
|
|
28
55
|
}
|
|
56
|
+
else if (usableLoginFlow !== undefined) {
|
|
57
|
+
this.info =
|
|
58
|
+
`Generic service. ${usableLoginFlow.flow.describe(usableLoginFlow.loginUrl)} ` +
|
|
59
|
+
'Alternatively, use `latchkey auth set` to supply credentials as curl arguments.';
|
|
60
|
+
}
|
|
29
61
|
else {
|
|
30
62
|
this.info =
|
|
31
63
|
'Generic service. Use `latchkey auth set` to supply credentials as curl arguments.';
|
|
@@ -33,6 +65,9 @@ export class RegisteredService extends Service {
|
|
|
33
65
|
if (loginUrl !== undefined && familyService?.getSession !== undefined) {
|
|
34
66
|
this.getSession = (appNamePrefix) => familyService.getSession(appNamePrefix);
|
|
35
67
|
}
|
|
68
|
+
else if (usableLoginFlow !== undefined) {
|
|
69
|
+
this.getSession = (appNamePrefix) => usableLoginFlow.flow.createSession(this, appNamePrefix);
|
|
70
|
+
}
|
|
36
71
|
}
|
|
37
72
|
checkApiCredentials() {
|
|
38
73
|
return Promise.resolve(ApiCredentialStatus.Unknown);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registered.js","sourceRoot":"","sources":["../../../../src/services/core/registered.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"registered.js","sourceRoot":"","sources":["../../../../src/services/core/registered.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,6 +4,9 @@
|
|
|
4
4
|
export { Service, ServiceSession, SimpleServiceSession, BrowserFollowupServiceSession, FollowupWork, buildFollowupSpinnerDetails, type LoginResult, } from './core/base.js';
|
|
5
5
|
export { LoginCancelledError, LoginFailedError, NoCurlCredentialsNotSupportedError, PrepareNotSupportedError, PrepareInputInvalidError, } from './core/base.js';
|
|
6
6
|
export { RegisteredService } from './core/registered.js';
|
|
7
|
+
export { LOGIN_FLOWS, UnknownLoginFlowError, formatLoginFlowsHelp, resolveLoginFlow, } from './core/loginFlows/registry.js';
|
|
8
|
+
export { LoginFlowParamsInvalidError, parseLoginFlowParams, type LoginFlow, type LoginFlowClass, } from './core/loginFlows/base.js';
|
|
9
|
+
export { CookieCaptureLoginFlow } from './core/loginFlows/cookieCapture.js';
|
|
7
10
|
export { Slack, SLACK } from './slack.js';
|
|
8
11
|
export { Discord, DISCORD } from './discord.js';
|
|
9
12
|
export { Github, GITHUB } from './github.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/services/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACL,OAAO,EACP,cAAc,EACd,oBAAoB,EACpB,6BAA6B,EAC7B,YAAY,EACZ,2BAA2B,EAC3B,KAAK,WAAW,GACjB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,mBAAmB,EACnB,gBAAgB,EAChB,kCAAkC,EAClC,wBAAwB,EACxB,wBAAwB,GACzB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/services/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,6 +4,9 @@
|
|
|
4
4
|
export { Service, ServiceSession, SimpleServiceSession, BrowserFollowupServiceSession, FollowupWork, buildFollowupSpinnerDetails, } from './core/base.js';
|
|
5
5
|
export { LoginCancelledError, LoginFailedError, NoCurlCredentialsNotSupportedError, PrepareNotSupportedError, PrepareInputInvalidError, } from './core/base.js';
|
|
6
6
|
export { RegisteredService } from './core/registered.js';
|
|
7
|
+
export { LOGIN_FLOWS, UnknownLoginFlowError, formatLoginFlowsHelp, resolveLoginFlow, } from './core/loginFlows/registry.js';
|
|
8
|
+
export { LoginFlowParamsInvalidError, parseLoginFlowParams, } from './core/loginFlows/base.js';
|
|
9
|
+
export { CookieCaptureLoginFlow } from './core/loginFlows/cookieCapture.js';
|
|
7
10
|
export { Slack, SLACK } from './slack.js';
|
|
8
11
|
export { Discord, DISCORD } from './discord.js';
|
|
9
12
|
export { Github, GITHUB } from './github.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/services/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACL,OAAO,EACP,cAAc,EACd,oBAAoB,EACpB,6BAA6B,EAC7B,YAAY,EACZ,2BAA2B,GAE5B,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,mBAAmB,EACnB,gBAAgB,EAChB,kCAAkC,EAClC,wBAAwB,EACxB,wBAAwB,GACzB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/services/index.ts"],"names":[],"mappings":"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"}
|
package/dist/src/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const VERSION = "3.
|
|
1
|
+
export declare const VERSION = "3.3.0";
|
|
2
2
|
//# sourceMappingURL=version.d.ts.map
|
package/dist/src/version.js
CHANGED
package/dist/tests/cli.test.js
CHANGED
|
@@ -1742,7 +1742,7 @@ describe('CLI commands with dependency injection', () => {
|
|
|
1742
1742
|
const entries = loadRegisteredServices(deps.config.configPath);
|
|
1743
1743
|
expect(entries.get('my-github')?.loginUrl).toBe('https://github.mycompany.com/login');
|
|
1744
1744
|
});
|
|
1745
|
-
it('should reject --login-url without --service-family', async () => {
|
|
1745
|
+
it('should reject --login-url without --service-family or --login-flow', async () => {
|
|
1746
1746
|
const deps = createMockDependencies({
|
|
1747
1747
|
registry: new ServiceRegistry([]),
|
|
1748
1748
|
});
|
|
@@ -1756,7 +1756,154 @@ describe('CLI commands with dependency injection', () => {
|
|
|
1756
1756
|
'https://example.com/login',
|
|
1757
1757
|
], deps);
|
|
1758
1758
|
expect(exitCode).toBe(1);
|
|
1759
|
-
expect(errorLogs[0]).toContain('--login-url requires a --service-family');
|
|
1759
|
+
expect(errorLogs[0]).toContain('--login-url requires either a --service-family');
|
|
1760
|
+
});
|
|
1761
|
+
it('should register a service with the cookie-capture login flow', async () => {
|
|
1762
|
+
const deps = createMockDependencies({
|
|
1763
|
+
registry: new ServiceRegistry([]),
|
|
1764
|
+
});
|
|
1765
|
+
await runCommand([
|
|
1766
|
+
'services',
|
|
1767
|
+
'register',
|
|
1768
|
+
'my-service',
|
|
1769
|
+
'--base-api-url',
|
|
1770
|
+
'https://example.com/api/',
|
|
1771
|
+
'--login-url',
|
|
1772
|
+
'https://example.com/login',
|
|
1773
|
+
'--login-flow',
|
|
1774
|
+
'cookie-capture',
|
|
1775
|
+
'--login-flow-params',
|
|
1776
|
+
'{"cookieKeys": ["sessionid", "csrftoken"]}',
|
|
1777
|
+
], deps);
|
|
1778
|
+
expect(exitCode).toBeNull();
|
|
1779
|
+
expect(logs).toContain("Service 'my-service' registered.");
|
|
1780
|
+
expect(loadRegisteredServices(deps.config.configPath).get('my-service')).toEqual({
|
|
1781
|
+
baseApiUrl: 'https://example.com/api/',
|
|
1782
|
+
loginUrl: 'https://example.com/login',
|
|
1783
|
+
loginFlow: {
|
|
1784
|
+
name: 'cookie-capture',
|
|
1785
|
+
params: { cookieKeys: ['sessionid', 'csrftoken'] },
|
|
1786
|
+
},
|
|
1787
|
+
});
|
|
1788
|
+
logs = [];
|
|
1789
|
+
await runCommand(['services', 'info', 'my-service'], deps);
|
|
1790
|
+
const info = JSON.parse(logs[0] ?? '');
|
|
1791
|
+
expect(info.authOptions).toEqual(['browser', 'set']);
|
|
1792
|
+
});
|
|
1793
|
+
it('should reject an unknown login flow and list the available ones', async () => {
|
|
1794
|
+
const deps = createMockDependencies({
|
|
1795
|
+
registry: new ServiceRegistry([]),
|
|
1796
|
+
});
|
|
1797
|
+
await runCommand([
|
|
1798
|
+
'services',
|
|
1799
|
+
'register',
|
|
1800
|
+
'my-service',
|
|
1801
|
+
'--base-api-url',
|
|
1802
|
+
'https://example.com/api/',
|
|
1803
|
+
'--login-url',
|
|
1804
|
+
'https://example.com/login',
|
|
1805
|
+
'--login-flow',
|
|
1806
|
+
'nonexistent',
|
|
1807
|
+
], deps);
|
|
1808
|
+
expect(exitCode).toBe(1);
|
|
1809
|
+
expect(errorLogs[0]).toContain("Unknown login flow 'nonexistent'");
|
|
1810
|
+
expect(errorLogs.join('\n')).toContain('cookie-capture');
|
|
1811
|
+
});
|
|
1812
|
+
it('should reject login flow parameters that are not valid JSON', async () => {
|
|
1813
|
+
const deps = createMockDependencies({
|
|
1814
|
+
registry: new ServiceRegistry([]),
|
|
1815
|
+
});
|
|
1816
|
+
await runCommand([
|
|
1817
|
+
'services',
|
|
1818
|
+
'register',
|
|
1819
|
+
'my-service',
|
|
1820
|
+
'--base-api-url',
|
|
1821
|
+
'https://example.com/api/',
|
|
1822
|
+
'--login-url',
|
|
1823
|
+
'https://example.com/login',
|
|
1824
|
+
'--login-flow',
|
|
1825
|
+
'cookie-capture',
|
|
1826
|
+
'--login-flow-params',
|
|
1827
|
+
'{not json}',
|
|
1828
|
+
], deps);
|
|
1829
|
+
expect(exitCode).toBe(1);
|
|
1830
|
+
expect(errorLogs[0]).toContain('--login-flow-params must be valid JSON');
|
|
1831
|
+
});
|
|
1832
|
+
it('should reject login flow parameters that do not match the flow schema', async () => {
|
|
1833
|
+
const deps = createMockDependencies({
|
|
1834
|
+
registry: new ServiceRegistry([]),
|
|
1835
|
+
});
|
|
1836
|
+
await runCommand([
|
|
1837
|
+
'services',
|
|
1838
|
+
'register',
|
|
1839
|
+
'my-service',
|
|
1840
|
+
'--base-api-url',
|
|
1841
|
+
'https://example.com/api/',
|
|
1842
|
+
'--login-url',
|
|
1843
|
+
'https://example.com/login',
|
|
1844
|
+
'--login-flow',
|
|
1845
|
+
'cookie-capture',
|
|
1846
|
+
'--login-flow-params',
|
|
1847
|
+
'{"cookieKeys": []}',
|
|
1848
|
+
], deps);
|
|
1849
|
+
expect(exitCode).toBe(1);
|
|
1850
|
+
expect(errorLogs[0]).toContain("Invalid parameters for login flow 'cookie-capture'");
|
|
1851
|
+
});
|
|
1852
|
+
it('should reject --login-flow-params without --login-flow', async () => {
|
|
1853
|
+
const deps = createMockDependencies({
|
|
1854
|
+
registry: new ServiceRegistry([]),
|
|
1855
|
+
});
|
|
1856
|
+
await runCommand([
|
|
1857
|
+
'services',
|
|
1858
|
+
'register',
|
|
1859
|
+
'my-service',
|
|
1860
|
+
'--base-api-url',
|
|
1861
|
+
'https://example.com/api/',
|
|
1862
|
+
'--login-url',
|
|
1863
|
+
'https://example.com/login',
|
|
1864
|
+
'--login-flow-params',
|
|
1865
|
+
'{"cookieKeys": ["sessionid"]}',
|
|
1866
|
+
], deps);
|
|
1867
|
+
expect(exitCode).toBe(1);
|
|
1868
|
+
expect(errorLogs[0]).toContain('--login-flow-params requires --login-flow');
|
|
1869
|
+
});
|
|
1870
|
+
it('should reject --login-flow without --login-url', async () => {
|
|
1871
|
+
const deps = createMockDependencies({
|
|
1872
|
+
registry: new ServiceRegistry([]),
|
|
1873
|
+
});
|
|
1874
|
+
await runCommand([
|
|
1875
|
+
'services',
|
|
1876
|
+
'register',
|
|
1877
|
+
'my-service',
|
|
1878
|
+
'--base-api-url',
|
|
1879
|
+
'https://example.com/api/',
|
|
1880
|
+
'--login-flow',
|
|
1881
|
+
'cookie-capture',
|
|
1882
|
+
'--login-flow-params',
|
|
1883
|
+
'{"cookieKeys": ["sessionid"]}',
|
|
1884
|
+
], deps);
|
|
1885
|
+
expect(exitCode).toBe(1);
|
|
1886
|
+
expect(errorLogs[0]).toContain('--login-flow requires --login-url');
|
|
1887
|
+
});
|
|
1888
|
+
it('should reject --login-flow combined with --service-family', async () => {
|
|
1889
|
+
const deps = createMockDependencies({
|
|
1890
|
+
registry: new ServiceRegistry([GITLAB]),
|
|
1891
|
+
});
|
|
1892
|
+
await runCommand([
|
|
1893
|
+
'services',
|
|
1894
|
+
'register',
|
|
1895
|
+
'my-gitlab',
|
|
1896
|
+
'--base-api-url',
|
|
1897
|
+
'https://gitlab.mycompany.com/api/',
|
|
1898
|
+
'--service-family',
|
|
1899
|
+
'gitlab',
|
|
1900
|
+
'--login-url',
|
|
1901
|
+
'https://gitlab.mycompany.com/users/sign_in',
|
|
1902
|
+
'--login-flow',
|
|
1903
|
+
'cookie-capture',
|
|
1904
|
+
], deps);
|
|
1905
|
+
expect(exitCode).toBe(1);
|
|
1906
|
+
expect(errorLogs[0]).toContain('--login-flow cannot be combined with --service-family');
|
|
1760
1907
|
});
|
|
1761
1908
|
it('should reject --login-url when service family does not support browser login', async () => {
|
|
1762
1909
|
const deps = createMockDependencies({
|
|
@@ -2413,6 +2560,35 @@ describe('registeredServiceStore', () => {
|
|
|
2413
2560
|
expect(service.getSession).toBeUndefined(); // eslint-disable-line @typescript-eslint/unbound-method
|
|
2414
2561
|
expect(service.loginUrl).toBe('');
|
|
2415
2562
|
});
|
|
2563
|
+
it('should load registered service with a login flow into registry', () => {
|
|
2564
|
+
const configPath = join(tempDir, 'config.json');
|
|
2565
|
+
saveRegisteredService(configPath, 'my-api', {
|
|
2566
|
+
baseApiUrl: 'https://api.example.com/',
|
|
2567
|
+
loginUrl: 'https://example.com/login',
|
|
2568
|
+
loginFlow: { name: 'cookie-capture', params: { cookieKeys: ['session_id'] } },
|
|
2569
|
+
});
|
|
2570
|
+
const registry = new ServiceRegistry([GITLAB]);
|
|
2571
|
+
loadRegisteredServicesIntoServiceRegistry(configPath, registry);
|
|
2572
|
+
const service = registry.getByName('my-api');
|
|
2573
|
+
expect(service).not.toBeNull();
|
|
2574
|
+
expect(service.getSession).toBeDefined(); // eslint-disable-line @typescript-eslint/unbound-method
|
|
2575
|
+
expect(service.loginUrl).toBe('https://example.com/login');
|
|
2576
|
+
});
|
|
2577
|
+
it('should keep a service whose stored login flow no longer resolves', () => {
|
|
2578
|
+
const configPath = join(tempDir, 'config.json');
|
|
2579
|
+
saveRegisteredService(configPath, 'my-api', {
|
|
2580
|
+
baseApiUrl: 'https://api.example.com/',
|
|
2581
|
+
loginUrl: 'https://example.com/login',
|
|
2582
|
+
// Written by a newer Latchkey, or hand-edited: the service stays usable
|
|
2583
|
+
// with `auth set`, it just loses the browser login.
|
|
2584
|
+
loginFlow: { name: 'flow-from-the-future', params: { anything: true } },
|
|
2585
|
+
});
|
|
2586
|
+
const registry = new ServiceRegistry([GITLAB]);
|
|
2587
|
+
loadRegisteredServicesIntoServiceRegistry(configPath, registry);
|
|
2588
|
+
const service = registry.getByName('my-api');
|
|
2589
|
+
expect(service).not.toBeNull();
|
|
2590
|
+
expect(service.getSession).toBeUndefined(); // eslint-disable-line @typescript-eslint/unbound-method
|
|
2591
|
+
});
|
|
2416
2592
|
it('should skip registered services with unknown family', () => {
|
|
2417
2593
|
const configPath = join(tempDir, 'config.json');
|
|
2418
2594
|
saveRegisteredService(configPath, 'my-unknown', {
|