mcp4openapi 0.2.8 → 0.3.1
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 +143 -63
- package/dist/scripts/validate-profile.js +3 -3
- package/dist/scripts/validate-profile.js.map +1 -1
- package/dist/src/argument-normalizer.d.ts +5 -0
- package/dist/src/argument-normalizer.d.ts.map +1 -0
- package/dist/src/argument-normalizer.js +61 -0
- package/dist/src/argument-normalizer.js.map +1 -0
- package/dist/src/auth/oauth-provider.d.ts +131 -0
- package/dist/src/auth/oauth-provider.d.ts.map +1 -0
- package/dist/src/auth/oauth-provider.js +839 -0
- package/dist/src/auth/oauth-provider.js.map +1 -0
- package/dist/src/cli-config.d.ts +9 -0
- package/dist/src/cli-config.d.ts.map +1 -0
- package/dist/src/cli-config.js +111 -0
- package/dist/src/cli-config.js.map +1 -0
- package/dist/src/core/cli-config.d.ts +9 -0
- package/dist/src/core/cli-config.d.ts.map +1 -0
- package/dist/src/core/cli-config.js +125 -0
- package/dist/src/core/cli-config.js.map +1 -0
- package/dist/src/core/constants.d.ts +86 -0
- package/dist/src/core/constants.d.ts.map +1 -0
- package/dist/src/core/constants.js +86 -0
- package/dist/src/core/constants.js.map +1 -0
- package/dist/src/core/errors.d.ts +59 -0
- package/dist/src/core/errors.d.ts.map +1 -0
- package/dist/src/core/errors.js +119 -0
- package/dist/src/core/errors.js.map +1 -0
- package/dist/src/core/filtering.d.ts +19 -0
- package/dist/src/core/filtering.d.ts.map +1 -0
- package/dist/src/core/filtering.js +292 -0
- package/dist/src/core/filtering.js.map +1 -0
- package/dist/src/core/index.d.ts +26 -0
- package/dist/src/core/index.d.ts.map +1 -0
- package/dist/src/core/index.js +276 -0
- package/dist/src/core/index.js.map +1 -0
- package/dist/src/core/lib.d.ts +8 -0
- package/dist/src/core/lib.d.ts.map +1 -0
- package/dist/src/core/lib.js +7 -0
- package/dist/src/core/lib.js.map +1 -0
- package/dist/src/core/logger.d.ts +59 -0
- package/dist/src/core/logger.d.ts.map +1 -0
- package/dist/src/core/logger.js +197 -0
- package/dist/src/core/logger.js.map +1 -0
- package/dist/src/core/metrics.d.ts +97 -0
- package/dist/src/core/metrics.d.ts.map +1 -0
- package/dist/src/core/metrics.js +273 -0
- package/dist/src/core/metrics.js.map +1 -0
- package/dist/src/core/naming-warnings.d.ts +23 -0
- package/dist/src/core/naming-warnings.d.ts.map +1 -0
- package/dist/src/core/naming-warnings.js +83 -0
- package/dist/src/core/naming-warnings.js.map +1 -0
- package/dist/src/core/naming.d.ts +58 -0
- package/dist/src/core/naming.d.ts.map +1 -0
- package/dist/src/core/naming.js +510 -0
- package/dist/src/core/naming.js.map +1 -0
- package/dist/src/errors.d.ts +6 -0
- package/dist/src/errors.d.ts.map +1 -1
- package/dist/src/errors.js +15 -6
- package/dist/src/errors.js.map +1 -1
- package/dist/src/filtering.d.ts +19 -0
- package/dist/src/filtering.d.ts.map +1 -0
- package/dist/src/filtering.js +292 -0
- package/dist/src/filtering.js.map +1 -0
- package/dist/src/generated-schemas.d.ts +290 -79
- package/dist/src/generated-schemas.d.ts.map +1 -1
- package/dist/src/generated-schemas.js +17 -2
- package/dist/src/generated-schemas.js.map +1 -1
- package/dist/src/http-transport-config.d.ts +6 -0
- package/dist/src/http-transport-config.d.ts.map +1 -0
- package/dist/src/http-transport-config.js +47 -0
- package/dist/src/http-transport-config.js.map +1 -0
- package/dist/src/http-transport.d.ts +63 -13
- package/dist/src/http-transport.d.ts.map +1 -1
- package/dist/src/http-transport.js +1045 -482
- package/dist/src/http-transport.js.map +1 -1
- package/dist/src/index.d.ts +1 -6
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +1 -170
- package/dist/src/index.js.map +1 -1
- package/dist/src/interceptors.d.ts +1 -0
- package/dist/src/interceptors.d.ts.map +1 -1
- package/dist/src/interceptors.js +73 -63
- package/dist/src/interceptors.js.map +1 -1
- package/dist/src/lib.d.ts +1 -7
- package/dist/src/lib.d.ts.map +1 -1
- package/dist/src/lib.js +1 -6
- package/dist/src/lib.js.map +1 -1
- package/dist/src/logger.d.ts +5 -0
- package/dist/src/logger.d.ts.map +1 -1
- package/dist/src/logger.js +9 -1
- package/dist/src/logger.js.map +1 -1
- package/dist/src/mcp/mcp-server-manager.d.ts +20 -0
- package/dist/src/mcp/mcp-server-manager.d.ts.map +1 -0
- package/dist/src/mcp/mcp-server-manager.js +38 -0
- package/dist/src/mcp/mcp-server-manager.js.map +1 -0
- package/dist/src/mcp/mcp-server.d.ts +205 -0
- package/dist/src/mcp/mcp-server.d.ts.map +1 -0
- package/dist/src/mcp/mcp-server.js +1473 -0
- package/dist/src/mcp/mcp-server.js.map +1 -0
- package/dist/src/mcp-server-manager.d.ts +20 -0
- package/dist/src/mcp-server-manager.d.ts.map +1 -0
- package/dist/src/mcp-server-manager.js +38 -0
- package/dist/src/mcp-server-manager.js.map +1 -0
- package/dist/src/mcp-server.d.ts +28 -0
- package/dist/src/mcp-server.d.ts.map +1 -1
- package/dist/src/mcp-server.js +406 -109
- package/dist/src/mcp-server.js.map +1 -1
- package/dist/src/metrics.d.ts +11 -0
- package/dist/src/metrics.d.ts.map +1 -1
- package/dist/src/metrics.js +61 -0
- package/dist/src/metrics.js.map +1 -1
- package/dist/src/oauth-provider.d.ts +5 -0
- package/dist/src/oauth-provider.d.ts.map +1 -1
- package/dist/src/oauth-provider.js +29 -1
- package/dist/src/oauth-provider.js.map +1 -1
- package/dist/src/openapi/openapi-parser.d.ts +70 -0
- package/dist/src/openapi/openapi-parser.d.ts.map +1 -0
- package/dist/src/openapi/openapi-parser.js +458 -0
- package/dist/src/openapi/openapi-parser.js.map +1 -0
- package/dist/src/profile/profile-loader.d.ts +78 -0
- package/dist/src/profile/profile-loader.d.ts.map +1 -0
- package/dist/src/profile/profile-loader.js +490 -0
- package/dist/src/profile/profile-loader.js.map +1 -0
- package/dist/src/profile/profile-registry.d.ts +19 -0
- package/dist/src/profile/profile-registry.d.ts.map +1 -0
- package/dist/src/profile/profile-registry.js +43 -0
- package/dist/src/profile/profile-registry.js.map +1 -0
- package/dist/src/profile/profile-resolver.d.ts +41 -0
- package/dist/src/profile/profile-resolver.d.ts.map +1 -0
- package/dist/src/profile/profile-resolver.js +324 -0
- package/dist/src/profile/profile-resolver.js.map +1 -0
- package/dist/src/profile/startup-profile.d.ts +17 -0
- package/dist/src/profile/startup-profile.d.ts.map +1 -0
- package/dist/src/profile/startup-profile.js +30 -0
- package/dist/src/profile/startup-profile.js.map +1 -0
- package/dist/src/profile/startup-validation.d.ts +11 -0
- package/dist/src/profile/startup-validation.d.ts.map +1 -0
- package/dist/src/profile/startup-validation.js +21 -0
- package/dist/src/profile/startup-validation.js.map +1 -0
- package/dist/src/profile-loader.d.ts +1 -0
- package/dist/src/profile-loader.d.ts.map +1 -1
- package/dist/src/profile-loader.js +14 -3
- package/dist/src/profile-loader.js.map +1 -1
- package/dist/src/profile-registry.d.ts +18 -0
- package/dist/src/profile-registry.d.ts.map +1 -0
- package/dist/src/profile-registry.js +26 -0
- package/dist/src/profile-registry.js.map +1 -0
- package/dist/src/profile-resolver.d.ts +19 -0
- package/dist/src/profile-resolver.d.ts.map +1 -0
- package/dist/src/profile-resolver.js +167 -0
- package/dist/src/profile-resolver.js.map +1 -0
- package/dist/src/proxy-executor.d.ts.map +1 -1
- package/dist/src/proxy-executor.js +7 -0
- package/dist/src/proxy-executor.js.map +1 -1
- package/dist/src/startup-profile.d.ts +17 -0
- package/dist/src/startup-profile.d.ts.map +1 -0
- package/dist/src/startup-profile.js +30 -0
- package/dist/src/startup-profile.js.map +1 -0
- package/dist/src/startup-validation.d.ts +11 -0
- package/dist/src/startup-validation.d.ts.map +1 -0
- package/dist/src/startup-validation.js +21 -0
- package/dist/src/startup-validation.js.map +1 -0
- package/dist/src/testing/dynamic-mock-server.d.ts +24 -0
- package/dist/src/testing/dynamic-mock-server.d.ts.map +1 -0
- package/dist/src/testing/dynamic-mock-server.js +138 -0
- package/dist/src/testing/dynamic-mock-server.js.map +1 -0
- package/dist/src/testing/request-assertions.d.ts +5 -0
- package/dist/src/testing/request-assertions.d.ts.map +1 -0
- package/dist/src/testing/request-assertions.js +165 -0
- package/dist/src/testing/request-assertions.js.map +1 -0
- package/dist/src/testing/template-utils.d.ts +10 -0
- package/dist/src/testing/template-utils.d.ts.map +1 -0
- package/dist/src/testing/template-utils.js +72 -0
- package/dist/src/testing/template-utils.js.map +1 -0
- package/dist/src/testing/test-http-utils.d.ts +1 -1
- package/dist/src/testing/test-http-utils.d.ts.map +1 -1
- package/dist/src/testing/test-http-utils.js +1 -1
- package/dist/src/testing/test-http-utils.js.map +1 -1
- package/dist/src/testing/test-loader.d.ts +6 -0
- package/dist/src/testing/test-loader.d.ts.map +1 -0
- package/dist/src/testing/test-loader.js +212 -0
- package/dist/src/testing/test-loader.js.map +1 -0
- package/dist/src/testing/test-schema.d.ts +1270 -0
- package/dist/src/testing/test-schema.d.ts.map +1 -0
- package/dist/src/testing/test-schema.js +76 -0
- package/dist/src/testing/test-schema.js.map +1 -0
- package/dist/src/tool-filter/compat.d.ts +49 -0
- package/dist/src/tool-filter/compat.d.ts.map +1 -0
- package/dist/src/tool-filter/compat.js +72 -0
- package/dist/src/tool-filter/compat.js.map +1 -0
- package/dist/src/tool-filter/config/env-config-parser.d.ts +38 -0
- package/dist/src/tool-filter/config/env-config-parser.d.ts.map +1 -0
- package/dist/src/tool-filter/config/env-config-parser.js +103 -0
- package/dist/src/tool-filter/config/env-config-parser.js.map +1 -0
- package/dist/src/tool-filter/config/header-config-parser.d.ts +37 -0
- package/dist/src/tool-filter/config/header-config-parser.d.ts.map +1 -0
- package/dist/src/tool-filter/config/header-config-parser.js +118 -0
- package/dist/src/tool-filter/config/header-config-parser.js.map +1 -0
- package/dist/src/tool-filter/errors.d.ts +18 -0
- package/dist/src/tool-filter/errors.d.ts.map +1 -0
- package/dist/src/tool-filter/errors.js +21 -0
- package/dist/src/tool-filter/errors.js.map +1 -0
- package/dist/src/tool-filter/filter/filter-engine.d.ts +45 -0
- package/dist/src/tool-filter/filter/filter-engine.d.ts.map +1 -0
- package/dist/src/tool-filter/filter/filter-engine.js +94 -0
- package/dist/src/tool-filter/filter/filter-engine.js.map +1 -0
- package/dist/src/tool-filter/filter/filter-rules.d.ts +44 -0
- package/dist/src/tool-filter/filter/filter-rules.d.ts.map +1 -0
- package/dist/src/tool-filter/filter/filter-rules.js +72 -0
- package/dist/src/tool-filter/filter/filter-rules.js.map +1 -0
- package/dist/src/tool-filter/filter/global-tool-filter.d.ts +40 -0
- package/dist/src/tool-filter/filter/global-tool-filter.d.ts.map +1 -0
- package/dist/src/tool-filter/filter/global-tool-filter.js +92 -0
- package/dist/src/tool-filter/filter/global-tool-filter.js.map +1 -0
- package/dist/src/tool-filter/filter/session-tool-filter.d.ts +29 -0
- package/dist/src/tool-filter/filter/session-tool-filter.d.ts.map +1 -0
- package/dist/src/tool-filter/filter/session-tool-filter.js +69 -0
- package/dist/src/tool-filter/filter/session-tool-filter.js.map +1 -0
- package/dist/src/tool-filter/index.d.ts +25 -0
- package/dist/src/tool-filter/index.d.ts.map +1 -0
- package/dist/src/tool-filter/index.js +30 -0
- package/dist/src/tool-filter/index.js.map +1 -0
- package/dist/src/tool-filter/integration/tool-filter-service.d.ts +44 -0
- package/dist/src/tool-filter/integration/tool-filter-service.d.ts.map +1 -0
- package/dist/src/tool-filter/integration/tool-filter-service.js +68 -0
- package/dist/src/tool-filter/integration/tool-filter-service.js.map +1 -0
- package/dist/src/tool-filter/operation/operation-classifier.d.ts +20 -0
- package/dist/src/tool-filter/operation/operation-classifier.d.ts.map +1 -0
- package/dist/src/tool-filter/operation/operation-classifier.js +26 -0
- package/dist/src/tool-filter/operation/operation-classifier.js.map +1 -0
- package/dist/src/tool-filter/operation/operation-detector.d.ts +30 -0
- package/dist/src/tool-filter/operation/operation-detector.d.ts.map +1 -0
- package/dist/src/tool-filter/operation/operation-detector.js +96 -0
- package/dist/src/tool-filter/operation/operation-detector.js.map +1 -0
- package/dist/src/tool-filter/operation/operation-resolver.d.ts +22 -0
- package/dist/src/tool-filter/operation/operation-resolver.d.ts.map +1 -0
- package/dist/src/tool-filter/operation/operation-resolver.js +32 -0
- package/dist/src/tool-filter/operation/operation-resolver.js.map +1 -0
- package/dist/src/tool-filter/regex/regex-compiler.d.ts +22 -0
- package/dist/src/tool-filter/regex/regex-compiler.d.ts.map +1 -0
- package/dist/src/tool-filter/regex/regex-compiler.js +56 -0
- package/dist/src/tool-filter/regex/regex-compiler.js.map +1 -0
- package/dist/src/tool-filter/regex/regex-validator.d.ts +24 -0
- package/dist/src/tool-filter/regex/regex-validator.d.ts.map +1 -0
- package/dist/src/tool-filter/regex/regex-validator.js +58 -0
- package/dist/src/tool-filter/regex/regex-validator.js.map +1 -0
- package/dist/src/tool-filter/types.d.ts +92 -0
- package/dist/src/tool-filter/types.d.ts.map +1 -0
- package/dist/src/tool-filter/types.js +5 -0
- package/dist/src/tool-filter/types.js.map +1 -0
- package/dist/src/tool-filter/utils.d.ts +11 -0
- package/dist/src/tool-filter/utils.d.ts.map +1 -0
- package/dist/src/tool-filter/utils.js +13 -0
- package/dist/src/tool-filter/utils.js.map +1 -0
- package/dist/src/tool-filter.d.ts +65 -0
- package/dist/src/tool-filter.d.ts.map +1 -0
- package/dist/src/tool-filter.js +471 -0
- package/dist/src/tool-filter.js.map +1 -0
- package/dist/src/tool-generator.d.ts +1 -0
- package/dist/src/tool-generator.d.ts.map +1 -1
- package/dist/src/tool-generator.js +15 -6
- package/dist/src/tool-generator.js.map +1 -1
- package/dist/src/tooling/composite-executor.d.ts +77 -0
- package/dist/src/tooling/composite-executor.d.ts.map +1 -0
- package/dist/src/tooling/composite-executor.js +198 -0
- package/dist/src/tooling/composite-executor.js.map +1 -0
- package/dist/src/tooling/dag-executor.d.ts +49 -0
- package/dist/src/tooling/dag-executor.d.ts.map +1 -0
- package/dist/src/tooling/dag-executor.js +138 -0
- package/dist/src/tooling/dag-executor.js.map +1 -0
- package/dist/src/tooling/proxy-executor.d.ts +86 -0
- package/dist/src/tooling/proxy-executor.d.ts.map +1 -0
- package/dist/src/tooling/proxy-executor.js +501 -0
- package/dist/src/tooling/proxy-executor.js.map +1 -0
- package/dist/src/tooling/tool-generator.d.ts +67 -0
- package/dist/src/tooling/tool-generator.d.ts.map +1 -0
- package/dist/src/tooling/tool-generator.js +222 -0
- package/dist/src/tooling/tool-generator.js.map +1 -0
- package/dist/src/transport/http-client-factory.d.ts +65 -0
- package/dist/src/transport/http-client-factory.d.ts.map +1 -0
- package/dist/src/transport/http-client-factory.js +143 -0
- package/dist/src/transport/http-client-factory.js.map +1 -0
- package/dist/src/transport/http-transport-config.d.ts +6 -0
- package/dist/src/transport/http-transport-config.d.ts.map +1 -0
- package/dist/src/transport/http-transport-config.js +63 -0
- package/dist/src/transport/http-transport-config.js.map +1 -0
- package/dist/src/transport/http-transport.d.ts +329 -0
- package/dist/src/transport/http-transport.d.ts.map +1 -0
- package/dist/src/transport/http-transport.js +2584 -0
- package/dist/src/transport/http-transport.js.map +1 -0
- package/dist/src/transport/interceptors.d.ts +119 -0
- package/dist/src/transport/interceptors.d.ts.map +1 -0
- package/dist/src/transport/interceptors.js +413 -0
- package/dist/src/transport/interceptors.js.map +1 -0
- package/dist/src/transport/profile-index.d.ts +84 -0
- package/dist/src/transport/profile-index.d.ts.map +1 -0
- package/dist/src/transport/profile-index.js +405 -0
- package/dist/src/transport/profile-index.js.map +1 -0
- package/dist/src/types/http-transport.d.ts +26 -0
- package/dist/src/types/http-transport.d.ts.map +1 -1
- package/dist/src/types/openapi.d.ts +3 -0
- package/dist/src/types/openapi.d.ts.map +1 -1
- package/dist/src/types/profile.d.ts +16 -1
- package/dist/src/types/profile.d.ts.map +1 -1
- package/dist/src/validation/argument-normalizer.d.ts +6 -0
- package/dist/src/validation/argument-normalizer.d.ts.map +1 -0
- package/dist/src/validation/argument-normalizer.js +70 -0
- package/dist/src/validation/argument-normalizer.js.map +1 -0
- package/dist/src/validation/jsonrpc-validator.d.ts +27 -0
- package/dist/src/validation/jsonrpc-validator.d.ts.map +1 -0
- package/dist/src/validation/jsonrpc-validator.js +58 -0
- package/dist/src/validation/jsonrpc-validator.js.map +1 -0
- package/dist/src/validation/schema-validator.d.ts +30 -0
- package/dist/src/validation/schema-validator.d.ts.map +1 -0
- package/dist/src/validation/schema-validator.js +128 -0
- package/dist/src/validation/schema-validator.js.map +1 -0
- package/dist/src/validation/validation-utils.d.ts +49 -0
- package/dist/src/validation/validation-utils.d.ts.map +1 -0
- package/dist/src/validation/validation-utils.js +139 -0
- package/dist/src/validation/validation-utils.js.map +1 -0
- package/html/profile-index.html +386 -0
- package/package.json +10 -3
- package/profile-schema.json +77 -3
- package/profiles/gitlab/developer-profile-oauth.json +1520 -0
- package/profiles/gitlab/developer-profile-oauth.test.json +3432 -0
- package/profiles/gitlab/developer-profile.json +1508 -0
- package/profiles/gitlab/developer-profile.test.json +3432 -0
- package/profiles/gitlab/openapi.yaml +6891 -0
- package/profiles/n8n/openapi.yaml +2441 -0
- package/profiles/n8n/profile-optimized.json +965 -0
- package/profiles/n8n/profile-optimized.test.json +1078 -0
- package/profiles/n8n/profile.json +1033 -0
- package/profiles/n8n/profile.test.json +983 -0
- package/profiles/n8n-nodes/openapi.yaml +24 -0
- package/profiles/n8n-nodes/profile-nodes.json +44 -0
- package/profiles/n8n-nodes/profile-nodes.test.json +91 -0
- package/profiles/semgrep/openapi.yaml +4706 -0
- package/profiles/semgrep/profile.json +692 -0
- package/profiles/semgrep/profile.test.json +471 -0
- package/profiles/youtrack/openapi.json +16976 -0
- package/profiles/youtrack/profile.json +608 -0
- package/profiles/youtrack/profile.test.json +1926 -0
- package/dist/src/testing/fixtures.d.ts +0 -684
- package/dist/src/testing/fixtures.d.ts.map +0 -1
- package/dist/src/testing/fixtures.js +0 -528
- package/dist/src/testing/fixtures.js.map +0 -1
- package/dist/src/testing/mock-gitlab-server.d.ts +0 -43
- package/dist/src/testing/mock-gitlab-server.d.ts.map +0 -1
- package/dist/src/testing/mock-gitlab-server.js +0 -1026
- package/dist/src/testing/mock-gitlab-server.js.map +0 -1
- package/dist/src/testing/mock-semgrep-server.d.ts +0 -32
- package/dist/src/testing/mock-semgrep-server.d.ts.map +0 -1
- package/dist/src/testing/mock-semgrep-server.js +0 -213
- package/dist/src/testing/mock-semgrep-server.js.map +0 -1
- package/dist/src/testing/mock-youtrack-server.d.ts +0 -11
- package/dist/src/testing/mock-youtrack-server.d.ts.map +0 -1
- package/dist/src/testing/mock-youtrack-server.js +0 -152
- package/dist/src/testing/mock-youtrack-server.js.map +0 -1
|
@@ -1,1026 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Mock GitLab API server for integration testing
|
|
3
|
-
*
|
|
4
|
-
* Why: Enables end-to-end testing without real GitLab instance.
|
|
5
|
-
* Tests actual HTTP flow, parameter handling, error scenarios.
|
|
6
|
-
*/
|
|
7
|
-
import { http, HttpResponse } from 'msw';
|
|
8
|
-
import { setupServer } from 'msw/node';
|
|
9
|
-
import * as fixtures from './fixtures.js';
|
|
10
|
-
import { parsePaginationParams, parseSearchParam, parseBranchParams, parseScopeParam, } from './mock-utils.js';
|
|
11
|
-
/** Default BASE_URL for GitLab API (used by MSW interceptor) */
|
|
12
|
-
export const DEFAULT_BASE_URL = 'https://gitlab.com/api/v4';
|
|
13
|
-
/** Default OAuth config */
|
|
14
|
-
const DEFAULT_OAUTH_CONFIG = {
|
|
15
|
-
oauthBaseUrl: 'http://localhost:4000',
|
|
16
|
-
accessToken: 'mock-access-token-12345',
|
|
17
|
-
refreshToken: 'mock-refresh-token-67890',
|
|
18
|
-
expiresIn: 3600,
|
|
19
|
-
};
|
|
20
|
-
/**
|
|
21
|
-
* Helper: Extract and validate IID from URL
|
|
22
|
-
*
|
|
23
|
-
* Why: Prevents path traversal and invalid integer attacks
|
|
24
|
-
* Returns null if invalid (caller should return 400 Bad Request)
|
|
25
|
-
*/
|
|
26
|
-
function extractIidFromUrl(url) {
|
|
27
|
-
const parts = url.split('/');
|
|
28
|
-
const iidStr = parts[parts.length - 1];
|
|
29
|
-
if (!iidStr || !/^\d+$/.test(iidStr)) {
|
|
30
|
-
return null;
|
|
31
|
-
}
|
|
32
|
-
const iid = parseInt(iidStr, 10);
|
|
33
|
-
if (isNaN(iid) || iid < 1 || iid > 2147483647) {
|
|
34
|
-
return null;
|
|
35
|
-
}
|
|
36
|
-
return iid;
|
|
37
|
-
}
|
|
38
|
-
/**
|
|
39
|
-
* Extract merge request IID from notes URL
|
|
40
|
-
* URL format: /projects/{project}/merge_requests/{iid}/notes
|
|
41
|
-
*/
|
|
42
|
-
function extractMrIidFromNotesUrl(url) {
|
|
43
|
-
const urlWithoutQuery = url.split('?')[0];
|
|
44
|
-
const match = urlWithoutQuery.match(/\/merge_requests\/(\d+)\/notes/);
|
|
45
|
-
if (!match) {
|
|
46
|
-
return null;
|
|
47
|
-
}
|
|
48
|
-
const iid = parseInt(match[1], 10);
|
|
49
|
-
if (isNaN(iid) || iid < 1 || iid > 2147483647) {
|
|
50
|
-
return null;
|
|
51
|
-
}
|
|
52
|
-
return iid;
|
|
53
|
-
}
|
|
54
|
-
function extractBranchNameFromUrl(url) {
|
|
55
|
-
const pathWithoutQuery = url.split('?')[0];
|
|
56
|
-
const match = pathWithoutQuery.match(/\/repository\/branches\/([^\/]+)/);
|
|
57
|
-
if (!match) {
|
|
58
|
-
return null;
|
|
59
|
-
}
|
|
60
|
-
return decodeURIComponent(match[1]);
|
|
61
|
-
}
|
|
62
|
-
/**
|
|
63
|
-
* Create OAuth handlers for mock OAuth server
|
|
64
|
-
*/
|
|
65
|
-
export function createOAuthHandlers(config = DEFAULT_OAUTH_CONFIG) {
|
|
66
|
-
const { oauthBaseUrl, accessToken, refreshToken, expiresIn } = { ...DEFAULT_OAUTH_CONFIG, ...config };
|
|
67
|
-
return [
|
|
68
|
-
// OAuth Discovery endpoint
|
|
69
|
-
http.get(`${oauthBaseUrl}/.well-known/oauth-authorization-server`, () => {
|
|
70
|
-
return HttpResponse.json({
|
|
71
|
-
issuer: oauthBaseUrl,
|
|
72
|
-
authorization_endpoint: `${oauthBaseUrl}/oauth/authorize`,
|
|
73
|
-
token_endpoint: `${oauthBaseUrl}/oauth/token`,
|
|
74
|
-
response_types_supported: ['code'],
|
|
75
|
-
grant_types_supported: ['authorization_code', 'refresh_token'],
|
|
76
|
-
code_challenge_methods_supported: ['S256'],
|
|
77
|
-
});
|
|
78
|
-
}),
|
|
79
|
-
// OAuth Authorization endpoint - redirects with code
|
|
80
|
-
http.get(`${oauthBaseUrl}/oauth/authorize`, ({ request }) => {
|
|
81
|
-
const url = new URL(request.url);
|
|
82
|
-
const redirectUri = url.searchParams.get('redirect_uri');
|
|
83
|
-
const state = url.searchParams.get('state');
|
|
84
|
-
if (!redirectUri) {
|
|
85
|
-
return HttpResponse.json({ error: 'missing_redirect_uri' }, { status: 400 });
|
|
86
|
-
}
|
|
87
|
-
const code = 'mock-code-' + Math.random().toString(36).substring(7);
|
|
88
|
-
const redirectUrl = new URL(redirectUri);
|
|
89
|
-
redirectUrl.searchParams.set('code', code);
|
|
90
|
-
if (state) {
|
|
91
|
-
redirectUrl.searchParams.set('state', state);
|
|
92
|
-
}
|
|
93
|
-
return new HttpResponse(null, {
|
|
94
|
-
status: 302,
|
|
95
|
-
headers: { Location: redirectUrl.toString() },
|
|
96
|
-
});
|
|
97
|
-
}),
|
|
98
|
-
// OAuth Token endpoint
|
|
99
|
-
http.post(`${oauthBaseUrl}/oauth/token`, async ({ request }) => {
|
|
100
|
-
const contentType = request.headers.get('content-type') || '';
|
|
101
|
-
let params;
|
|
102
|
-
if (contentType.includes('application/json')) {
|
|
103
|
-
params = await request.json();
|
|
104
|
-
}
|
|
105
|
-
else {
|
|
106
|
-
const body = await request.text();
|
|
107
|
-
params = Object.fromEntries(new URLSearchParams(body));
|
|
108
|
-
}
|
|
109
|
-
const grantType = params.grant_type;
|
|
110
|
-
if (grantType === 'authorization_code') {
|
|
111
|
-
if (!params.code) {
|
|
112
|
-
return HttpResponse.json({ error: 'invalid_grant' }, { status: 400 });
|
|
113
|
-
}
|
|
114
|
-
return HttpResponse.json({
|
|
115
|
-
access_token: accessToken,
|
|
116
|
-
refresh_token: refreshToken,
|
|
117
|
-
token_type: 'Bearer',
|
|
118
|
-
expires_in: expiresIn,
|
|
119
|
-
scope: 'api',
|
|
120
|
-
});
|
|
121
|
-
}
|
|
122
|
-
if (grantType === 'refresh_token') {
|
|
123
|
-
if (!params.refresh_token) {
|
|
124
|
-
return HttpResponse.json({ error: 'invalid_grant' }, { status: 400 });
|
|
125
|
-
}
|
|
126
|
-
return HttpResponse.json({
|
|
127
|
-
access_token: `${accessToken}-refreshed`,
|
|
128
|
-
refresh_token: `${refreshToken}-new`,
|
|
129
|
-
token_type: 'Bearer',
|
|
130
|
-
expires_in: expiresIn,
|
|
131
|
-
scope: 'api',
|
|
132
|
-
});
|
|
133
|
-
}
|
|
134
|
-
return HttpResponse.json({ error: 'unsupported_grant_type' }, { status: 400 });
|
|
135
|
-
}),
|
|
136
|
-
];
|
|
137
|
-
}
|
|
138
|
-
/**
|
|
139
|
-
* Create GitLab API handlers with configurable base URL
|
|
140
|
-
*/
|
|
141
|
-
export function createGitLabHandlers(baseUrl = DEFAULT_BASE_URL) {
|
|
142
|
-
const branchStates = new Map(fixtures.mockBranchesList.map((branch) => [
|
|
143
|
-
branch.name,
|
|
144
|
-
structuredClone(branch),
|
|
145
|
-
]));
|
|
146
|
-
const escapedBaseUrl = baseUrl.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&');
|
|
147
|
-
const findBranch = (name) => {
|
|
148
|
-
if (!name)
|
|
149
|
-
return undefined;
|
|
150
|
-
return branchStates.get(name);
|
|
151
|
-
};
|
|
152
|
-
const listBranches = () => Array.from(branchStates.values());
|
|
153
|
-
return [
|
|
154
|
-
// Groups
|
|
155
|
-
http.get(`${baseUrl}/groups`, ({ request }) => {
|
|
156
|
-
const { page } = parsePaginationParams(request);
|
|
157
|
-
const search = parseSearchParam(request);
|
|
158
|
-
let groups = fixtures.mockGroupsList;
|
|
159
|
-
if (search) {
|
|
160
|
-
groups = groups.filter(g => g.name.toLowerCase().includes(search.toLowerCase()) ||
|
|
161
|
-
g.path.toLowerCase().includes(search.toLowerCase()));
|
|
162
|
-
}
|
|
163
|
-
if (page > 1) {
|
|
164
|
-
return HttpResponse.json([]);
|
|
165
|
-
}
|
|
166
|
-
return HttpResponse.json(groups);
|
|
167
|
-
}),
|
|
168
|
-
http.get(`${baseUrl}/groups/:id`, ({ params }) => {
|
|
169
|
-
const groupId = params.id;
|
|
170
|
-
if (groupId === '36173' || groupId === 'davidruzicka') {
|
|
171
|
-
return HttpResponse.json(fixtures.mockGroup);
|
|
172
|
-
}
|
|
173
|
-
return HttpResponse.json({ message: 'Group Not Found' }, { status: 404 });
|
|
174
|
-
}),
|
|
175
|
-
http.get(`${baseUrl}/groups/:id/projects`, ({ request, params }) => {
|
|
176
|
-
const groupId = params.id;
|
|
177
|
-
const { page } = parsePaginationParams(request);
|
|
178
|
-
if (groupId === '36173' || groupId === 'davidruzicka') {
|
|
179
|
-
if (page > 1) {
|
|
180
|
-
return HttpResponse.json([]);
|
|
181
|
-
}
|
|
182
|
-
return HttpResponse.json(fixtures.mockProjectsList);
|
|
183
|
-
}
|
|
184
|
-
return HttpResponse.json({ message: 'Group Not Found' }, { status: 404 });
|
|
185
|
-
}),
|
|
186
|
-
http.get(`${baseUrl}/groups/:id/subgroups`, ({ request, params }) => {
|
|
187
|
-
const groupId = params.id;
|
|
188
|
-
const { page } = parsePaginationParams(request);
|
|
189
|
-
if (groupId === '36173' || groupId === 'davidruzicka') {
|
|
190
|
-
if (page > 1) {
|
|
191
|
-
return HttpResponse.json([]);
|
|
192
|
-
}
|
|
193
|
-
return HttpResponse.json(fixtures.mockSubgroupsList);
|
|
194
|
-
}
|
|
195
|
-
return HttpResponse.json({ message: 'Group Not Found' }, { status: 404 });
|
|
196
|
-
}),
|
|
197
|
-
// Projects
|
|
198
|
-
http.get(`${baseUrl}/projects`, ({ request }) => {
|
|
199
|
-
const { page } = parsePaginationParams(request);
|
|
200
|
-
const search = parseSearchParam(request);
|
|
201
|
-
let projects = fixtures.mockProjectsList;
|
|
202
|
-
if (search) {
|
|
203
|
-
projects = projects.filter(p => p.name.toLowerCase().includes(search.toLowerCase()) ||
|
|
204
|
-
p.description?.toLowerCase().includes(search.toLowerCase()));
|
|
205
|
-
}
|
|
206
|
-
if (page > 1) {
|
|
207
|
-
return HttpResponse.json([]);
|
|
208
|
-
}
|
|
209
|
-
return HttpResponse.json(projects);
|
|
210
|
-
}),
|
|
211
|
-
http.get(`${baseUrl}/projects/:id`, ({ params }) => {
|
|
212
|
-
const projectId = params.id;
|
|
213
|
-
if (projectId === '12345' || projectId === 'davidruzicka%2Fmcp4openapi') {
|
|
214
|
-
return HttpResponse.json(fixtures.mockProject);
|
|
215
|
-
}
|
|
216
|
-
return HttpResponse.json({ message: 'Project Not Found' }, { status: 404 });
|
|
217
|
-
}),
|
|
218
|
-
// Project Badges
|
|
219
|
-
http.get(`${baseUrl}/projects/*/badges`, ({ request }) => {
|
|
220
|
-
const { page } = parsePaginationParams(request);
|
|
221
|
-
if (page === 1) {
|
|
222
|
-
return HttpResponse.json(fixtures.mockBadgesList);
|
|
223
|
-
}
|
|
224
|
-
return HttpResponse.json([]);
|
|
225
|
-
}),
|
|
226
|
-
http.get(`${baseUrl}/projects/*/badges/*`, ({ request }) => {
|
|
227
|
-
const badgeId = extractIidFromUrl(request.url);
|
|
228
|
-
if (badgeId === null) {
|
|
229
|
-
return HttpResponse.json({ error: 'Invalid badge ID' }, { status: 400 });
|
|
230
|
-
}
|
|
231
|
-
if (badgeId === 1) {
|
|
232
|
-
return HttpResponse.json(fixtures.mockBadge);
|
|
233
|
-
}
|
|
234
|
-
return HttpResponse.json({ message: 'Not Found' }, { status: 404 });
|
|
235
|
-
}),
|
|
236
|
-
http.post(`${baseUrl}/projects/*/badges`, async ({ request }) => {
|
|
237
|
-
const body = await request.json();
|
|
238
|
-
if (!body.link_url || !body.image_url) {
|
|
239
|
-
return HttpResponse.json({ error: 'link_url and image_url are required' }, { status: 400 });
|
|
240
|
-
}
|
|
241
|
-
return HttpResponse.json({
|
|
242
|
-
...fixtures.mockBadge,
|
|
243
|
-
id: 3,
|
|
244
|
-
name: body.name || 'New Badge',
|
|
245
|
-
link_url: body.link_url,
|
|
246
|
-
image_url: body.image_url,
|
|
247
|
-
}, { status: 201 });
|
|
248
|
-
}),
|
|
249
|
-
http.put(`${baseUrl}/projects/*/badges/*`, async ({ request }) => {
|
|
250
|
-
const badgeId = extractIidFromUrl(request.url);
|
|
251
|
-
if (badgeId === null) {
|
|
252
|
-
return HttpResponse.json({ error: 'Invalid badge ID' }, { status: 400 });
|
|
253
|
-
}
|
|
254
|
-
const body = await request.json();
|
|
255
|
-
if (badgeId === 1) {
|
|
256
|
-
return HttpResponse.json({
|
|
257
|
-
...fixtures.mockBadge,
|
|
258
|
-
name: body.name || fixtures.mockBadge.name,
|
|
259
|
-
link_url: body.link_url || fixtures.mockBadge.link_url,
|
|
260
|
-
image_url: body.image_url || fixtures.mockBadge.image_url,
|
|
261
|
-
});
|
|
262
|
-
}
|
|
263
|
-
return HttpResponse.json({ message: 'Not Found' }, { status: 404 });
|
|
264
|
-
}),
|
|
265
|
-
http.delete(`${baseUrl}/projects/*/badges/*`, ({ request }) => {
|
|
266
|
-
const badgeId = extractIidFromUrl(request.url);
|
|
267
|
-
if (badgeId === null) {
|
|
268
|
-
return HttpResponse.json({ error: 'Invalid badge ID' }, { status: 400 });
|
|
269
|
-
}
|
|
270
|
-
if (badgeId === 1) {
|
|
271
|
-
return new HttpResponse(null, { status: 204 });
|
|
272
|
-
}
|
|
273
|
-
return HttpResponse.json({ message: 'Not Found' }, { status: 404 });
|
|
274
|
-
}),
|
|
275
|
-
// Group Badges
|
|
276
|
-
http.get(`${baseUrl}/groups/*/badges`, () => {
|
|
277
|
-
return HttpResponse.json(fixtures.mockBadgesList);
|
|
278
|
-
}),
|
|
279
|
-
http.post(`${baseUrl}/groups/*/badges`, async ({ request }) => {
|
|
280
|
-
const body = await request.json();
|
|
281
|
-
return HttpResponse.json({
|
|
282
|
-
...fixtures.mockBadge,
|
|
283
|
-
id: 4,
|
|
284
|
-
kind: 'group',
|
|
285
|
-
name: body.name || 'Group Badge',
|
|
286
|
-
}, { status: 201 });
|
|
287
|
-
}),
|
|
288
|
-
// Branches
|
|
289
|
-
http.get(`${baseUrl}/projects/*/repository/branches`, ({ request }) => {
|
|
290
|
-
const search = parseSearchParam(request);
|
|
291
|
-
let branches = listBranches();
|
|
292
|
-
if (search) {
|
|
293
|
-
branches = branches.filter((b) => b.name.includes(search));
|
|
294
|
-
}
|
|
295
|
-
return HttpResponse.json(branches);
|
|
296
|
-
}),
|
|
297
|
-
http.head(`${baseUrl}/projects/*/repository/branches/*`, ({ request }) => {
|
|
298
|
-
const branchName = extractBranchNameFromUrl(request.url);
|
|
299
|
-
const branch = findBranch(branchName);
|
|
300
|
-
if (!branch) {
|
|
301
|
-
return HttpResponse.json({ message: 'Branch Not Found' }, { status: 404 });
|
|
302
|
-
}
|
|
303
|
-
return HttpResponse.json({ exists: true });
|
|
304
|
-
}),
|
|
305
|
-
http.get(`${baseUrl}/projects/*/repository/branches/*`, ({ request }) => {
|
|
306
|
-
const branchName = extractBranchNameFromUrl(request.url);
|
|
307
|
-
const branch = findBranch(branchName);
|
|
308
|
-
if (branch) {
|
|
309
|
-
return HttpResponse.json(branch);
|
|
310
|
-
}
|
|
311
|
-
return HttpResponse.json({ message: 'Branch Not Found' }, { status: 404 });
|
|
312
|
-
}),
|
|
313
|
-
http.post(`${baseUrl}/projects/*/repository/branches`, async ({ request }) => {
|
|
314
|
-
const { branch, ref } = parseBranchParams(request);
|
|
315
|
-
if (!branch || !ref) {
|
|
316
|
-
return HttpResponse.json({ error: 'branch and ref parameters are required' }, { status: 400 });
|
|
317
|
-
}
|
|
318
|
-
const baseCommit = structuredClone(fixtures.mockBranch.commit);
|
|
319
|
-
const newBranch = {
|
|
320
|
-
name: branch,
|
|
321
|
-
commit: {
|
|
322
|
-
...baseCommit,
|
|
323
|
-
id: `${branch}-${Date.now()}`,
|
|
324
|
-
short_id: `${branch.substring(0, 7)}-new`
|
|
325
|
-
},
|
|
326
|
-
merged: false,
|
|
327
|
-
protected: false,
|
|
328
|
-
default: false,
|
|
329
|
-
can_push: true,
|
|
330
|
-
web_url: `https://gitlab.com/my-org/my-project/-/tree/${encodeURIComponent(branch)}`,
|
|
331
|
-
};
|
|
332
|
-
branchStates.set(branch, newBranch);
|
|
333
|
-
return HttpResponse.json(newBranch, { status: 201 });
|
|
334
|
-
}),
|
|
335
|
-
http.delete(`${baseUrl}/projects/*/repository/branches/*`, ({ request }) => {
|
|
336
|
-
const branchName = extractBranchNameFromUrl(request.url);
|
|
337
|
-
const branch = findBranch(branchName);
|
|
338
|
-
if (!branch) {
|
|
339
|
-
return HttpResponse.json({ message: 'Branch Not Found' }, { status: 404 });
|
|
340
|
-
}
|
|
341
|
-
if (branch.default) {
|
|
342
|
-
return HttpResponse.json({ message: 'Cannot delete default branch' }, { status: 403 });
|
|
343
|
-
}
|
|
344
|
-
branchStates.delete(branchName);
|
|
345
|
-
return new HttpResponse(null, { status: 204 });
|
|
346
|
-
}),
|
|
347
|
-
http.put(`${baseUrl}/projects/*/repository/branches/*/protect`, async ({ request }) => {
|
|
348
|
-
const branchName = extractBranchNameFromUrl(request.url);
|
|
349
|
-
const branch = findBranch(branchName);
|
|
350
|
-
if (!branch) {
|
|
351
|
-
return HttpResponse.json({ message: 'Branch Not Found' }, { status: 404 });
|
|
352
|
-
}
|
|
353
|
-
const body = await request.json();
|
|
354
|
-
branch.protected = true;
|
|
355
|
-
if (typeof body.developers_can_push === 'boolean') {
|
|
356
|
-
branch.developers_can_push = body.developers_can_push;
|
|
357
|
-
}
|
|
358
|
-
if (typeof body.developers_can_merge === 'boolean') {
|
|
359
|
-
branch.developers_can_merge = body.developers_can_merge;
|
|
360
|
-
}
|
|
361
|
-
branchStates.set(branchName, branch);
|
|
362
|
-
return HttpResponse.json(branch);
|
|
363
|
-
}),
|
|
364
|
-
http.put(`${baseUrl}/projects/*/repository/branches/*/unprotect`, async ({ request }) => {
|
|
365
|
-
const branchName = extractBranchNameFromUrl(request.url);
|
|
366
|
-
const branch = findBranch(branchName);
|
|
367
|
-
if (!branch) {
|
|
368
|
-
return HttpResponse.json({ message: 'Branch Not Found' }, { status: 404 });
|
|
369
|
-
}
|
|
370
|
-
const body = await request.json();
|
|
371
|
-
branch.protected = false;
|
|
372
|
-
if (typeof body.developers_can_push === 'boolean') {
|
|
373
|
-
branch.developers_can_push = body.developers_can_push;
|
|
374
|
-
}
|
|
375
|
-
if (typeof body.developers_can_merge === 'boolean') {
|
|
376
|
-
branch.developers_can_merge = body.developers_can_merge;
|
|
377
|
-
}
|
|
378
|
-
branchStates.set(branchName, branch);
|
|
379
|
-
return HttpResponse.json(branch);
|
|
380
|
-
}),
|
|
381
|
-
// Access Requests
|
|
382
|
-
http.get(`${baseUrl}/projects/*/access_requests`, () => {
|
|
383
|
-
return HttpResponse.json(fixtures.mockAccessRequestsList);
|
|
384
|
-
}),
|
|
385
|
-
http.get(`${baseUrl}/groups/*/access_requests`, () => {
|
|
386
|
-
return HttpResponse.json(fixtures.mockAccessRequestsList);
|
|
387
|
-
}),
|
|
388
|
-
http.post(`${baseUrl}/projects/*/access_requests`, () => {
|
|
389
|
-
return HttpResponse.json(fixtures.mockAccessRequest, { status: 201 });
|
|
390
|
-
}),
|
|
391
|
-
http.put(`${baseUrl}/projects/*/access_requests/*/approve`, async ({ request }) => {
|
|
392
|
-
const body = await request.json();
|
|
393
|
-
const parts = request.url.split('/');
|
|
394
|
-
const userId = parseInt(parts[parts.length - 2], 10);
|
|
395
|
-
return HttpResponse.json({
|
|
396
|
-
...fixtures.mockAccessRequest,
|
|
397
|
-
id: userId,
|
|
398
|
-
access_level: body.access_level || 30,
|
|
399
|
-
});
|
|
400
|
-
}),
|
|
401
|
-
http.delete(`${baseUrl}/projects/*/access_requests/*`, () => {
|
|
402
|
-
return new HttpResponse(null, { status: 204 });
|
|
403
|
-
}),
|
|
404
|
-
// Jobs
|
|
405
|
-
http.get(`${baseUrl}/projects/*/jobs/*/artifacts`, () => {
|
|
406
|
-
const content = fixtures.mockArtifactsContent;
|
|
407
|
-
return new HttpResponse(content, {
|
|
408
|
-
status: 200,
|
|
409
|
-
headers: {
|
|
410
|
-
'Content-Type': 'application/octet-stream',
|
|
411
|
-
'Content-Length': String(Buffer.byteLength(content)),
|
|
412
|
-
},
|
|
413
|
-
});
|
|
414
|
-
}),
|
|
415
|
-
http.get(`${baseUrl}/projects/*/jobs`, ({ request }) => {
|
|
416
|
-
const scope = parseScopeParam(request);
|
|
417
|
-
if (scope.length > 0 && scope.includes('failed')) {
|
|
418
|
-
return HttpResponse.json(fixtures.mockJobsList.filter(j => j.status === 'failed'));
|
|
419
|
-
}
|
|
420
|
-
return HttpResponse.json(fixtures.mockJobsList);
|
|
421
|
-
}),
|
|
422
|
-
http.get(`${baseUrl}/projects/*/jobs/*`, ({ request }) => {
|
|
423
|
-
const jobId = extractIidFromUrl(request.url);
|
|
424
|
-
if (jobId === null) {
|
|
425
|
-
return HttpResponse.json({ error: 'Invalid job ID' }, { status: 400 });
|
|
426
|
-
}
|
|
427
|
-
if (jobId === 1234) {
|
|
428
|
-
return HttpResponse.json(fixtures.mockJobWithArtifacts);
|
|
429
|
-
}
|
|
430
|
-
return HttpResponse.json({ message: 'Not Found' }, { status: 404 });
|
|
431
|
-
}),
|
|
432
|
-
http.post(`${baseUrl}/projects/*/jobs/*/play`, ({ request }) => {
|
|
433
|
-
const parts = request.url.split('/');
|
|
434
|
-
const jobId = parseInt(parts[parts.length - 2], 10);
|
|
435
|
-
return HttpResponse.json({
|
|
436
|
-
...fixtures.mockJob,
|
|
437
|
-
id: jobId,
|
|
438
|
-
status: 'pending',
|
|
439
|
-
});
|
|
440
|
-
}),
|
|
441
|
-
http.post(`${baseUrl}/projects/*/jobs/*/retry`, ({ request }) => {
|
|
442
|
-
const parts = request.url.split('/jobs/');
|
|
443
|
-
const tail = parts[1] || '';
|
|
444
|
-
const jobId = parseInt(tail.split('/')[0], 10);
|
|
445
|
-
if (isNaN(jobId)) {
|
|
446
|
-
return HttpResponse.json({ error: 'Invalid job ID' }, { status: 400 });
|
|
447
|
-
}
|
|
448
|
-
if (jobId === 1234) {
|
|
449
|
-
return HttpResponse.json({ ...fixtures.mockJobWithArtifacts, status: 'pending' }, { status: 201 });
|
|
450
|
-
}
|
|
451
|
-
return HttpResponse.json({ message: 'Not Found' }, { status: 404 });
|
|
452
|
-
}),
|
|
453
|
-
http.post(`${baseUrl}/projects/*/jobs/*/cancel`, ({ request }) => {
|
|
454
|
-
const parts = request.url.split('/jobs/');
|
|
455
|
-
const tail = parts[1] || '';
|
|
456
|
-
const jobId = parseInt(tail.split('/')[0], 10);
|
|
457
|
-
if (isNaN(jobId)) {
|
|
458
|
-
return HttpResponse.json({ error: 'Invalid job ID' }, { status: 400 });
|
|
459
|
-
}
|
|
460
|
-
if (jobId === 1234) {
|
|
461
|
-
return HttpResponse.json({ ...fixtures.mockJobWithArtifacts, status: 'canceled' }, { status: 201 });
|
|
462
|
-
}
|
|
463
|
-
return HttpResponse.json({ message: 'Not Found' }, { status: 404 });
|
|
464
|
-
}),
|
|
465
|
-
// Rate limiting simulation
|
|
466
|
-
http.get(`${baseUrl}/rate-limit-test`, () => {
|
|
467
|
-
return HttpResponse.json({ message: 'Rate limit exceeded' }, { status: 429, headers: { 'Retry-After': '60' } });
|
|
468
|
-
}),
|
|
469
|
-
// Pipelines
|
|
470
|
-
http.post(`${baseUrl}/projects/*/pipeline`, async ({ request }) => {
|
|
471
|
-
const body = await request.json();
|
|
472
|
-
if (!body.ref) {
|
|
473
|
-
return HttpResponse.json({ error: 'ref is required' }, { status: 400 });
|
|
474
|
-
}
|
|
475
|
-
return HttpResponse.json(fixtures.mockPipeline, { status: 201 });
|
|
476
|
-
}),
|
|
477
|
-
http.get(`${baseUrl}/projects/*/pipelines/*`, ({ request }) => {
|
|
478
|
-
const pipelineId = extractIidFromUrl(request.url);
|
|
479
|
-
if (pipelineId === null) {
|
|
480
|
-
return HttpResponse.json({ error: 'Invalid pipeline ID' }, { status: 400 });
|
|
481
|
-
}
|
|
482
|
-
if (pipelineId === fixtures.mockPipeline.id) {
|
|
483
|
-
return HttpResponse.json(fixtures.mockPipeline);
|
|
484
|
-
}
|
|
485
|
-
return HttpResponse.json({ message: 'Not Found' }, { status: 404 });
|
|
486
|
-
}),
|
|
487
|
-
// Repository files
|
|
488
|
-
http.get(`${baseUrl}/projects/*/repository/files/*/raw`, ({ request }) => {
|
|
489
|
-
const url = new URL(request.url);
|
|
490
|
-
const pathWithoutBase = url.pathname.replace(`${new URL(baseUrl).pathname}/projects/`, '');
|
|
491
|
-
const fileMatch = decodeURIComponent(pathWithoutBase).match(/repository\/files\/(.+)\/raw$/);
|
|
492
|
-
if (!fileMatch) {
|
|
493
|
-
return HttpResponse.json({ error: 'Invalid file path' }, { status: 400 });
|
|
494
|
-
}
|
|
495
|
-
const filePath = fileMatch[1];
|
|
496
|
-
if (!url.searchParams.get('ref')) {
|
|
497
|
-
return HttpResponse.json({ error: 'ref is required' }, { status: 400 });
|
|
498
|
-
}
|
|
499
|
-
if (filePath === 'src/index.js') {
|
|
500
|
-
return new HttpResponse(fixtures.mockRepositoryFileContent, {
|
|
501
|
-
status: 200,
|
|
502
|
-
headers: { 'Content-Type': 'text/plain' },
|
|
503
|
-
});
|
|
504
|
-
}
|
|
505
|
-
return HttpResponse.json({ message: 'Not Found' }, { status: 404 });
|
|
506
|
-
}),
|
|
507
|
-
http.get(`${baseUrl}/projects/*/repository/files/*`, ({ request }) => {
|
|
508
|
-
const url = new URL(request.url);
|
|
509
|
-
const pathWithoutBase = url.pathname.replace(`${new URL(baseUrl).pathname}/projects/`, '');
|
|
510
|
-
const fileMatch = decodeURIComponent(pathWithoutBase).match(/repository\/files\/(.+)$/);
|
|
511
|
-
if (!fileMatch) {
|
|
512
|
-
return HttpResponse.json({ error: 'Invalid file path' }, { status: 400 });
|
|
513
|
-
}
|
|
514
|
-
const filePath = fileMatch[1];
|
|
515
|
-
if (!url.searchParams.get('ref')) {
|
|
516
|
-
return HttpResponse.json({ error: 'ref is required' }, { status: 400 });
|
|
517
|
-
}
|
|
518
|
-
if (filePath === 'src/index.js') {
|
|
519
|
-
return HttpResponse.json(fixtures.mockRepositoryFile);
|
|
520
|
-
}
|
|
521
|
-
return HttpResponse.json({ message: 'Not Found' }, { status: 404 });
|
|
522
|
-
}),
|
|
523
|
-
// Merge Request Notes (MUST be before generic merge_requests/* handlers)
|
|
524
|
-
http.get(`${baseUrl}/projects/*/merge_requests/*/notes`, ({ request }) => {
|
|
525
|
-
let mergeRequestIid = extractMrIidFromNotesUrl(request.url);
|
|
526
|
-
if (mergeRequestIid === null) {
|
|
527
|
-
const decodedUrl = decodeURIComponent(request.url);
|
|
528
|
-
mergeRequestIid = extractMrIidFromNotesUrl(decodedUrl);
|
|
529
|
-
}
|
|
530
|
-
if (mergeRequestIid === null) {
|
|
531
|
-
const altMatch = request.url.match(/merge_requests[\/%2F](\d+)[\/%2F]notes/);
|
|
532
|
-
if (altMatch) {
|
|
533
|
-
mergeRequestIid = parseInt(altMatch[1], 10);
|
|
534
|
-
}
|
|
535
|
-
}
|
|
536
|
-
if (mergeRequestIid === null || isNaN(mergeRequestIid)) {
|
|
537
|
-
return HttpResponse.json({ error: 'Invalid merge request IID' }, { status: 400 });
|
|
538
|
-
}
|
|
539
|
-
if (mergeRequestIid === 1) {
|
|
540
|
-
return HttpResponse.json(fixtures.mockNotesList);
|
|
541
|
-
}
|
|
542
|
-
return HttpResponse.json({ message: 'Not Found' }, { status: 404 });
|
|
543
|
-
}),
|
|
544
|
-
http.post(`${baseUrl}/projects/*/merge_requests/*/notes`, async ({ request }) => {
|
|
545
|
-
let mergeRequestIid = extractMrIidFromNotesUrl(request.url);
|
|
546
|
-
if (mergeRequestIid === null) {
|
|
547
|
-
const decodedUrl = decodeURIComponent(request.url);
|
|
548
|
-
mergeRequestIid = extractMrIidFromNotesUrl(decodedUrl);
|
|
549
|
-
}
|
|
550
|
-
if (mergeRequestIid === null) {
|
|
551
|
-
const altMatch = request.url.match(/merge_requests[\/%2F](\d+)[\/%2F]notes/);
|
|
552
|
-
if (altMatch) {
|
|
553
|
-
mergeRequestIid = parseInt(altMatch[1], 10);
|
|
554
|
-
}
|
|
555
|
-
}
|
|
556
|
-
if (mergeRequestIid === null || isNaN(mergeRequestIid)) {
|
|
557
|
-
return HttpResponse.json({ error: 'Invalid merge request IID' }, { status: 400 });
|
|
558
|
-
}
|
|
559
|
-
const body = await request.json();
|
|
560
|
-
if (!body.body) {
|
|
561
|
-
return HttpResponse.json({ error: 'body is required' }, { status: 400 });
|
|
562
|
-
}
|
|
563
|
-
const createdNote = {
|
|
564
|
-
...fixtures.mockNote,
|
|
565
|
-
id: 3,
|
|
566
|
-
body: body.body,
|
|
567
|
-
confidential: body.confidential || false,
|
|
568
|
-
created_at: new Date().toISOString(),
|
|
569
|
-
};
|
|
570
|
-
return HttpResponse.json(createdNote, { status: 201 });
|
|
571
|
-
}),
|
|
572
|
-
http.put(`${baseUrl}/projects/*/merge_requests/*/notes/*`, async ({ request }) => {
|
|
573
|
-
const urlWithoutQuery = request.url.split('?')[0];
|
|
574
|
-
const urlParts = urlWithoutQuery.split('/notes/');
|
|
575
|
-
if (urlParts.length < 2) {
|
|
576
|
-
return HttpResponse.json({ error: 'Invalid note ID' }, { status: 400 });
|
|
577
|
-
}
|
|
578
|
-
const noteIdStr = urlParts[1].split('?')[0].split('/')[0];
|
|
579
|
-
const noteId = parseInt(noteIdStr, 10);
|
|
580
|
-
if (isNaN(noteId)) {
|
|
581
|
-
return HttpResponse.json({ error: 'Invalid note ID' }, { status: 400 });
|
|
582
|
-
}
|
|
583
|
-
if (noteId === 1) {
|
|
584
|
-
const body = await request.json();
|
|
585
|
-
if (!body.body) {
|
|
586
|
-
return HttpResponse.json({ error: 'body is required' }, { status: 400 });
|
|
587
|
-
}
|
|
588
|
-
const updatedNote = {
|
|
589
|
-
...fixtures.mockNote,
|
|
590
|
-
id: noteId,
|
|
591
|
-
body: body.body,
|
|
592
|
-
confidential: body.confidential !== undefined ? body.confidential : fixtures.mockNote.confidential,
|
|
593
|
-
updated_at: new Date().toISOString(),
|
|
594
|
-
};
|
|
595
|
-
return HttpResponse.json(updatedNote, { status: 200 });
|
|
596
|
-
}
|
|
597
|
-
return HttpResponse.json({ message: 'Not Found' }, { status: 404 });
|
|
598
|
-
}),
|
|
599
|
-
http.get(`${baseUrl}/projects/*/merge_requests/*/notes/*`, ({ request }) => {
|
|
600
|
-
const parts = request.url.split('/notes/');
|
|
601
|
-
if (parts.length < 2) {
|
|
602
|
-
return HttpResponse.json({ error: 'Invalid note ID' }, { status: 400 });
|
|
603
|
-
}
|
|
604
|
-
const noteIdStr = parts[1].split(/[/?]/)[0];
|
|
605
|
-
const noteId = parseInt(noteIdStr, 10);
|
|
606
|
-
if (isNaN(noteId)) {
|
|
607
|
-
return HttpResponse.json({ error: 'Invalid note ID' }, { status: 400 });
|
|
608
|
-
}
|
|
609
|
-
if (noteId === 1) {
|
|
610
|
-
return HttpResponse.json(fixtures.mockNote);
|
|
611
|
-
}
|
|
612
|
-
if (noteId === 2) {
|
|
613
|
-
return HttpResponse.json(fixtures.mockNotesList[1]);
|
|
614
|
-
}
|
|
615
|
-
return HttpResponse.json({ message: 'Not Found' }, { status: 404 });
|
|
616
|
-
}),
|
|
617
|
-
http.delete(`${baseUrl}/projects/*/merge_requests/*/notes/*`, ({ request }) => {
|
|
618
|
-
const urlParts = request.url.split('/notes/');
|
|
619
|
-
if (urlParts.length < 2) {
|
|
620
|
-
return HttpResponse.json({ error: 'Invalid note ID' }, { status: 400 });
|
|
621
|
-
}
|
|
622
|
-
const noteId = parseInt(urlParts[1], 10);
|
|
623
|
-
if (isNaN(noteId)) {
|
|
624
|
-
return HttpResponse.json({ error: 'Invalid note ID' }, { status: 400 });
|
|
625
|
-
}
|
|
626
|
-
if (noteId === 1) {
|
|
627
|
-
return new HttpResponse(null, { status: 204 });
|
|
628
|
-
}
|
|
629
|
-
return HttpResponse.json({ message: 'Not Found' }, { status: 404 });
|
|
630
|
-
}),
|
|
631
|
-
// Discussions
|
|
632
|
-
http.get(`${baseUrl}/projects/*/merge_requests/*/discussions`, () => {
|
|
633
|
-
return HttpResponse.json([fixtures.mockDiscussion]);
|
|
634
|
-
}),
|
|
635
|
-
http.post(`${baseUrl}/projects/*/merge_requests/*/discussions`, async ({ request }) => {
|
|
636
|
-
const body = await request.json();
|
|
637
|
-
if (!body.body) {
|
|
638
|
-
return HttpResponse.json({ error: 'body is required' }, { status: 400 });
|
|
639
|
-
}
|
|
640
|
-
return HttpResponse.json(fixtures.mockDiscussion, { status: 201 });
|
|
641
|
-
}),
|
|
642
|
-
http.get(`${baseUrl}/projects/*/merge_requests/*/discussions/*`, ({ request }) => {
|
|
643
|
-
if (request.url.includes(fixtures.mockDiscussion.id)) {
|
|
644
|
-
return HttpResponse.json(fixtures.mockDiscussion);
|
|
645
|
-
}
|
|
646
|
-
return HttpResponse.json({ message: 'Not Found' }, { status: 404 });
|
|
647
|
-
}),
|
|
648
|
-
http.delete(`${baseUrl}/projects/*/merge_requests/*/discussions/*`, ({ request }) => {
|
|
649
|
-
if (request.url.includes(fixtures.mockDiscussion.id)) {
|
|
650
|
-
return new HttpResponse(null, { status: 204 });
|
|
651
|
-
}
|
|
652
|
-
return HttpResponse.json({ message: 'Not Found' }, { status: 404 });
|
|
653
|
-
}),
|
|
654
|
-
http.put(`${baseUrl}/projects/*/merge_requests/*/discussions/*/resolve`, async ({ request }) => {
|
|
655
|
-
const url = request.url;
|
|
656
|
-
let resolved = true;
|
|
657
|
-
try {
|
|
658
|
-
const body = await request.json();
|
|
659
|
-
if (body.resolved === false) {
|
|
660
|
-
resolved = false;
|
|
661
|
-
}
|
|
662
|
-
}
|
|
663
|
-
catch (e) {
|
|
664
|
-
resolved = true;
|
|
665
|
-
}
|
|
666
|
-
if (url.includes(fixtures.mockDiscussion.id)) {
|
|
667
|
-
return HttpResponse.json({ ...fixtures.mockDiscussion, resolved });
|
|
668
|
-
}
|
|
669
|
-
return HttpResponse.json({ message: 'Not Found' }, { status: 404 });
|
|
670
|
-
}),
|
|
671
|
-
// Approvals
|
|
672
|
-
http.get(`${baseUrl}/projects/*/merge_requests/*/approvals`, () => {
|
|
673
|
-
return HttpResponse.json(fixtures.mockApproval);
|
|
674
|
-
}),
|
|
675
|
-
http.post(`${baseUrl}/projects/*/merge_requests/*/approve`, () => {
|
|
676
|
-
return HttpResponse.json({ ...fixtures.mockApproval, approvals_left: 0 }, { status: 201 });
|
|
677
|
-
}),
|
|
678
|
-
http.post(`${baseUrl}/projects/*/merge_requests/*/unapprove`, () => {
|
|
679
|
-
return HttpResponse.json(fixtures.mockApproval, { status: 201 });
|
|
680
|
-
}),
|
|
681
|
-
// Merge Request diffs and versions
|
|
682
|
-
http.get(`${baseUrl}/projects/*/merge_requests/*/changes`, ({ request }) => {
|
|
683
|
-
const decodedUrl = decodeURIComponent(request.url);
|
|
684
|
-
const match = decodedUrl.match(/merge_requests\/(\d+)\/changes/);
|
|
685
|
-
if (!match) {
|
|
686
|
-
return HttpResponse.json({ error: 'Invalid merge request IID' }, { status: 400 });
|
|
687
|
-
}
|
|
688
|
-
const mergeRequestIid = parseInt(match[1], 10);
|
|
689
|
-
if (isNaN(mergeRequestIid)) {
|
|
690
|
-
return HttpResponse.json({ error: 'Invalid merge request IID' }, { status: 400 });
|
|
691
|
-
}
|
|
692
|
-
if (mergeRequestIid === 1) {
|
|
693
|
-
return HttpResponse.json(fixtures.mockMergeRequestChanges);
|
|
694
|
-
}
|
|
695
|
-
return HttpResponse.json({ message: 'Not Found' }, { status: 404 });
|
|
696
|
-
}),
|
|
697
|
-
http.get(`${baseUrl}/projects/*/merge_requests/*/versions`, ({ request }) => {
|
|
698
|
-
const decodedUrl = decodeURIComponent(request.url);
|
|
699
|
-
const match = decodedUrl.match(/merge_requests\/(\d+)\/versions/);
|
|
700
|
-
if (!match) {
|
|
701
|
-
return HttpResponse.json({ error: 'Invalid merge request IID' }, { status: 400 });
|
|
702
|
-
}
|
|
703
|
-
const mergeRequestIid = parseInt(match[1], 10);
|
|
704
|
-
if (isNaN(mergeRequestIid)) {
|
|
705
|
-
return HttpResponse.json({ error: 'Invalid merge request IID' }, { status: 400 });
|
|
706
|
-
}
|
|
707
|
-
if (mergeRequestIid === 1) {
|
|
708
|
-
return HttpResponse.json(fixtures.mockMergeRequestVersions);
|
|
709
|
-
}
|
|
710
|
-
return HttpResponse.json({ message: 'Not Found' }, { status: 404 });
|
|
711
|
-
}),
|
|
712
|
-
http.get(`${baseUrl}/projects/*/merge_requests/*/versions/*`, ({ request }) => {
|
|
713
|
-
const decodedUrl = decodeURIComponent(request.url);
|
|
714
|
-
const mrMatch = decodedUrl.match(/merge_requests\/(\d+)\/versions/);
|
|
715
|
-
const versionMatch = decodedUrl.match(/versions\/(\d+)/);
|
|
716
|
-
if (!mrMatch || !versionMatch) {
|
|
717
|
-
return HttpResponse.json({ error: 'Invalid merge request version' }, { status: 400 });
|
|
718
|
-
}
|
|
719
|
-
const mergeRequestIid = parseInt(mrMatch[1], 10);
|
|
720
|
-
const versionId = parseInt(versionMatch[1], 10);
|
|
721
|
-
if (isNaN(mergeRequestIid) || isNaN(versionId)) {
|
|
722
|
-
return HttpResponse.json({ error: 'Invalid merge request version' }, { status: 400 });
|
|
723
|
-
}
|
|
724
|
-
if (mergeRequestIid === 1 && versionId === 101) {
|
|
725
|
-
return HttpResponse.json(fixtures.mockMergeRequestVersionDetails);
|
|
726
|
-
}
|
|
727
|
-
return HttpResponse.json({ message: 'Not Found' }, { status: 404 });
|
|
728
|
-
}),
|
|
729
|
-
// Attachment download endpoint (used by proxy download)
|
|
730
|
-
http.get(`${baseUrl}/uploads/attachments/note-2.txt`, () => {
|
|
731
|
-
const content = 'attachment log content\n';
|
|
732
|
-
return new HttpResponse(content, {
|
|
733
|
-
status: 200,
|
|
734
|
-
headers: {
|
|
735
|
-
'Content-Type': 'text/plain',
|
|
736
|
-
'Content-Length': String(Buffer.byteLength(content)),
|
|
737
|
-
},
|
|
738
|
-
});
|
|
739
|
-
}),
|
|
740
|
-
// Labels
|
|
741
|
-
http.get(`${baseUrl}/projects/*/labels`, () => {
|
|
742
|
-
return HttpResponse.json(fixtures.mockLabels);
|
|
743
|
-
}),
|
|
744
|
-
http.post(`${baseUrl}/projects/*/labels`, async ({ request }) => {
|
|
745
|
-
const body = await request.json();
|
|
746
|
-
if (!body.name || !body.color) {
|
|
747
|
-
return HttpResponse.json({ error: 'name and color required' }, { status: 400 });
|
|
748
|
-
}
|
|
749
|
-
return HttpResponse.json({ id: 3, name: body.name, color: body.color, description: body.description }, { status: 201 });
|
|
750
|
-
}),
|
|
751
|
-
http.delete(`${baseUrl}/projects/*/labels`, ({ request }) => {
|
|
752
|
-
const url = new URL(request.url);
|
|
753
|
-
if (!url.searchParams.get('name')) {
|
|
754
|
-
return HttpResponse.json({ error: 'name is required' }, { status: 400 });
|
|
755
|
-
}
|
|
756
|
-
return new HttpResponse(null, { status: 204 });
|
|
757
|
-
}),
|
|
758
|
-
// Milestones
|
|
759
|
-
http.get(`${baseUrl}/projects/*/milestones`, () => {
|
|
760
|
-
return HttpResponse.json(fixtures.mockMilestones);
|
|
761
|
-
}),
|
|
762
|
-
http.post(`${baseUrl}/projects/*/milestones`, async ({ request }) => {
|
|
763
|
-
const body = await request.json();
|
|
764
|
-
if (!body.title) {
|
|
765
|
-
return HttpResponse.json({ error: 'title is required' }, { status: 400 });
|
|
766
|
-
}
|
|
767
|
-
return HttpResponse.json({ ...fixtures.mockMilestones[0], id: 2, iid: 2, title: body.title }, { status: 201 });
|
|
768
|
-
}),
|
|
769
|
-
http.put(`${baseUrl}/projects/*/milestones/*`, async ({ request }) => {
|
|
770
|
-
const milestoneId = extractIidFromUrl(request.url);
|
|
771
|
-
if (milestoneId === null) {
|
|
772
|
-
return HttpResponse.json({ error: 'Invalid milestone ID' }, { status: 400 });
|
|
773
|
-
}
|
|
774
|
-
const body = await request.json();
|
|
775
|
-
if (milestoneId === 1) {
|
|
776
|
-
return HttpResponse.json({ ...fixtures.mockMilestones[0], ...body });
|
|
777
|
-
}
|
|
778
|
-
return HttpResponse.json({ message: 'Not Found' }, { status: 404 });
|
|
779
|
-
}),
|
|
780
|
-
// Releases
|
|
781
|
-
http.get(`${baseUrl}/projects/*/releases`, () => {
|
|
782
|
-
return HttpResponse.json(fixtures.mockReleases);
|
|
783
|
-
}),
|
|
784
|
-
http.post(`${baseUrl}/projects/*/releases`, async ({ request }) => {
|
|
785
|
-
const body = await request.json();
|
|
786
|
-
if (!body.tag_name || !body.name) {
|
|
787
|
-
return HttpResponse.json({ error: 'tag_name and name required' }, { status: 400 });
|
|
788
|
-
}
|
|
789
|
-
return HttpResponse.json({ ...fixtures.mockReleases[0], tag_name: body.tag_name, name: body.name, description: body.description }, { status: 201 });
|
|
790
|
-
}),
|
|
791
|
-
// Tags
|
|
792
|
-
http.get(`${baseUrl}/projects/*/repository/tags`, () => {
|
|
793
|
-
return HttpResponse.json(fixtures.mockTags);
|
|
794
|
-
}),
|
|
795
|
-
http.post(`${baseUrl}/projects/*/repository/tags`, async ({ request }) => {
|
|
796
|
-
const body = await request.json();
|
|
797
|
-
if (!body.tag_name || !body.ref) {
|
|
798
|
-
return HttpResponse.json({ error: 'tag_name and ref required' }, { status: 400 });
|
|
799
|
-
}
|
|
800
|
-
return HttpResponse.json({ name: body.tag_name, target: body.ref, message: '' }, { status: 201 });
|
|
801
|
-
}),
|
|
802
|
-
http.delete(`${baseUrl}/projects/*/repository/tags/*`, ({ request }) => {
|
|
803
|
-
const parts = request.url.split('/repository/tags/');
|
|
804
|
-
const tagName = decodeURIComponent(parts[1] || '');
|
|
805
|
-
if (!tagName) {
|
|
806
|
-
return HttpResponse.json({ error: 'Tag name required' }, { status: 400 });
|
|
807
|
-
}
|
|
808
|
-
return new HttpResponse(null, { status: 204 });
|
|
809
|
-
}),
|
|
810
|
-
// Members
|
|
811
|
-
http.get(`${baseUrl}/projects/*/members`, () => {
|
|
812
|
-
return HttpResponse.json(fixtures.mockMembers);
|
|
813
|
-
}),
|
|
814
|
-
http.post(`${baseUrl}/projects/*/members`, async ({ request }) => {
|
|
815
|
-
const body = await request.json();
|
|
816
|
-
if (!body.user_id || !body.access_level) {
|
|
817
|
-
return HttpResponse.json({ error: 'user_id and access_level required' }, { status: 400 });
|
|
818
|
-
}
|
|
819
|
-
const member = { id: body.user_id, username: `user${body.user_id}`, name: `User ${body.user_id}`, access_level: body.access_level };
|
|
820
|
-
return HttpResponse.json(member, { status: 201 });
|
|
821
|
-
}),
|
|
822
|
-
http.delete(`${baseUrl}/projects/*/members/*`, ({ request }) => {
|
|
823
|
-
const userId = extractIidFromUrl(request.url);
|
|
824
|
-
if (userId === null) {
|
|
825
|
-
return HttpResponse.json({ error: 'Invalid user ID' }, { status: 400 });
|
|
826
|
-
}
|
|
827
|
-
return new HttpResponse(null, { status: 204 });
|
|
828
|
-
}),
|
|
829
|
-
// Hooks
|
|
830
|
-
http.get(`${baseUrl}/projects/*/hooks`, () => {
|
|
831
|
-
return HttpResponse.json(fixtures.mockHooks);
|
|
832
|
-
}),
|
|
833
|
-
http.post(`${baseUrl}/projects/*/hooks`, async ({ request }) => {
|
|
834
|
-
const body = await request.json();
|
|
835
|
-
if (!body.url) {
|
|
836
|
-
return HttpResponse.json({ error: 'url required' }, { status: 400 });
|
|
837
|
-
}
|
|
838
|
-
return HttpResponse.json({ id: 2, url: body.url, push_events: !!body.push_events, issues_events: !!body.issues_events, merge_requests_events: !!body.merge_requests_events }, { status: 201 });
|
|
839
|
-
}),
|
|
840
|
-
http.delete(`${baseUrl}/projects/*/hooks/*`, ({ request }) => {
|
|
841
|
-
const hookId = extractIidFromUrl(request.url);
|
|
842
|
-
if (hookId === null) {
|
|
843
|
-
return HttpResponse.json({ error: 'Invalid hook ID' }, { status: 400 });
|
|
844
|
-
}
|
|
845
|
-
return new HttpResponse(null, { status: 204 });
|
|
846
|
-
}),
|
|
847
|
-
// Snippets
|
|
848
|
-
http.get(`${baseUrl}/projects/*/snippets`, ({ request }) => {
|
|
849
|
-
const url = new URL(request.url);
|
|
850
|
-
const projectPath = url.pathname.split('/projects/')[1]?.split('/snippets')[0] || '';
|
|
851
|
-
const snippets = fixtures.mockSnippets.map(snippet => ({
|
|
852
|
-
...snippet,
|
|
853
|
-
raw_url: `${baseUrl}/projects/${projectPath}/snippets/${snippet.id}/raw`,
|
|
854
|
-
}));
|
|
855
|
-
return HttpResponse.json(snippets);
|
|
856
|
-
}),
|
|
857
|
-
http.get(`${baseUrl}/projects/*/snippets/*/raw`, () => {
|
|
858
|
-
const content = fixtures.mockSnippetRawContent;
|
|
859
|
-
return new HttpResponse(content, {
|
|
860
|
-
status: 200,
|
|
861
|
-
headers: {
|
|
862
|
-
'Content-Type': 'text/plain',
|
|
863
|
-
'Content-Length': String(Buffer.byteLength(content)),
|
|
864
|
-
},
|
|
865
|
-
});
|
|
866
|
-
}),
|
|
867
|
-
http.get(`${baseUrl}/projects/*/snippets/*`, ({ request }) => {
|
|
868
|
-
const snippetId = extractIidFromUrl(request.url);
|
|
869
|
-
if (snippetId === 1) {
|
|
870
|
-
const url = new URL(request.url);
|
|
871
|
-
const projectPath = url.pathname.split('/projects/')[1]?.split('/snippets')[0] || '';
|
|
872
|
-
return HttpResponse.json({
|
|
873
|
-
...fixtures.mockSnippets[0],
|
|
874
|
-
raw_url: `${baseUrl}/projects/${projectPath}/snippets/${snippetId}/raw`,
|
|
875
|
-
});
|
|
876
|
-
}
|
|
877
|
-
return HttpResponse.json({ message: 'Not Found' }, { status: 404 });
|
|
878
|
-
}),
|
|
879
|
-
// Merge Requests (generic handlers after more specific /notes handlers)
|
|
880
|
-
http.get(`${baseUrl}/projects/*/merge_requests`, ({ request }) => {
|
|
881
|
-
const { page } = parsePaginationParams(request);
|
|
882
|
-
if (page === 1) {
|
|
883
|
-
return HttpResponse.json(fixtures.mockMergeRequestsList);
|
|
884
|
-
}
|
|
885
|
-
return HttpResponse.json([]);
|
|
886
|
-
}),
|
|
887
|
-
http.get(`${baseUrl}/projects/*/merge_requests/*`, ({ request }) => {
|
|
888
|
-
const mergeRequestIid = extractIidFromUrl(request.url);
|
|
889
|
-
if (mergeRequestIid === null) {
|
|
890
|
-
return HttpResponse.json({ error: 'Invalid merge request IID' }, { status: 400 });
|
|
891
|
-
}
|
|
892
|
-
if (mergeRequestIid === 1) {
|
|
893
|
-
return HttpResponse.json(fixtures.mockMergeRequest);
|
|
894
|
-
}
|
|
895
|
-
return HttpResponse.json({ message: 'Not Found' }, { status: 404 });
|
|
896
|
-
}),
|
|
897
|
-
http.post(`${baseUrl}/projects/*/merge_requests`, async ({ request }) => {
|
|
898
|
-
const body = await request.json();
|
|
899
|
-
if (!body.source_branch || !body.target_branch || !body.title) {
|
|
900
|
-
return HttpResponse.json({ error: 'source_branch, target_branch, and title are required' }, { status: 400 });
|
|
901
|
-
}
|
|
902
|
-
const createdMR = {
|
|
903
|
-
...fixtures.mockMergeRequest,
|
|
904
|
-
iid: 3,
|
|
905
|
-
id: 3,
|
|
906
|
-
title: body.title,
|
|
907
|
-
source_branch: body.source_branch,
|
|
908
|
-
target_branch: body.target_branch,
|
|
909
|
-
description: body.description,
|
|
910
|
-
web_url: 'https://gitlab.com/my-org/my-project/-/merge_requests/3',
|
|
911
|
-
};
|
|
912
|
-
return HttpResponse.json(createdMR, { status: 201 });
|
|
913
|
-
}),
|
|
914
|
-
http.put(`${baseUrl}/projects/*/merge_requests/*`, async ({ request }) => {
|
|
915
|
-
const mergeRequestIid = extractIidFromUrl(request.url);
|
|
916
|
-
if (mergeRequestIid === null) {
|
|
917
|
-
return HttpResponse.json({ error: 'Invalid merge request IID' }, { status: 400 });
|
|
918
|
-
}
|
|
919
|
-
if (mergeRequestIid === 1) {
|
|
920
|
-
const body = await request.json();
|
|
921
|
-
const updatedMR = {
|
|
922
|
-
...fixtures.mockMergeRequest,
|
|
923
|
-
title: body.title || fixtures.mockMergeRequest.title,
|
|
924
|
-
description: body.description !== undefined ? body.description : fixtures.mockMergeRequest.description,
|
|
925
|
-
state: body.state_event === 'close' ? 'closed' : fixtures.mockMergeRequest.state,
|
|
926
|
-
updated_at: new Date().toISOString(),
|
|
927
|
-
};
|
|
928
|
-
return HttpResponse.json(updatedMR, { status: 200 });
|
|
929
|
-
}
|
|
930
|
-
return HttpResponse.json({ message: 'Not Found' }, { status: 404 });
|
|
931
|
-
}),
|
|
932
|
-
http.delete(`${baseUrl}/projects/*/merge_requests/*`, ({ request }) => {
|
|
933
|
-
if (request.url.includes('/forbidden-project/')) {
|
|
934
|
-
return HttpResponse.json({ message: 'Forbidden', error: 'You do not have permission to delete this merge request' }, { status: 403 });
|
|
935
|
-
}
|
|
936
|
-
const mergeRequestIid = extractIidFromUrl(request.url);
|
|
937
|
-
if (mergeRequestIid === null) {
|
|
938
|
-
return HttpResponse.json({ error: 'Invalid merge request IID' }, { status: 400 });
|
|
939
|
-
}
|
|
940
|
-
if (mergeRequestIid === 1) {
|
|
941
|
-
return new HttpResponse(null, { status: 204 });
|
|
942
|
-
}
|
|
943
|
-
return HttpResponse.json({ message: 'Not Found' }, { status: 404 });
|
|
944
|
-
}),
|
|
945
|
-
// Issues
|
|
946
|
-
http.get(`${baseUrl}/projects/*/issues`, ({ request }) => {
|
|
947
|
-
const { page } = parsePaginationParams(request);
|
|
948
|
-
if (page === 1) {
|
|
949
|
-
return HttpResponse.json(fixtures.mockIssuesList);
|
|
950
|
-
}
|
|
951
|
-
return HttpResponse.json([]);
|
|
952
|
-
}),
|
|
953
|
-
http.get(`${baseUrl}/projects/*/issues/*`, ({ request }) => {
|
|
954
|
-
const issueIid = extractIidFromUrl(request.url);
|
|
955
|
-
if (issueIid === null) {
|
|
956
|
-
return HttpResponse.json({ error: 'Invalid issue IID' }, { status: 400 });
|
|
957
|
-
}
|
|
958
|
-
if (issueIid === 1) {
|
|
959
|
-
return HttpResponse.json(fixtures.mockIssue);
|
|
960
|
-
}
|
|
961
|
-
return HttpResponse.json({ message: 'Not Found' }, { status: 404 });
|
|
962
|
-
}),
|
|
963
|
-
http.post(`${baseUrl}/projects/*/issues`, async ({ request }) => {
|
|
964
|
-
const body = await request.json();
|
|
965
|
-
if (!body.title) {
|
|
966
|
-
return HttpResponse.json({ error: 'title is required' }, { status: 400 });
|
|
967
|
-
}
|
|
968
|
-
const createdIssue = {
|
|
969
|
-
...fixtures.mockIssue,
|
|
970
|
-
iid: 3,
|
|
971
|
-
id: 3,
|
|
972
|
-
title: body.title,
|
|
973
|
-
description: body.description || '',
|
|
974
|
-
state: 'opened',
|
|
975
|
-
web_url: 'https://gitlab.com/my-org/my-project/-/issues/3',
|
|
976
|
-
created_at: new Date().toISOString(),
|
|
977
|
-
};
|
|
978
|
-
return HttpResponse.json(createdIssue, { status: 201 });
|
|
979
|
-
}),
|
|
980
|
-
http.delete(`${baseUrl}/projects/*/issues/*`, ({ request }) => {
|
|
981
|
-
if (request.url.includes('/forbidden-project/')) {
|
|
982
|
-
return HttpResponse.json({ message: 'Forbidden', error: 'You do not have permission to delete this issue' }, { status: 403 });
|
|
983
|
-
}
|
|
984
|
-
const issueIid = extractIidFromUrl(request.url);
|
|
985
|
-
if (issueIid === null) {
|
|
986
|
-
return HttpResponse.json({ error: 'Invalid issue IID' }, { status: 400 });
|
|
987
|
-
}
|
|
988
|
-
if (issueIid === 1) {
|
|
989
|
-
return new HttpResponse(null, { status: 204 });
|
|
990
|
-
}
|
|
991
|
-
return HttpResponse.json({ message: 'Not Found' }, { status: 404 });
|
|
992
|
-
}),
|
|
993
|
-
// Server error simulation
|
|
994
|
-
http.get(`${baseUrl}/server-error-test`, () => {
|
|
995
|
-
return HttpResponse.json({ message: 'Internal Server Error' }, { status: 503 });
|
|
996
|
-
}),
|
|
997
|
-
];
|
|
998
|
-
}
|
|
999
|
-
/**
|
|
1000
|
-
* Create all handlers (GitLab API + OAuth) with configurable URLs
|
|
1001
|
-
*/
|
|
1002
|
-
export function createAllHandlers(gitlabBaseUrl = DEFAULT_BASE_URL, oauthConfig) {
|
|
1003
|
-
const gitlabHandlers = createGitLabHandlers(gitlabBaseUrl);
|
|
1004
|
-
const oauthHandlers = oauthConfig ? createOAuthHandlers(oauthConfig) : [];
|
|
1005
|
-
return [...oauthHandlers, ...gitlabHandlers];
|
|
1006
|
-
}
|
|
1007
|
-
// Legacy exports for backward compatibility with existing unit tests
|
|
1008
|
-
export const handlers = createGitLabHandlers(DEFAULT_BASE_URL);
|
|
1009
|
-
export const mockServer = setupServer(...handlers);
|
|
1010
|
-
export function startMockServer() {
|
|
1011
|
-
mockServer.listen({ onUnhandledRequest: 'error' });
|
|
1012
|
-
}
|
|
1013
|
-
export function resetMockServer() {
|
|
1014
|
-
mockServer.resetHandlers();
|
|
1015
|
-
}
|
|
1016
|
-
export function stopMockServer() {
|
|
1017
|
-
mockServer.close();
|
|
1018
|
-
}
|
|
1019
|
-
/**
|
|
1020
|
-
* Create a new MSW server instance with custom handlers
|
|
1021
|
-
*/
|
|
1022
|
-
export function createMockServer(gitlabBaseUrl = DEFAULT_BASE_URL, oauthConfig) {
|
|
1023
|
-
const allHandlers = createAllHandlers(gitlabBaseUrl, oauthConfig);
|
|
1024
|
-
return setupServer(...allHandlers);
|
|
1025
|
-
}
|
|
1026
|
-
//# sourceMappingURL=mock-gitlab-server.js.map
|