├── .devcontainer └── devcontainer.json ├── .flox ├── .gitignore ├── env.json └── env │ ├── manifest.lock │ └── manifest.toml ├── .gitattributes ├── .github ├── CODEOWNERS ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE │ ├── Bug_report.md │ ├── Feature_proposal.md │ ├── Meeting.md │ ├── Office_Hours.md │ ├── Project_proposal.md │ ├── Schema_change_proposal.md │ └── Support_question.md ├── labeler.yml ├── maven-cve-ignore-list.xml ├── node-cve-ignore-list.xml └── workflows │ ├── auto-comment.yml │ ├── build-calm-hub-coverage.yml │ ├── build-calm-hub-ui.yml │ ├── build-calm-hub.yml │ ├── build-cli.yml │ ├── build-docs.yml │ ├── build-shared.yml │ ├── cve-scanning-maven.yml │ ├── cve-scanning-node.yml │ ├── docker-publish-calm-hub.yml │ ├── license-scanning-maven.yml │ ├── license-scanning-node.yml │ ├── pr-labelling.yml │ ├── publish-cli-to-npm.yml │ ├── s3-docs-sync.yml │ ├── s3-sync.yml │ ├── s3-video-sync.yml │ ├── semgrep-ci.yml │ └── validate-spectral.yml ├── .gitignore ├── .mvn └── wrapper │ ├── MavenWrapperDownloader.java │ ├── maven-wrapper.jar │ └── maven-wrapper.properties ├── .vscode └── settings.json ├── LICENSE ├── LICENSE.spdx ├── NOTICE ├── README.md ├── brand ├── 2025_CALM_Logo.pdf ├── Horizontal │ ├── 2025_CALM_Horizontal.eps │ ├── 2025_CALM_Horizontal.jpg │ ├── 2025_CALM_Horizontal.png │ ├── 2025_CALM_Horizontal.svg │ ├── 2025_CALM_Horizontal_BLK.eps │ ├── 2025_CALM_Horizontal_BLK.jpg │ ├── 2025_CALM_Horizontal_BLK.png │ ├── 2025_CALM_Horizontal_BLK.svg │ ├── 2025_CALM_Horizontal_WHT.eps │ ├── 2025_CALM_Horizontal_WHT.png │ └── 2025_CALM_Horizontal_WHT.svg ├── Icon │ ├── 2025_CALM_Icon.eps │ ├── 2025_CALM_Icon.jpg │ ├── 2025_CALM_Icon.png │ ├── 2025_CALM_Icon.svg │ ├── 2025_CALM_Icon_BLK.eps │ ├── 2025_CALM_Icon_BLK.jpg │ ├── 2025_CALM_Icon_BLK.png │ ├── 2025_CALM_Icon_BLK.svg │ ├── 2025_CALM_Icon_WHT.eps │ ├── 2025_CALM_Icon_WHT.png │ └── 2025_CALM_Icon_WHT.svg ├── Stacked │ ├── 2025_CALM_Stacked.eps │ ├── 2025_CALM_Stacked.jpg │ ├── 2025_CALM_Stacked.png │ ├── 2025_CALM_Stacked.svg │ ├── 2025_CALM_Stacked_BLK.eps │ ├── 2025_CALM_Stacked_BLK.jpg │ ├── 2025_CALM_Stacked_BLK.png │ ├── 2025_CALM_Stacked_BLK.svg │ ├── 2025_CALM_Stacked_WHT.eps │ ├── 2025_CALM_Stacked_WHT.png │ └── 2025_CALM_Stacked_WHT.svg └── Wordmark │ ├── 2025_CALM_Wordmark.eps │ ├── 2025_CALM_Wordmark.jpg │ ├── 2025_CALM_Wordmark.png │ ├── 2025_CALM_Wordmark.svg │ ├── 2025_CALM_Wordmark_BLK.eps │ ├── 2025_CALM_Wordmark_BLK.jpg │ ├── 2025_CALM_Wordmark_BLK.png │ ├── 2025_CALM_Wordmark_BLK.svg │ ├── 2025_CALM_Wordmark_WHT.eps │ ├── 2025_CALM_Wordmark_WHT.png │ └── 2025_CALM_Wordmark_WHT.svg ├── calm-hub-ui ├── .gitignore ├── .prettierrc ├── README.md ├── eslint.config.js ├── index.html ├── package.json ├── postcss.config.cjs ├── prettier.config.js ├── src │ ├── App.css │ ├── App.tsx │ ├── ProtectedRoute.tsx │ ├── authService.tsx │ ├── components │ │ └── navbar │ │ │ ├── Navbar.css │ │ │ └── Navbar.tsx │ ├── hub │ │ ├── Hub.tsx │ │ └── components │ │ │ ├── json-renderer │ │ │ ├── JsonRenderer.test.tsx │ │ │ └── JsonRenderer.tsx │ │ │ └── value-table │ │ │ ├── ValueTable.test.tsx │ │ │ └── ValueTable.tsx │ ├── index.css │ ├── index.tsx │ ├── model │ │ └── calm.tsx │ ├── output.css │ ├── reportWebVitals.js │ ├── service │ │ └── calm-service.tsx │ └── visualizer │ │ ├── Visualizer.css │ │ ├── Visualizer.tsx │ │ ├── components │ │ ├── cytoscape-renderer │ │ │ ├── CytoscapeRenderer.tsx │ │ │ ├── cytoscape.css │ │ │ └── cytoscape.d.ts │ │ ├── drawer │ │ │ ├── Drawer.test.tsx │ │ │ └── Drawer.tsx │ │ ├── menu │ │ │ ├── Menu.test.tsx │ │ │ └── Menu.tsx │ │ ├── sidebar │ │ │ ├── Sidebar.test.tsx │ │ │ └── Sidebar.tsx │ │ └── visualizer-container │ │ │ ├── VisualizerContainer.test.tsx │ │ │ └── VisualizerContainer.tsx │ │ ├── contracts │ │ └── contracts.ts │ │ ├── helpers │ │ ├── set-functions.test.ts │ │ └── set-functions.ts │ │ └── services │ │ ├── layout-correction-service.test.ts │ │ ├── layout-correction-service.ts │ │ ├── node-position-service.test.tsx │ │ └── node-position-service.tsx ├── tsconfig.json ├── vite-env.d.ts ├── vite.config.ts └── vitest.setup.ts ├── calm-hub ├── .dockerignore ├── .gitignore ├── README.md ├── deploy │ └── docker-compose.yml ├── k8s │ ├── calm-hub.yml │ ├── mongo-config.yml │ └── mongo.yml ├── keycloak-dev │ ├── device-code-flow-entraid.sh │ ├── device-code-flow.sh │ ├── docker-compose.yml │ └── imports │ │ └── realm.json ├── local-dev │ └── docker-compose.yml ├── mongo │ └── init-mongo.js ├── nitrite │ └── init-nitrite.sh ├── pom.xml └── src │ ├── integration-test │ └── java │ │ └── integration │ │ ├── EndToEndResource.java │ │ ├── IntegrationTestProfile.java │ │ ├── IntegrationTestSecureProfile.java │ │ ├── KeycloakTestResource.java │ │ ├── MongoAdrIntegration.java │ │ ├── MongoArchitectureIntegration.java │ │ ├── MongoDomainIntegration.java │ │ ├── MongoFlowIntegration.java │ │ ├── MongoNamespaceIntegration.java │ │ ├── MongoPatternIntegration.java │ │ ├── MongoSchemaIntegration.java │ │ ├── MongoSetup.java │ │ ├── MongoStandardIntegration.java │ │ ├── PermittedScopesIntegration.java │ │ └── UserAccessGrantsIntegration.java │ ├── main │ ├── docker │ │ ├── Dockerfile.jvm │ │ └── Dockerfile.multistage │ ├── java │ │ └── org │ │ │ └── finos │ │ │ └── calm │ │ │ ├── config │ │ │ ├── NitriteDBConfig.java │ │ │ └── StandaloneQualifier.java │ │ │ ├── domain │ │ │ ├── Architecture.java │ │ │ ├── Domain.java │ │ │ ├── Flow.java │ │ │ ├── Pattern.java │ │ │ ├── Standard.java │ │ │ ├── StandardDetails.java │ │ │ ├── UserAccess.java │ │ │ ├── ValueWrapper.java │ │ │ ├── adr │ │ │ │ ├── Adr.java │ │ │ │ ├── AdrMeta.java │ │ │ │ ├── Decision.java │ │ │ │ ├── Link.java │ │ │ │ ├── NewAdrRequest.java │ │ │ │ ├── Option.java │ │ │ │ └── Status.java │ │ │ └── exception │ │ │ │ ├── AdrNotFoundException.java │ │ │ │ ├── AdrParseException.java │ │ │ │ ├── AdrPersistenceException.java │ │ │ │ ├── AdrRevisionNotFoundException.java │ │ │ │ ├── ArchitectureNotFoundException.java │ │ │ │ ├── ArchitectureVersionExistsException.java │ │ │ │ ├── ArchitectureVersionNotFoundException.java │ │ │ │ ├── DomainAlreadyExistsException.java │ │ │ │ ├── FlowNotFoundException.java │ │ │ │ ├── FlowVersionExistsException.java │ │ │ │ ├── FlowVersionNotFoundException.java │ │ │ │ ├── NamespaceNotFoundException.java │ │ │ │ ├── PatternNotFoundException.java │ │ │ │ ├── PatternVersionExistsException.java │ │ │ │ ├── PatternVersionNotFoundException.java │ │ │ │ ├── StandardNotFoundException.java │ │ │ │ ├── StandardVersionExistsException.java │ │ │ │ ├── StandardVersionNotFoundException.java │ │ │ │ └── UserAccessNotFoundException.java │ │ │ ├── resources │ │ │ ├── AdrResource.java │ │ │ ├── ArchitectureResource.java │ │ │ ├── CalmResourceErrorResponses.java │ │ │ ├── CoreSchemaResource.java │ │ │ ├── DomainSchemaResource.java │ │ │ ├── FlowResource.java │ │ │ ├── NamespaceResource.java │ │ │ ├── PatternResource.java │ │ │ ├── ResourceValidationConstants.java │ │ │ ├── StandardResource.java │ │ │ └── UserAccessResource.java │ │ │ ├── security │ │ │ ├── AccessControlFilter.java │ │ │ ├── CalmHubScopes.java │ │ │ ├── PermittedScopes.java │ │ │ ├── UserAccessValidator.java │ │ │ └── UserRequestAttributes.java │ │ │ └── store │ │ │ ├── AdrStore.java │ │ │ ├── ArchitectureStore.java │ │ │ ├── CoreSchemaStore.java │ │ │ ├── DomainStore.java │ │ │ ├── FlowStore.java │ │ │ ├── NamespaceStore.java │ │ │ ├── PatternStore.java │ │ │ ├── StandardStore.java │ │ │ ├── UserAccessStore.java │ │ │ ├── mongo │ │ │ ├── MongoAdrStore.java │ │ │ ├── MongoArchitectureStore.java │ │ │ ├── MongoCoreSchemaStore.java │ │ │ ├── MongoCounterStore.java │ │ │ ├── MongoDomainStore.java │ │ │ ├── MongoFlowStore.java │ │ │ ├── MongoNamespaceStore.java │ │ │ ├── MongoPatternStore.java │ │ │ ├── MongoStandardStore.java │ │ │ └── MongoUserAccessStore.java │ │ │ ├── nitrite │ │ │ ├── NitriteAdrStore.java │ │ │ ├── NitriteArchitectureStore.java │ │ │ ├── NitriteCoreSchemaStore.java │ │ │ ├── NitriteCounterStore.java │ │ │ ├── NitriteDomainStore.java │ │ │ ├── NitriteFlowStore.java │ │ │ ├── NitriteNamespaceStore.java │ │ │ ├── NitritePatternStore.java │ │ │ ├── NitriteStandardStore.java │ │ │ └── NitriteUserAccessStore.java │ │ │ └── producer │ │ │ ├── AdrStoreProducer.java │ │ │ ├── ArchitectureStoreProducer.java │ │ │ ├── CoreSchemaStoreProducer.java │ │ │ ├── DomainStoreProducer.java │ │ │ ├── FlowStoreProducer.java │ │ │ ├── NamespaceStoreProducer.java │ │ │ ├── PatternStoreProducer.java │ │ │ ├── StandardStoreProducer.java │ │ │ └── UserAccessStoreProducer.java │ └── resources │ │ ├── application-secure.properties │ │ └── application.properties │ └── test │ ├── java │ └── org │ │ └── finos │ │ └── calm │ │ ├── config │ │ └── NitriteDBConfigTest.java │ │ ├── domain │ │ ├── TestPattern.java │ │ ├── TestUserAccessShould.java │ │ └── adr │ │ │ ├── TestDecisionShould.java │ │ │ ├── TestLinkShould.java │ │ │ ├── TestNewAdrRequestShould.java │ │ │ └── TestOptionShould.java │ │ ├── resources │ │ ├── AllowPutProfile.java │ │ ├── TestAdrResourceShould.java │ │ ├── TestArchitectureResourcePutEnabledShould.java │ │ ├── TestArchitectureResourceShould.java │ │ ├── TestCalmErrorResponsesShould.java │ │ ├── TestCoreSchemaResourceShould.java │ │ ├── TestDomainSchemaResourceShould.java │ │ ├── TestFlowResourcePutEnabledShould.java │ │ ├── TestFlowResourceShould.java │ │ ├── TestNamespaceResourceShould.java │ │ ├── TestPatternResourcePutEnabledShould.java │ │ ├── TestPatternResourceShould.java │ │ ├── TestStandardResourceShould.java │ │ └── TestUserAccessResourceShould.java │ │ ├── security │ │ ├── TestAccessControlFilterShould.java │ │ ├── TestCalmHubScopesShould.java │ │ └── TestUserAccessValidatorShould.java │ │ └── store │ │ ├── mongo │ │ ├── TestMongoAdrStoreShould.java │ │ ├── TestMongoArchitectureStoreShould.java │ │ ├── TestMongoCoreSchemaStoreShould.java │ │ ├── TestMongoCounterStoreShould.java │ │ ├── TestMongoDomainStoreShould.java │ │ ├── TestMongoFlowStoreShould.java │ │ ├── TestMongoNamespaceStoreShould.java │ │ ├── TestMongoPatternStoreShould.java │ │ ├── TestMongoStandardStoreShould.java │ │ └── TestMongoUserAccessStoreShould.java │ │ ├── nitrite │ │ ├── TestNitriteAdrStoreShould.java │ │ ├── TestNitriteArchitectureStoreShould.java │ │ ├── TestNitriteCoreSchemaStoreShould.java │ │ ├── TestNitriteCounterStoreShould.java │ │ ├── TestNitriteDomainStoreShould.java │ │ ├── TestNitriteFlowStoreShould.java │ │ ├── TestNitriteNamespaceStoreShould.java │ │ ├── TestNitritePatternStoreShould.java │ │ ├── TestNitriteStandardStoreShould.java │ │ └── TestNitriteUserAccessStoreShould.java │ │ └── producer │ │ ├── TestAdrStoreProducerShould.java │ │ ├── TestArchitectureStoreProducerShould.java │ │ ├── TestCoreSchemaStoreProducerShould.java │ │ ├── TestDomainStoreProducerShould.java │ │ ├── TestFlowStoreProducerShould.java │ │ ├── TestNamespaceStoreProducerShould.java │ │ ├── TestPatternStoreProducerShould.java │ │ ├── TestStandardStoreProducerShould.java │ │ └── TestUserAccessStoreProducerShould.java │ └── resources │ ├── application.properties │ └── secure-profile │ └── realm.json ├── calm ├── README.md ├── control-example │ ├── one-node-wonder.json │ ├── pre-prod-review-configuration.json │ ├── pre-prod-review-evidence.json │ └── pre-prod-review-specification.json ├── controls │ └── README.md ├── domains-example │ ├── pattern │ │ ├── instantiation.json │ │ └── secure-application-pattern.json │ └── security │ │ ├── cluster-ingress-https.json │ │ ├── cluster-internal-mtls.json │ │ ├── cluster-micro-segmentation.json │ │ └── schema │ │ ├── micro-segmentation.json │ │ └── permitted-connection.json ├── draft │ ├── 1083 │ │ ├── README.md │ │ ├── meta │ │ │ ├── calm.json │ │ │ ├── core.json │ │ │ └── interface.json │ │ └── prototype │ │ │ ├── example-architecture.json │ │ │ └── interfaces │ │ │ ├── grpc-service.json │ │ │ └── kafka-topic.json │ ├── 1177 │ │ ├── README.md │ │ ├── meta │ │ │ └── core.json │ │ └── prototype │ │ │ ├── authentication-as-control.json │ │ │ ├── authentication-control-config.json │ │ │ └── authentication-control-requirement.json │ ├── 1224 │ │ ├── README.md │ │ ├── meta │ │ │ └── core.json │ │ └── prototype │ │ │ └── api-gateway.json │ ├── 1232 │ │ └── meta │ │ │ └── core.json │ ├── 1233 │ │ ├── README.md │ │ ├── meta │ │ │ └── control.json │ │ └── prototype │ │ │ ├── access-control-requirement.json │ │ │ ├── data-encryption-requirement.json │ │ │ ├── encryption-config.json │ │ │ ├── example-inline-config.json │ │ │ ├── example-mixed-config.json │ │ │ ├── example-url-config.json │ │ │ └── rbac-config.json │ ├── 2024-02 │ │ └── meta │ │ │ ├── calm.json │ │ │ └── core.json │ ├── 2024-03 │ │ └── meta │ │ │ ├── calm.json │ │ │ └── core.json │ ├── 2024-04 │ │ └── meta │ │ │ ├── calm.json │ │ │ ├── core.json │ │ │ └── interface.json │ ├── 2024-08 │ │ └── meta │ │ │ ├── calm.json │ │ │ ├── control-requirement.json │ │ │ ├── control.json │ │ │ ├── core.json │ │ │ ├── evidence.json │ │ │ └── interface.json │ ├── 2024-09 │ │ └── meta │ │ │ ├── calm.json │ │ │ ├── control-requirement.json │ │ │ ├── control.json │ │ │ ├── core.json │ │ │ ├── evidence.json │ │ │ └── interface.json │ ├── 2024-10 │ │ └── meta │ │ │ ├── calm.json │ │ │ ├── control-requirement.json │ │ │ ├── control.json │ │ │ ├── core.json │ │ │ ├── evidence.json │ │ │ ├── flow.json │ │ │ ├── interface.json │ │ │ └── units.json │ ├── 2024-12 │ │ └── meta │ │ │ ├── calm.json │ │ │ ├── control-requirement.json │ │ │ ├── control.json │ │ │ ├── core.json │ │ │ ├── evidence.json │ │ │ ├── flow.json │ │ │ ├── interface.json │ │ │ └── units.json │ ├── 2025-01 │ │ └── meta │ │ │ ├── calm.json │ │ │ ├── control-requirement.json │ │ │ ├── control.json │ │ │ ├── core.json │ │ │ ├── evidence.json │ │ │ ├── flow.json │ │ │ ├── interface.json │ │ │ └── units.json │ └── 2025-03 │ │ ├── meta │ │ ├── calm.json │ │ ├── control-requirement.json │ │ ├── control.json │ │ ├── core.json │ │ ├── evidence.json │ │ ├── flow.json │ │ ├── interface.json │ │ └── units.json │ │ ├── prototype │ │ ├── anyof │ │ │ ├── both-options.architecture.json │ │ │ ├── neither-option.architecture.json │ │ │ └── options-prototype.pattern.json │ │ ├── multiple-choices │ │ │ ├── architecture.json │ │ │ └── options-prototype.pattern.json │ │ ├── oneof │ │ │ ├── application-a.architecture.json │ │ │ ├── application-b.architecture.json │ │ │ └── options-prototype.pattern.json │ │ └── throughput-control-prototype.json │ │ └── samples │ │ └── traderx │ │ └── control-requirement │ │ ├── README.md │ │ ├── business_logic_and_process_control │ │ └── business-logic-enforcement-control-requirement.json │ │ ├── compliance_and_governance │ │ ├── access-review-control-requirements.json │ │ ├── approval-workflow-control-requirement.json │ │ ├── change-management-control-requirement.json │ │ ├── regulatory-compliance-control-requirement.json │ │ └── review-adjustments-control-requirement.json │ │ ├── data_integrity_and_retention │ │ ├── data-consistency-requirement.json │ │ ├── data-integrity-control-requirement.json │ │ ├── data-retention-control-requirement.json │ │ └── schema-validation-control-requirement.json │ │ ├── monitoring_and_observability │ │ ├── alerting-control-requirement.json │ │ ├── db-monitoring-control-requirement.json │ │ ├── health-check-requirement.json │ │ ├── logging-control-requirement.json │ │ ├── monitoring-control-requirement.json │ │ └── tracing-control-requirement.json │ │ ├── performance_and_scalability │ │ ├── latency-control-requirement.json │ │ ├── resource-utilization-control-requirement.json │ │ ├── scalability-control-requirement.json │ │ ├── throughput-control-requirement.json │ │ └── timeout-handling-control-requirement.json │ │ ├── resilience_and_risk_management │ │ ├── availability-control-requirement.json │ │ ├── disaster-recovery-control-requirement.json │ │ ├── error-handling-control-requirement.json │ │ ├── escalation-path-control-requirement.json │ │ ├── failover-redundancy-control-requirement.json │ │ ├── incident-response-control-requirement.json │ │ └── risk-management-control-requirement.json │ │ └── security_and_access_control │ │ ├── api-rate-limiting-control-requirement.json │ │ ├── audit-logging-control-requirement.json │ │ ├── authentication-control-requirement.json │ │ ├── authorization-control-requirement.json │ │ ├── data-encryption-control-requirement.json │ │ └── secrets-management-control-requirement.json ├── interfaces │ └── README.md ├── pattern │ └── api-gateway.json ├── pom.xml ├── release │ └── 1.0-rc1 │ │ ├── RELEASE_NOTES.md │ │ ├── meta │ │ ├── calm.json │ │ ├── control-requirement.json │ │ ├── control.json │ │ ├── core.json │ │ ├── evidence.json │ │ ├── flow.json │ │ ├── interface.json │ │ └── units.json │ │ └── prototype │ │ ├── access-control-requirement.json │ │ ├── adr-example.json │ │ ├── anyof │ │ ├── both-options.architecture.json │ │ ├── neither-option.architecture.json │ │ └── options-prototype.pattern.json │ │ ├── authentication-as-control.json │ │ ├── authentication-control-config.json │ │ ├── authentication-control-requirement.json │ │ ├── custom-interface-example.json │ │ ├── custom-node-type-example.json │ │ ├── data-encryption-requirement.json │ │ ├── example-inline-config.json │ │ ├── example-mixed-config.json │ │ ├── interfaces │ │ ├── grpc-service.json │ │ └── kafka-topic.json │ │ ├── multiple-choices │ │ ├── architecture.json │ │ └── options-prototype.pattern.json │ │ ├── oneof │ │ ├── application-a.architecture.json │ │ ├── application-b.architecture.json │ │ └── options-prototype.pattern.json │ │ ├── rbac-config.json │ │ └── throughput-control-prototype.json ├── samples │ ├── 2024-10 │ │ └── traderx │ │ │ ├── README.md │ │ │ ├── controls │ │ │ └── flow-sla-control-requirement.json │ │ │ ├── flows │ │ │ ├── add-update-account │ │ │ │ ├── add-update-account-control-configuration.json │ │ │ │ └── add-update-account.json │ │ │ ├── bootstrapping-position-blotter │ │ │ │ └── bootstrapping-position-blotter.json │ │ │ ├── load-list-of-accounts │ │ │ │ └── load-list-of-accounts.json │ │ │ ├── submit-trade-ticket │ │ │ │ ├── submit-trade-ticket-control-configuration.json │ │ │ │ └── submit-trade-ticket.json │ │ │ └── trade-processing │ │ │ │ ├── trade-processing-control-configuration.json │ │ │ │ ├── trade-processing-new-trade.json │ │ │ │ └── trade-processing-update-trade.json │ │ │ ├── traderx.json │ │ │ └── traderx.yaml │ ├── 2024-12 │ │ └── traderx │ │ │ ├── README.md │ │ │ ├── controls │ │ │ └── flow-sla-control-requirement.json │ │ │ ├── flows │ │ │ ├── add-update-account │ │ │ │ ├── add-update-account-control-configuration.json │ │ │ │ └── add-update-account.json │ │ │ ├── load-list-of-accounts │ │ │ │ └── load-list-of-accounts.json │ │ │ ├── load-positions │ │ │ │ └── load-positions.json │ │ │ ├── submit-trade-ticket │ │ │ │ ├── submit-trade-ticket-control-configuration.json │ │ │ │ └── submit-trade-ticket.json │ │ │ └── trade-processing │ │ │ │ ├── trade-processing-control-configuration.json │ │ │ │ ├── trade-processing-new-trade.json │ │ │ │ └── trade-processing-update-trade.json │ │ │ └── traderx.json │ ├── api-gateway-architecture.json │ ├── traderx │ │ └── traderx.layout.json │ └── visualization.png └── workshop │ ├── README.md │ ├── architecture │ ├── conference-secure-signup-amended.arch.json │ └── conference-signup.arch.json │ ├── cached │ └── cluster_start.sh │ ├── conference-secure-signup.pattern.json │ ├── conference-signup.pattern.json │ ├── controls │ ├── micro-segmentation.config.json │ ├── micro-segmentation.requirement.json │ ├── permitted-connection-http.config.json │ ├── permitted-connection-jdbc.config.json │ └── permitted-connection.requirement.json │ ├── directory.json │ ├── insecure-example │ ├── cluster │ │ └── cluster_start.sh │ └── kubernetes │ │ ├── application-deployment.yaml │ │ ├── application-service.yaml │ │ ├── database-deployment.yaml │ │ ├── database-service.yaml │ │ ├── kustomization.yaml │ │ └── namespace.yaml │ ├── secure-infra-template-bundle │ ├── application-deployment.yaml │ ├── application-service.yaml │ ├── calico-global-deny.yaml │ ├── cluster_start.hbs │ ├── database-deployment.yaml │ ├── database-service.yaml │ ├── index.json │ ├── infrastructure-transformer.js │ ├── kustomization.yaml │ ├── namespace.yaml │ ├── permit-app-from-db.yaml │ ├── permit-app-to-db.yaml │ └── permit-lb-to-app.yaml │ └── walkthrough.sh ├── cli ├── .gitignore ├── DEVELOPER_GUIDE.md ├── README.md ├── eslint.config.mjs ├── package.json ├── pom.xml ├── src │ ├── cli-config.spec.ts │ ├── cli-config.ts │ ├── cli.e2e.spec.ts │ ├── cli.spec.ts │ ├── cli.ts │ ├── command-helpers │ │ ├── calmhub-input.spec.ts │ │ ├── calmhub-input.ts │ │ ├── file-input.spec.ts │ │ ├── file-input.ts │ │ ├── generate-options.spec.ts │ │ ├── generate-options.ts │ │ ├── template.spec.ts │ │ ├── template.ts │ │ ├── validate.spec.ts │ │ └── validate.ts │ ├── index.ts │ └── server │ │ ├── cli-server.ts │ │ └── routes │ │ ├── health-route.spec.ts │ │ ├── health-route.ts │ │ ├── routes.spec.ts │ │ ├── routes.ts │ │ ├── validation-route.spec.ts │ │ └── validation-route.ts ├── test_fixtures │ ├── generate_output.json │ ├── template │ │ ├── expected-output │ │ │ └── cli-e2e-output.html │ │ ├── model │ │ │ ├── document-system.json │ │ │ ├── flows │ │ │ │ └── flow-document-upload.json │ │ │ └── url-to-file-directory.json │ │ └── template-bundles │ │ │ └── doc-system │ │ │ ├── basic-transformer.js │ │ │ ├── index.json │ │ │ └── main.html │ ├── validate_architecture_only_output.json │ ├── validate_output.json │ ├── validate_output_junit.xml │ ├── validate_output_pretty.txt │ └── validation_route │ │ ├── invalid_api_gateway_instantiation_missing_schema_key.json │ │ ├── invalid_api_gateway_instantiation_schema_points_to_missing_schema.json │ │ └── valid_instantiation.json ├── tsconfig.build.json ├── tsconfig.json ├── tsup.config.ts ├── vitest.config.ts └── watch.mjs ├── docs ├── .gitignore ├── DEVELOPER_GUIDE.md ├── README.md ├── babel.config.js ├── calm │ └── intro.md ├── community │ └── intro.md ├── docs │ ├── core-concepts │ │ ├── controls.md │ │ ├── index.md │ │ ├── interfaces.md │ │ ├── metadata.md │ │ ├── nodes.md │ │ └── relationships.md │ ├── index.md │ ├── introduction │ │ ├── index.md │ │ ├── key-features.md │ │ ├── what-is-calm.md │ │ └── why-use-calm.md │ └── working-with-calm │ │ ├── generate.md │ │ ├── index.md │ │ ├── installation.md │ │ ├── using-the-cli.md │ │ └── validate.md ├── docusaurus.config.js ├── package.json ├── pom.xml ├── sidebars.js ├── src │ ├── components │ │ └── HomepageFeatures │ │ │ ├── index.js │ │ │ └── styles.module.css │ ├── css │ │ └── custom.css │ └── pages │ │ ├── index.js.old │ │ ├── index.module.css │ │ └── markdown-page.md ├── static │ ├── .nojekyll │ └── img │ │ ├── favicon.ico │ │ ├── logo.png │ │ └── logo.svg └── talks │ └── index.md ├── mvnw ├── mvnw.cmd ├── package-lock.json ├── package.json ├── pom.xml ├── prettier.config.js ├── renovate.json ├── shared ├── README.md ├── eslint.config.mjs ├── package.json ├── pom.xml ├── scripts │ └── copy-templates.mjs ├── spectral-examples │ └── bad-rest-api.json ├── src │ ├── commands │ │ ├── generate │ │ │ ├── components │ │ │ │ ├── instantiate.spec.ts │ │ │ │ ├── instantiate.ts │ │ │ │ ├── options.spec.ts │ │ │ │ ├── options.ts │ │ │ │ ├── property.spec.ts │ │ │ │ └── property.ts │ │ │ ├── generate.e2e.spec.ts │ │ │ ├── generate.spec.ts │ │ │ └── generate.ts │ │ └── validate │ │ │ ├── output-formats │ │ │ ├── junit-output.spec.ts │ │ │ ├── junit-output.ts │ │ │ ├── pretty-output.spec.ts │ │ │ └── pretty-output.ts │ │ │ ├── spectral.result.ts │ │ │ ├── validate.spec.ts │ │ │ ├── validate.ts │ │ │ └── validation.output.ts │ ├── consts.ts │ ├── docify │ │ ├── docifier.e2e.spec.ts │ │ ├── docifier.spec.ts │ │ ├── docifier.ts │ │ ├── graphing │ │ │ ├── c4.spec.ts │ │ │ ├── c4.ts │ │ │ ├── relationship-graph.spec.ts │ │ │ └── relationship-graph.ts │ │ └── template-bundles │ │ │ └── docusaurus │ │ │ ├── c4-container.hbs │ │ │ ├── control-requirement.hbs │ │ │ ├── controls.hbs │ │ │ ├── custom.css │ │ │ ├── docusaurus-transformer.ts │ │ │ ├── docusaurus.config.js │ │ │ ├── flow-sequence.hbs │ │ │ ├── flow.mdx.hbs │ │ │ ├── index.json │ │ │ ├── index.md.hbs │ │ │ ├── json-doc.mdx.hbs │ │ │ ├── node.mdx.hbs │ │ │ ├── package.json │ │ │ ├── relationships.hbs │ │ │ ├── remark-replace-links.js │ │ │ ├── row-template.html │ │ │ ├── sidebar.js.hbs │ │ │ └── table-template.html │ ├── document-loader │ │ ├── calmhub-document-loader.spec.ts │ │ ├── calmhub-document-loader.ts │ │ ├── document-loader.spec.ts │ │ ├── document-loader.ts │ │ ├── file-system-document-loader.spec.ts │ │ └── file-system-document-loader.ts │ ├── index.ts │ ├── logger.ts │ ├── model │ │ ├── control-requirement.spec.ts │ │ ├── control-requirement.ts │ │ ├── control.spec.ts │ │ ├── control.ts │ │ ├── core.spec.ts │ │ ├── core.ts │ │ ├── flow.spec.ts │ │ ├── flow.ts │ │ ├── interface.spec.ts │ │ ├── interface.ts │ │ ├── metadata.spec.ts │ │ ├── metadata.ts │ │ ├── node.spec.ts │ │ ├── node.ts │ │ ├── relationship.spec.ts │ │ └── relationship.ts │ ├── parser │ │ ├── parser.e2e.spec.ts │ │ └── parser.ts │ ├── resolver │ │ ├── calm-reference-resolver.spec.ts │ │ └── calm-reference-resolver.ts │ ├── schema-directory.spec.ts │ ├── schema-directory.ts │ ├── spectral │ │ ├── examples │ │ │ └── bad-rest-api.json │ │ ├── functions │ │ │ ├── architecture │ │ │ │ ├── ids-are-unique.ts │ │ │ │ ├── interface-id-exists-on-node.ts │ │ │ │ ├── interface-id-exists.ts │ │ │ │ ├── node-has-relationship.ts │ │ │ │ └── node-id-exists.ts │ │ │ ├── helper-functions.ts │ │ │ └── pattern │ │ │ │ ├── ids-are-unique.ts │ │ │ │ ├── interface-id-exists-on-node.ts │ │ │ │ ├── interface-id-exists.ts │ │ │ │ ├── is-defined-in-oneof-or-anyof.ts │ │ │ │ ├── node-has-relationship.ts │ │ │ │ └── node-id-exists.ts │ │ ├── rules-architecture.ts │ │ └── rules-pattern.ts │ ├── template │ │ ├── template-bundle-file-loader.spec.ts │ │ ├── template-bundle-file-loader.ts │ │ ├── template-calm-file-dereferencer.e2e.spec.ts │ │ ├── template-calm-file-dereferencer.spec.ts │ │ ├── template-calm-file-dereferencer.ts │ │ ├── template-default-transfomer.spec.ts │ │ ├── template-default-transformer.ts │ │ ├── template-engine.spec.ts │ │ ├── template-engine.ts │ │ ├── template-processor.e2e.spec.ts │ │ ├── template-processor.spec.ts │ │ ├── template-processor.ts │ │ └── types.ts │ ├── test │ │ ├── mocks │ │ │ ├── handlers.ts │ │ │ └── server.ts │ │ └── setup-msw.ts │ ├── types.ts │ ├── types │ │ ├── control-requirement-types.ts │ │ ├── control-types.ts │ │ ├── core-types.ts │ │ ├── flow-types.ts │ │ ├── interface-types.ts │ │ ├── metadata-types.ts │ │ └── units-types.ts │ ├── util.spec.ts │ └── util.ts ├── test_fixtures │ ├── additional-props.json │ ├── api-gateway-implementation-that-does-not-match-schema.json │ ├── api-gateway-implementation-that-does-not-pass-the-spectral-validation.json │ ├── api-gateway-implementation.json │ ├── api-gateway-self-reference.json │ ├── api-gateway-with-no-relationships.json │ ├── api-gateway.json │ ├── bad-schema │ │ └── bad-json-schema.json │ ├── calm.json │ ├── calm │ │ ├── calm.json │ │ ├── core.json │ │ └── file-to-ignore.txt │ ├── command │ │ └── generate │ │ │ └── expected-output │ │ │ ├── conference-secure-signup.arch.json │ │ │ └── conference-signup.arch.json │ ├── core.json │ ├── markdown.md │ ├── schema-directory │ │ ├── missing-inner-ref.json │ │ ├── recursive.json │ │ ├── references.json │ │ └── relative-ref.json │ └── template │ │ ├── bundles │ │ ├── bad-transformer │ │ │ ├── bad-transformer.ts │ │ │ ├── index.json │ │ │ └── main.hbs │ │ ├── default-transformer │ │ │ ├── index.json │ │ │ └── main.hbs │ │ ├── dereferencing-transformer │ │ │ ├── deref-transformer.ts │ │ │ ├── index.json │ │ │ └── main.hbs │ │ ├── failing-transformer │ │ │ ├── failing-transformer.ts │ │ │ ├── index.json │ │ │ └── main.hbs │ │ ├── infrastructure-transformer │ │ │ ├── application-deployment.yaml │ │ │ ├── application-service.yaml │ │ │ ├── calico-global-deny.yaml │ │ │ ├── cluster_start.hbs │ │ │ ├── database-deployment.yaml │ │ │ ├── database-service.yaml │ │ │ ├── index.json │ │ │ ├── infrastructure-transformer.ts │ │ │ ├── kustomization.yaml │ │ │ ├── namespace.yaml │ │ │ ├── permit-app-from-db.yaml │ │ │ ├── permit-app-to-db.yaml │ │ │ └── permit-lb-to-app.yaml │ │ ├── js-transformer │ │ │ ├── index.json │ │ │ ├── main.hbs │ │ │ └── transformer.js │ │ ├── missing-transformer │ │ │ ├── index.json │ │ │ └── main.hbs │ │ ├── repeated │ │ │ ├── index.json │ │ │ ├── main.hbs │ │ │ └── repeated-transformer.ts │ │ ├── single-file │ │ │ ├── index.json │ │ │ ├── main.hbs │ │ │ └── simple-transformer.ts │ │ └── with-partials │ │ │ ├── footer.hbs │ │ │ ├── header.hbs │ │ │ ├── index.json │ │ │ ├── main.hbs │ │ │ └── with-partials-transformer.ts │ │ ├── data │ │ ├── conference-secure-signup.amended.arch.json │ │ ├── controls │ │ │ ├── architect.configuration.json │ │ │ ├── business-owner.configuration.json │ │ │ ├── owner-responsibility.requirement.json │ │ │ └── system-owner.configuration.json │ │ ├── document-system-with-controls.json │ │ ├── document-system.json │ │ ├── flow-document-upload.json │ │ └── simple-nodes.json │ │ └── expected-output │ │ ├── deref-output.html │ │ ├── doc-system-one-pager.md │ │ ├── js-transformer.txt │ │ ├── with-partials.txt │ │ └── workshop │ │ └── infrastructure │ │ ├── cluster │ │ ├── calico-global-deny.yaml │ │ └── cluster_start.sh │ │ └── kubernetes │ │ ├── application-deployment.yaml │ │ ├── application-service.yaml │ │ ├── database-deployment.yaml │ │ ├── database-service.yaml │ │ ├── kustomization.yaml │ │ ├── namespace.yaml │ │ ├── permit-app-from-db.yaml │ │ ├── permit-app-to-db.yaml │ │ └── permit-lb-to-app.yaml ├── tsconfig.build.json ├── tsconfig.json ├── tsup.config.ts └── vitest.config.ts ├── template-bundles └── control │ ├── control-requirement.hbs │ ├── control-transformer.ts │ └── index.json ├── tsconfig.base.json └── vitest-globals.d.ts /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "image": "mcr.microsoft.com/devcontainers/universal:3", 3 | "features": { 4 | "ghcr.io/devcontainers-contrib/features/maven-sdkman:2": { 5 | "version": "latest", 6 | "jdkVersion": "latest", 7 | "jdkDistro": "open" 8 | }, 9 | "ghcr.io/devcontainers-contrib/features/mvnd-sdkman:2": { 10 | "version": "latest", 11 | "jdkVersion": "latest", 12 | "jdkDistro": "open" 13 | } 14 | }, 15 | "containerEnv": { 16 | "JAVA_HOME": "/usr/local/sdkman/candidates/java/current" 17 | }, 18 | "postCreateCommand": "echo 'export JAVA_HOME=/usr/local/sdkman/candidates/java/current' >> ~/.bashrc", 19 | "hostRequirements": { 20 | "cpus": 4, 21 | "memory": "16gb" 22 | }, 23 | "customizations": { 24 | "vscode": { 25 | "extensions": [ 26 | "vscjava.vscode-java-pack" 27 | ] 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /.flox/.gitignore: -------------------------------------------------------------------------------- 1 | run/ 2 | cache/ 3 | lib/ 4 | log/ 5 | -------------------------------------------------------------------------------- /.flox/env.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "calm", 3 | "version": 1 4 | } -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.mov filter=lfs diff=lfs merge=lfs -text 2 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @finos/architecture-as-code-maintainers 2 | 3 | /calm/ @finos/calm-schema-governance 4 | 5 | /calm-hub/ @jpgough-ms @rocketstack-matt @grahampacker-ms @Thels 6 | 7 | /cli/ @aidanm3341 @lbulanti-ms @willosborne @grahampacker-ms @jpgough-ms @rocketstack-matt @Thels @LeighFinegold 8 | 9 | /calm-visualizer/ @oliviajanejohns @aidanm3341 @jpgough-ms @rocketstack-matt 10 | 11 | /shared/ @aidanm3341 @lbulanti-ms @willosborne @grahampacker-ms @jpgough-ms @rocketstack-matt @Thels @LeighFinegold 12 | 13 | # Ownership for dependency-related files at the root level 14 | /package.json @finos/architecture-as-code-maintainers 15 | /package-lock.json @finos/architecture-as-code-maintainers 16 | /pom.xml @finos/architecture-as-code-maintainers 17 | 18 | # Ownership for dependency-related files across all subdirectories 19 | **/package.json @finos/architecture-as-code-maintainers 20 | **/package-lock.json @finos/architecture-as-code-maintainers 21 | **/pom.xml @finos/architecture-as-code-maintainers 22 | -------------------------------------------------------------------------------- /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct for architecture-as-code 2 | 3 | Please see the [Community Code of Conduct](https://www.finos.org/code-of-conduct). 4 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/Bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 🐛 Bug Report 3 | about: If something isn't working as expected 🤔 4 | 5 | --- 6 | 7 | ## Bug Report 8 | 9 | ### Steps to Reproduce: 10 | 1. ...step 1 description... 11 | 2. ...step 2 description... 12 | 3. ...step 3 description... 13 | 14 | ### Expected Result: 15 | ...description of what you expected to see... 16 | 17 | ### Actual Result: 18 | ...what actually happened, including full exceptions (please include the entire stack trace, including "caused by" entries), log entries, screen shots etc. where appropriate... 19 | 20 | ### Environment: 21 | ...version and build of the project, OS and runtime versions, virtualised environment (if any), etc. ... 22 | 23 | ### Additional Context: 24 | ...add any other context about the problem here. If applicable, add screenshots to help explain... 25 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/Support_question.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 🤗 Support Question 3 | about: If you have a question about configuration, usage, etc. 💬 4 | 5 | --- 6 | 7 | ## Support Question 8 | 9 | ...ask your question here. 10 | 11 | ...be sure to search existing issues since someone might have already asked something similar. 12 | -------------------------------------------------------------------------------- /.github/labeler.yml: -------------------------------------------------------------------------------- 1 | cli: 2 | - "cli/**" 3 | shared: 4 | - "shared/**" 5 | docs: 6 | - "docs/**" 7 | calm-hub: 8 | - "calm-hub/**" 9 | calm: 10 | - "calm/**" 11 | calm-hub-ui: 12 | - "calm-hub-ui/**" 13 | config: 14 | - "pom.xml" 15 | - "**/package.json" 16 | - "**/package-lock.json" 17 | -------------------------------------------------------------------------------- /.github/maven-cve-ignore-list.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | .*quarkus-run\.jar 6 | 10.0 7 | 8 | 9 | -------------------------------------------------------------------------------- /.github/workflows/build-calm-hub-coverage.yml: -------------------------------------------------------------------------------- 1 | name: Build Calm Hub For Unit Test Coverage 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - "main" 7 | push: 8 | branches: 9 | - "main" 10 | 11 | jobs: 12 | build: 13 | name: Build Calm Hub 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | # Step 1: Checkout PR Branch 18 | - name: Checkout PR Branch 19 | uses: actions/checkout@v4 20 | 21 | # Step 2: Set up JDK 22 | - name: Set up JDK 23 | uses: actions/setup-java@v4 24 | with: 25 | distribution: 'temurin' 26 | java-version: '21' 27 | 28 | # Step 3: Cache Maven Dependencies 29 | - name: Cache Maven Dependencies 30 | uses: actions/cache@v4 31 | with: 32 | path: ~/.m2 33 | key: ${{ runner.os }}-m2-${{ hashFiles('calm-hub/pom.xml') }} 34 | restore-keys: | 35 | ${{ runner.os }}-m2- 36 | 37 | # Step 4: Build and Test 38 | - name: Build and Test 39 | working-directory: calm-hub 40 | run: mvn clean verify 41 | -------------------------------------------------------------------------------- /.github/workflows/build-calm-hub-ui.yml: -------------------------------------------------------------------------------- 1 | name: Build CALM Hub UI 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - "main" 7 | push: 8 | branches: 9 | - "main" 10 | 11 | jobs: 12 | shared: 13 | name: Build, Test, and Lint Shared Module 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - name: Checkout PR Branch 18 | uses: actions/checkout@v4 19 | 20 | - name: Setup Node.js 21 | uses: actions/setup-node@v4 22 | with: 23 | node-version: v22 24 | 25 | - name: Install workspace 26 | run: npm ci 27 | 28 | - name: Lint Shared Module 29 | run: npm run lint --workspace=calm-hub-ui 30 | 31 | - name: Build workspace 32 | run: npm run build --workspace=calm-hub-ui 33 | 34 | - name: Run tests for Shared 35 | run: npm run test --workspace=calm-hub-ui 36 | -------------------------------------------------------------------------------- /.github/workflows/build-cli.yml: -------------------------------------------------------------------------------- 1 | name: Build CLI 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - "main" 7 | push: 8 | branches: 9 | - "main" 10 | 11 | jobs: 12 | cli: 13 | name: Build, Test, and Lint CLI Module 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - name: Checkout PR Branch 18 | uses: actions/checkout@v4 19 | 20 | - name: Setup Node.js 21 | uses: actions/setup-node@v4 22 | with: 23 | node-version: v22 24 | 25 | - name: Install workspace 26 | run: npm ci 27 | 28 | - name: Lint CLI Module 29 | run: npm run lint --workspace=cli 30 | 31 | - name: Build workspace 32 | run: npm run build:cli 33 | 34 | - name: Run tests with coverage for CLI 35 | run: npm run test --workspace=cli 36 | -------------------------------------------------------------------------------- /.github/workflows/build-docs.yml: -------------------------------------------------------------------------------- 1 | name: Build Docs 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - "main" 7 | push: 8 | branches: 9 | - "main" 10 | 11 | jobs: 12 | docs: 13 | name: Build Docs Module 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - name: Checkout PR Branch 18 | uses: actions/checkout@v4 19 | 20 | - name: Setup Node.js 21 | uses: actions/setup-node@v4 22 | with: 23 | node-version: v22 24 | cache: npm 25 | cache-dependency-path: package-lock.json 26 | 27 | - name: Install workspace 28 | run: npm ci 29 | 30 | - name: Build Docs 31 | run: npm run build:docs 32 | -------------------------------------------------------------------------------- /.github/workflows/build-shared.yml: -------------------------------------------------------------------------------- 1 | name: Build Shared 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - "main" 7 | push: 8 | branches: 9 | - "main" 10 | 11 | jobs: 12 | shared: 13 | name: Build, Test, and Lint Shared Module 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - name: Checkout PR Branch 18 | uses: actions/checkout@v4 19 | 20 | - name: Setup Node.js 21 | uses: actions/setup-node@v4 22 | with: 23 | node-version: v22 24 | 25 | - name: Install workspace 26 | run: npm ci 27 | 28 | - name: Lint Shared Module 29 | run: npm run lint --workspace=shared 30 | 31 | - name: Build workspace 32 | run: npm run build --workspace=shared 33 | 34 | - name: Run tests with coverage for Shared 35 | run: npm run test --workspace=shared -------------------------------------------------------------------------------- /.github/workflows/license-scanning-node.yml: -------------------------------------------------------------------------------- 1 | name: License Scanning for Node.js 2 | 3 | on: 4 | schedule: 5 | - cron: '0 8,18 * * 1-5' 6 | push: 7 | paths: 8 | - '**/package-json.lock' 9 | - '**/package.json' 10 | - '.github/workflows/license-scanning-node.yml' 11 | 12 | jobs: 13 | scan: 14 | runs-on: ubuntu-latest 15 | strategy: 16 | matrix: 17 | node-version: ['22.x'] 18 | module-folder: ['cli', 'docs', 'shared'] 19 | steps: 20 | - uses: actions/checkout@v4 21 | - name: Use Node.js ${{ matrix.node-version }} 22 | uses: actions/setup-node@v4 23 | with: 24 | node-version: ${{ matrix.node-version }} 25 | - run: npm install --omit=dev 26 | working-directory: ${{ matrix.module-folder }} 27 | - run: npm install -g node-license-validator 28 | working-directory: ${{ matrix.module-folder }} 29 | - run: node-license-validator . --allow-licenses Apache-2.0 MIT BSD-2-Clause BSD BSD-3-Clause Unlicense ISC 30 | working-directory: ${{ matrix.module-folder }} 31 | -------------------------------------------------------------------------------- /.github/workflows/pr-labelling.yml: -------------------------------------------------------------------------------- 1 | name: PR Labeler 2 | 3 | on: 4 | pull_request_target: 5 | types: [opened, synchronize] 6 | 7 | jobs: 8 | label: 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | - name: Checkout repository 13 | uses: actions/checkout@v4 14 | 15 | - name: Label PR based on changes 16 | uses: actions/labeler@v4 17 | with: 18 | repo-token: "${{ secrets.GITHUB_TOKEN }}" -------------------------------------------------------------------------------- /.github/workflows/publish-cli-to-npm.yml: -------------------------------------------------------------------------------- 1 | name: Publish CLI to NPM 2 | 3 | on: 4 | release: 5 | types: [published] 6 | 7 | jobs: 8 | publish-cli: 9 | runs-on: ubuntu-latest 10 | steps: 11 | 12 | # Checkout the code 13 | - name: Checkout code 14 | uses: actions/checkout@v4 15 | 16 | # Set up Node.js 17 | - name: Setup Node.js 18 | uses: actions/setup-node@v4 19 | with: 20 | node-version: v22 21 | registry-url: "https://registry.npmjs.org" 22 | 23 | # Install the workspace 24 | - name: Install workspace 25 | run: npm ci 26 | 27 | # Build the entire workspace 28 | - name: Build workspace 29 | run: npm run build 30 | 31 | # Publish the CLI module 32 | - name: Publish CLI module 33 | run: npm publish --workspace cli 34 | env: 35 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 36 | -------------------------------------------------------------------------------- /.github/workflows/s3-docs-sync.yml: -------------------------------------------------------------------------------- 1 | name: Sync Docs to S3 2 | 3 | on: 4 | workflow_dispatch: {} 5 | push: 6 | branches: ["main"] 7 | paths: ["docs/**"] 8 | 9 | jobs: 10 | sync-to-s3: 11 | runs-on: ubuntu-latest 12 | defaults: 13 | run: 14 | working-directory: docs 15 | 16 | steps: 17 | - name: Checkout repository 18 | uses: actions/checkout@v4 19 | 20 | - uses: actions/setup-node@v4 21 | with: 22 | node-version: 22 23 | cache: npm 24 | cache-dependency-path: package-lock.json 25 | - name: Install dependencies 26 | run: npm ci 27 | - name: Build website 28 | run: npm run build 29 | 30 | - name: Configure AWS credentials 31 | uses: aws-actions/configure-aws-credentials@v4 32 | with: 33 | aws-access-key-id: ${{ secrets.AWS_S3_ACCESS_KEY }} 34 | aws-secret-access-key: ${{ secrets.AWS_S3_SECRET_ACCESS_KEY }} 35 | aws-region: us-east-1 36 | - name: Sync docs to S3 37 | run: | 38 | aws s3 sync build s3://calm.finos.org/ --delete --include "*" --exclude "draft/*" --exclude "samples/*" --exclude "video/*" 39 | -------------------------------------------------------------------------------- /.github/workflows/s3-sync.yml: -------------------------------------------------------------------------------- 1 | name: Sync CALM to S3 2 | 3 | on: 4 | workflow_dispatch: {} 5 | push: 6 | branches: 7 | - main 8 | paths: 9 | - 'calm/draft/**' 10 | - 'calm/samples/**' 11 | 12 | jobs: 13 | sync-to-s3: 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - name: Checkout repository 18 | uses: actions/checkout@v4 19 | - name: Configure AWS credentials 20 | uses: aws-actions/configure-aws-credentials@v4 21 | with: 22 | aws-access-key-id: ${{ secrets.AWS_S3_ACCESS_KEY }} 23 | aws-secret-access-key: ${{ secrets.AWS_S3_SECRET_ACCESS_KEY }} 24 | aws-region: us-east-1 25 | - name: Sync calm/draft folder to S3 26 | run: | 27 | aws s3 sync calm/draft s3://${{ vars.AWS_S3_BUCKET }}/draft --delete 28 | - name: Sync calm/samples folder to S3 29 | run: | 30 | aws s3 sync calm/samples s3://${{ vars.AWS_S3_BUCKET }}/samples --delete 31 | -------------------------------------------------------------------------------- /.github/workflows/s3-video-sync.yml: -------------------------------------------------------------------------------- 1 | name: Sync Videos to S3 2 | 3 | on: 4 | workflow_dispatch: {} 5 | push: 6 | branches: 7 | - main 8 | paths: 9 | - 'video' 10 | 11 | jobs: 12 | sync-to-s3: 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - name: Checkout repository 17 | uses: actions/checkout@v4 18 | - name: Configure AWS credentials 19 | uses: aws-actions/configure-aws-credentials@v4 20 | with: 21 | aws-access-key-id: ${{ secrets.AWS_S3_ACCESS_KEY }} 22 | aws-secret-access-key: ${{ secrets.AWS_S3_SECRET_ACCESS_KEY }} 23 | aws-region: us-east-1 24 | - name: Sync video folder to S3 25 | run: | 26 | aws s3 sync video s3://${{ vars.AWS_S3_BUCKET }}/video 27 | -------------------------------------------------------------------------------- /.github/workflows/semgrep-ci.yml: -------------------------------------------------------------------------------- 1 | name: Semgrep 2 | on: 3 | workflow_dispatch: {} 4 | pull_request: {} 5 | push: 6 | branches: 7 | - main 8 | paths: 9 | - .github/workflows/semgrep-ci.yml 10 | schedule: 11 | # random HH:MM to avoid a load spike on GitHub Actions at 00:00 12 | - cron: '09 15 * * *' 13 | jobs: 14 | semgrep: 15 | name: semgrep/ci 16 | runs-on: ubuntu-24.04 17 | env: 18 | SEMGREP_APP_TOKEN: ${{ secrets.SEMGREP_APP_TOKEN }} 19 | container: 20 | image: semgrep/semgrep 21 | if: (github.actor != 'dependabot[bot]') 22 | steps: 23 | - uses: actions/checkout@v4 24 | - run: semgrep ci 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | # Docusaurus generated folders 4 | website/translated_docs/ 5 | website/build/ 6 | website/i18n/ 7 | 8 | # Yarn build 9 | website/node_modules/ 10 | 11 | # Generated docs 12 | docs/contributing.md 13 | 14 | # We use YARN 15 | website/package-lock.json 16 | /.idea/ 17 | 18 | **/.vscode/ 19 | 20 | node_modules/ 21 | **/dist/ 22 | **/.vscode-test/ 23 | **/out/ 24 | **/dependency-check-report/ 25 | 26 | **/tsconfig.tsbuildinfo 27 | 28 | !/.mvn/wrapper/maven-wrapper.jar 29 | 30 | coverage/ -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/finos/architecture-as-code/a54518a8317eb889ea2541210c0297e30e9d880f/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | wrapperVersion=3.3.2 18 | distributionType=only-script 19 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip 20 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "testing.openTesting": "neverOpen", 3 | "jest.outputConfig": { 4 | "revealOn": "run", 5 | "revealWithFocus": "none", 6 | "clearOnRun": "none" 7 | }, 8 | "editor.formatOnSave": true, 9 | "editor.codeActionsOnSave": { 10 | "source.fixAll.eslint": "explicit" 11 | }, 12 | "java.compile.nullAnalysis.mode": "automatic", 13 | "java.configuration.updateBuildConfiguration": "automatic", 14 | "editor.defaultFormatter": "esbenp.prettier-vscode", 15 | "editor.formatOnPaste": false, 16 | "testing.automaticallyOpenTestResults": "neverOpen" 17 | } 18 | -------------------------------------------------------------------------------- /LICENSE.spdx: -------------------------------------------------------------------------------- 1 | SPDXVersion: SPDX-2.0 2 | DataLicense: CC0-1.0 3 | Creator: FINOS 4 | PackageName: architecture-as-code 5 | PackageOriginator: FINOS 6 | PackageHomePage: https://github.com/finos/architecture-as-code 7 | PackageLicenseDeclared: Apache-2.0 8 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | architecture-as-code - FINOS 2 | Copyright - 2023 FINOS info@finos.org 3 | 4 | This product includes software developed at the Fintech Open Source Foundation (https://www.finos.org/). 5 | 6 | -------------------------------------------------------------------------------- /brand/2025_CALM_Logo.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/finos/architecture-as-code/a54518a8317eb889ea2541210c0297e30e9d880f/brand/2025_CALM_Logo.pdf -------------------------------------------------------------------------------- /brand/Horizontal/2025_CALM_Horizontal.eps: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/finos/architecture-as-code/a54518a8317eb889ea2541210c0297e30e9d880f/brand/Horizontal/2025_CALM_Horizontal.eps -------------------------------------------------------------------------------- /brand/Horizontal/2025_CALM_Horizontal.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/finos/architecture-as-code/a54518a8317eb889ea2541210c0297e30e9d880f/brand/Horizontal/2025_CALM_Horizontal.jpg -------------------------------------------------------------------------------- /brand/Horizontal/2025_CALM_Horizontal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/finos/architecture-as-code/a54518a8317eb889ea2541210c0297e30e9d880f/brand/Horizontal/2025_CALM_Horizontal.png -------------------------------------------------------------------------------- /brand/Horizontal/2025_CALM_Horizontal_BLK.eps: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/finos/architecture-as-code/a54518a8317eb889ea2541210c0297e30e9d880f/brand/Horizontal/2025_CALM_Horizontal_BLK.eps -------------------------------------------------------------------------------- /brand/Horizontal/2025_CALM_Horizontal_BLK.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/finos/architecture-as-code/a54518a8317eb889ea2541210c0297e30e9d880f/brand/Horizontal/2025_CALM_Horizontal_BLK.jpg -------------------------------------------------------------------------------- /brand/Horizontal/2025_CALM_Horizontal_BLK.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/finos/architecture-as-code/a54518a8317eb889ea2541210c0297e30e9d880f/brand/Horizontal/2025_CALM_Horizontal_BLK.png -------------------------------------------------------------------------------- /brand/Horizontal/2025_CALM_Horizontal_WHT.eps: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/finos/architecture-as-code/a54518a8317eb889ea2541210c0297e30e9d880f/brand/Horizontal/2025_CALM_Horizontal_WHT.eps -------------------------------------------------------------------------------- /brand/Horizontal/2025_CALM_Horizontal_WHT.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/finos/architecture-as-code/a54518a8317eb889ea2541210c0297e30e9d880f/brand/Horizontal/2025_CALM_Horizontal_WHT.png -------------------------------------------------------------------------------- /brand/Icon/2025_CALM_Icon.eps: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/finos/architecture-as-code/a54518a8317eb889ea2541210c0297e30e9d880f/brand/Icon/2025_CALM_Icon.eps -------------------------------------------------------------------------------- /brand/Icon/2025_CALM_Icon.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/finos/architecture-as-code/a54518a8317eb889ea2541210c0297e30e9d880f/brand/Icon/2025_CALM_Icon.jpg -------------------------------------------------------------------------------- /brand/Icon/2025_CALM_Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/finos/architecture-as-code/a54518a8317eb889ea2541210c0297e30e9d880f/brand/Icon/2025_CALM_Icon.png -------------------------------------------------------------------------------- /brand/Icon/2025_CALM_Icon_BLK.eps: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/finos/architecture-as-code/a54518a8317eb889ea2541210c0297e30e9d880f/brand/Icon/2025_CALM_Icon_BLK.eps -------------------------------------------------------------------------------- /brand/Icon/2025_CALM_Icon_BLK.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/finos/architecture-as-code/a54518a8317eb889ea2541210c0297e30e9d880f/brand/Icon/2025_CALM_Icon_BLK.jpg -------------------------------------------------------------------------------- /brand/Icon/2025_CALM_Icon_BLK.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/finos/architecture-as-code/a54518a8317eb889ea2541210c0297e30e9d880f/brand/Icon/2025_CALM_Icon_BLK.png -------------------------------------------------------------------------------- /brand/Icon/2025_CALM_Icon_WHT.eps: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/finos/architecture-as-code/a54518a8317eb889ea2541210c0297e30e9d880f/brand/Icon/2025_CALM_Icon_WHT.eps -------------------------------------------------------------------------------- /brand/Icon/2025_CALM_Icon_WHT.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/finos/architecture-as-code/a54518a8317eb889ea2541210c0297e30e9d880f/brand/Icon/2025_CALM_Icon_WHT.png -------------------------------------------------------------------------------- /brand/Stacked/2025_CALM_Stacked.eps: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/finos/architecture-as-code/a54518a8317eb889ea2541210c0297e30e9d880f/brand/Stacked/2025_CALM_Stacked.eps -------------------------------------------------------------------------------- /brand/Stacked/2025_CALM_Stacked.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/finos/architecture-as-code/a54518a8317eb889ea2541210c0297e30e9d880f/brand/Stacked/2025_CALM_Stacked.jpg -------------------------------------------------------------------------------- /brand/Stacked/2025_CALM_Stacked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/finos/architecture-as-code/a54518a8317eb889ea2541210c0297e30e9d880f/brand/Stacked/2025_CALM_Stacked.png -------------------------------------------------------------------------------- /brand/Stacked/2025_CALM_Stacked_BLK.eps: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/finos/architecture-as-code/a54518a8317eb889ea2541210c0297e30e9d880f/brand/Stacked/2025_CALM_Stacked_BLK.eps -------------------------------------------------------------------------------- /brand/Stacked/2025_CALM_Stacked_BLK.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/finos/architecture-as-code/a54518a8317eb889ea2541210c0297e30e9d880f/brand/Stacked/2025_CALM_Stacked_BLK.jpg -------------------------------------------------------------------------------- /brand/Stacked/2025_CALM_Stacked_BLK.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/finos/architecture-as-code/a54518a8317eb889ea2541210c0297e30e9d880f/brand/Stacked/2025_CALM_Stacked_BLK.png -------------------------------------------------------------------------------- /brand/Stacked/2025_CALM_Stacked_WHT.eps: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/finos/architecture-as-code/a54518a8317eb889ea2541210c0297e30e9d880f/brand/Stacked/2025_CALM_Stacked_WHT.eps -------------------------------------------------------------------------------- /brand/Stacked/2025_CALM_Stacked_WHT.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/finos/architecture-as-code/a54518a8317eb889ea2541210c0297e30e9d880f/brand/Stacked/2025_CALM_Stacked_WHT.png -------------------------------------------------------------------------------- /brand/Wordmark/2025_CALM_Wordmark.eps: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/finos/architecture-as-code/a54518a8317eb889ea2541210c0297e30e9d880f/brand/Wordmark/2025_CALM_Wordmark.eps -------------------------------------------------------------------------------- /brand/Wordmark/2025_CALM_Wordmark.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/finos/architecture-as-code/a54518a8317eb889ea2541210c0297e30e9d880f/brand/Wordmark/2025_CALM_Wordmark.jpg -------------------------------------------------------------------------------- /brand/Wordmark/2025_CALM_Wordmark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/finos/architecture-as-code/a54518a8317eb889ea2541210c0297e30e9d880f/brand/Wordmark/2025_CALM_Wordmark.png -------------------------------------------------------------------------------- /brand/Wordmark/2025_CALM_Wordmark_BLK.eps: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/finos/architecture-as-code/a54518a8317eb889ea2541210c0297e30e9d880f/brand/Wordmark/2025_CALM_Wordmark_BLK.eps -------------------------------------------------------------------------------- /brand/Wordmark/2025_CALM_Wordmark_BLK.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/finos/architecture-as-code/a54518a8317eb889ea2541210c0297e30e9d880f/brand/Wordmark/2025_CALM_Wordmark_BLK.jpg -------------------------------------------------------------------------------- /brand/Wordmark/2025_CALM_Wordmark_BLK.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/finos/architecture-as-code/a54518a8317eb889ea2541210c0297e30e9d880f/brand/Wordmark/2025_CALM_Wordmark_BLK.png -------------------------------------------------------------------------------- /brand/Wordmark/2025_CALM_Wordmark_WHT.eps: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/finos/architecture-as-code/a54518a8317eb889ea2541210c0297e30e9d880f/brand/Wordmark/2025_CALM_Wordmark_WHT.eps -------------------------------------------------------------------------------- /brand/Wordmark/2025_CALM_Wordmark_WHT.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/finos/architecture-as-code/a54518a8317eb889ea2541210c0297e30e9d880f/brand/Wordmark/2025_CALM_Wordmark_WHT.png -------------------------------------------------------------------------------- /calm-hub-ui/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | /public/brand 14 | 15 | # misc 16 | .DS_Store 17 | .env.local 18 | .env.development.local 19 | .env.test.local 20 | .env.production.local 21 | 22 | npm-debug.log* 23 | yarn-debug.log* 24 | yarn-error.log* 25 | -------------------------------------------------------------------------------- /calm-hub-ui/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "es5", 3 | "tabWidth": 4, 4 | "semi": true, 5 | "singleQuote": true, 6 | "bracketSpacing": true, 7 | "endOfLine": "auto", 8 | "printWidth": 100 9 | } 10 | -------------------------------------------------------------------------------- /calm-hub-ui/eslint.config.js: -------------------------------------------------------------------------------- 1 | import js from '@eslint/js'; 2 | import globals from 'globals'; 3 | import reactHooks from 'eslint-plugin-react-hooks'; 4 | import reactRefresh from 'eslint-plugin-react-refresh'; 5 | import tseslint from 'typescript-eslint'; 6 | 7 | export default tseslint.config( 8 | { ignores: ['dist'] }, 9 | { 10 | extends: [js.configs.recommended, ...tseslint.configs.recommended], 11 | files: ['**/*.{ts,tsx}'], 12 | languageOptions: { 13 | ecmaVersion: 2020, 14 | globals: globals.browser, 15 | }, 16 | plugins: { 17 | 'react-hooks': reactHooks, 18 | 'react-refresh': reactRefresh, 19 | }, 20 | rules: { 21 | ...reactHooks.configs.recommended.rules, 22 | 'react-refresh/only-export-components': ['warn', { allowConstantExport: true }], 23 | }, 24 | } 25 | ); 26 | -------------------------------------------------------------------------------- /calm-hub-ui/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | CALM 9 | 10 | 11 |
12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /calm-hub-ui/postcss.config.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: {}, 3 | }; 4 | -------------------------------------------------------------------------------- /calm-hub-ui/prettier.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @see https://prettier.io/docs/en/configuration.html 3 | * @type {import("prettier").Config} 4 | */ 5 | const config = { 6 | trailingComma: 'es5', 7 | tabWidth: 4, 8 | semi: true, 9 | singleQuote: true, 10 | }; 11 | 12 | module.exports = config; 13 | -------------------------------------------------------------------------------- /calm-hub-ui/src/App.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/finos/architecture-as-code/a54518a8317eb889ea2541210c0297e30e9d880f/calm-hub-ui/src/App.css -------------------------------------------------------------------------------- /calm-hub-ui/src/App.tsx: -------------------------------------------------------------------------------- 1 | import { HashRouter as Router, Route, Routes } from 'react-router-dom'; 2 | import Hub from './hub/Hub.js'; 3 | import Visualizer from './visualizer/Visualizer.js'; 4 | 5 | function App() { 6 | return ( 7 | 8 | 9 | } /> 10 | } /> 11 | 12 | 13 | ); 14 | } 15 | 16 | export default App; 17 | -------------------------------------------------------------------------------- /calm-hub-ui/src/components/navbar/Navbar.css: -------------------------------------------------------------------------------- 1 | .logo { 2 | max-width: unset; 3 | height: inherit; 4 | } 5 | -------------------------------------------------------------------------------- /calm-hub-ui/src/hub/components/value-table/ValueTable.tsx: -------------------------------------------------------------------------------- 1 | interface ValueTableProps { 2 | header: string; 3 | values: string[]; 4 | callback: (value: string) => void; 5 | currentValue: string | undefined; 6 | } 7 | 8 | export function ValueTable({ header, values, callback, currentValue }: ValueTableProps) { 9 | return ( 10 |
11 |
12 | {header} 13 |
14 |
15 |
16 | {values.map((value) => { 17 | let styles = 'h-fit p-5 hover:bg-gray-50'; 18 | 19 | if (currentValue === value) { 20 | styles = styles + ' bg-[#eee]'; 21 | } 22 | 23 | return ( 24 |
callback(value)}> 25 | {value} 26 |
27 | ); 28 | })} 29 |
30 |
31 | ); 32 | } 33 | -------------------------------------------------------------------------------- /calm-hub-ui/src/index.css: -------------------------------------------------------------------------------- 1 | @import 'tailwindcss'; 2 | @plugin "daisyui" { 3 | themes: light --default; 4 | } 5 | 6 | [data-theme='light'] { 7 | --color-primary: #000063; 8 | --color-accent: #007dff; 9 | } 10 | 11 | #root { 12 | height: 100vh; 13 | } 14 | 15 | body { 16 | margin: 0; 17 | font-family: 18 | -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 19 | 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; 20 | -webkit-font-smoothing: antialiased; 21 | -moz-osx-font-smoothing: grayscale; 22 | } 23 | 24 | code { 25 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace; 26 | } 27 | -------------------------------------------------------------------------------- /calm-hub-ui/src/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import ProtectedRoute from './ProtectedRoute.js'; 4 | import './index.css'; 5 | import { authService } from './authService.js'; 6 | import App from './App.js'; 7 | 8 | const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement); 9 | 10 | const LogoutButton: React.FC = () => { 11 | const handleLogout = async () => { 12 | await authService.logout(); 13 | }; 14 | 15 | return ( 16 | 19 | ); 20 | }; 21 | 22 | const isHttps = window.location.protocol === 'https:'; 23 | 24 | root.render( 25 | 26 | {isHttps ? ( 27 | 28 | 29 | 30 | 31 | ) : ( 32 | 33 | )} 34 | 35 | ); 36 | -------------------------------------------------------------------------------- /calm-hub-ui/src/model/calm.tsx: -------------------------------------------------------------------------------- 1 | export type Namespace = string; 2 | export type PatternID = string; 3 | export type Pattern = string; 4 | export type Architecture = string; 5 | export type ArchitectureID = string; 6 | export type FlowID = string; 7 | export type Flow = string; 8 | export type Version = string; 9 | export type CalmType = 'Architectures' | 'Patterns' | 'Flows'; 10 | 11 | export type Data = { 12 | id: string; 13 | version: string; 14 | name: Namespace; 15 | calmType: CalmType; 16 | data: Pattern | Architecture | Flow | undefined; 17 | }; 18 | -------------------------------------------------------------------------------- /calm-hub-ui/src/output.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/finos/architecture-as-code/a54518a8317eb889ea2541210c0297e30e9d880f/calm-hub-ui/src/output.css -------------------------------------------------------------------------------- /calm-hub-ui/src/reportWebVitals.js: -------------------------------------------------------------------------------- 1 | const reportWebVitals = (onPerfEntry) => { 2 | if (onPerfEntry && onPerfEntry instanceof Function) { 3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 4 | getCLS(onPerfEntry); 5 | getFID(onPerfEntry); 6 | getFCP(onPerfEntry); 7 | getLCP(onPerfEntry); 8 | getTTFB(onPerfEntry); 9 | }); 10 | } 11 | }; 12 | 13 | export default reportWebVitals; 14 | -------------------------------------------------------------------------------- /calm-hub-ui/src/visualizer/Visualizer.css: -------------------------------------------------------------------------------- 1 | .node { 2 | padding: 2rem; 3 | border: 2px black solid; 4 | position: absolute; 5 | } 6 | 7 | .container { 8 | position: relative; 9 | display: flex; 10 | justify-content: center; 11 | } 12 | 13 | .z-1 { 14 | z-index: 1; 15 | } 16 | -------------------------------------------------------------------------------- /calm-hub-ui/src/visualizer/components/cytoscape-renderer/cytoscape.css: -------------------------------------------------------------------------------- 1 | .node { 2 | border: 1px solid; 3 | text-align: center; 4 | width: 200px; 5 | font-family: Arial; 6 | padding: 5px 10px 5px 10px; 7 | background-color: white; 8 | } 9 | 10 | .selected-node { 11 | background-color: #cce1f9; 12 | } 13 | 14 | :focus { 15 | border: 1px solid; 16 | border-color: blue; 17 | } 18 | 19 | .graph-title { 20 | z-index: 1; 21 | padding: var(--navbar-padding, 0.5rem); 22 | } 23 | 24 | .element { 25 | display: flex; 26 | flex-direction: column; 27 | align-items: center; 28 | margin-left: -100px; 29 | } 30 | 31 | .title { 32 | font-weight: bold; 33 | color: black; 34 | } 35 | 36 | .type { 37 | margin-top: 10px; 38 | color: grey; 39 | } 40 | 41 | .description { 42 | margin-top: 10px; 43 | color: black; 44 | } 45 | -------------------------------------------------------------------------------- /calm-hub-ui/src/visualizer/components/cytoscape-renderer/cytoscape.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'cytoscape-node-html-label'; 2 | declare module 'cytoscape-cose-bilkent'; 3 | declare module 'cytoscape-expand-collapse'; 4 | declare module 'cytoscape-node-edge-html-label'; 5 | -------------------------------------------------------------------------------- /calm-hub-ui/src/visualizer/components/drawer/Drawer.test.tsx: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from 'vitest'; 2 | import { render, screen } from '@testing-library/react'; 3 | import { Drawer } from './Drawer.js'; 4 | 5 | describe('Drawer', () => { 6 | it('should render Drawer', () => { 7 | render( 8 | 14 | ); 15 | expect(screen.getByText('No file selected')).toBeInTheDocument(); 16 | }); 17 | 18 | it('should render Drawer', () => { 19 | render( 20 | 26 | ); 27 | expect(screen.getByText('No file selected')).toBeInTheDocument(); 28 | expect(screen.getByRole('checkbox', { name: 'drawer-toggle' })).not.toBeChecked(); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /calm-hub-ui/src/visualizer/helpers/set-functions.ts: -------------------------------------------------------------------------------- 1 | // Required because sometimes the set API of Typescript is not supported. 2 | 3 | /** 4 | * @param set1 5 | * @param set2 6 | * @returns union of set1 and set2. 7 | */ 8 | export function union(set1: Set, set2: Set): Set { 9 | return new Set([...set1, ...set2]); 10 | } 11 | 12 | /** 13 | * 14 | * @param set1 15 | * @param set2 16 | * @returns intersection between set1 and set2. 17 | */ 18 | export function intersection(set1: Set, set2: Set): Set { 19 | return new Set([...set1].filter((item) => set2.has(item))); 20 | } 21 | 22 | /** 23 | * 24 | * @param set1 25 | * @param set2 26 | * @returns set1 - set2 i.e. elements in set1 that are not in set2. 27 | */ 28 | export function difference(set1: Set, set2: Set): Set { 29 | return new Set([...set1].filter((item) => !set2.has(item))); 30 | } 31 | -------------------------------------------------------------------------------- /calm-hub-ui/src/visualizer/services/node-position-service.tsx: -------------------------------------------------------------------------------- 1 | export interface StoredNodePosition { 2 | id: string; 3 | position: { x: number; y: number }; 4 | } 5 | 6 | export function saveNodePositions(title: string, positions: StoredNodePosition[]) { 7 | try { 8 | localStorage.setItem(title, JSON.stringify(positions)); 9 | } catch (err) { 10 | console.error('Failed to save node positions:', err); 11 | } 12 | } 13 | 14 | export function loadStoredNodePositions(title: string): StoredNodePosition[] | null { 15 | try { 16 | const data = localStorage.getItem(title); 17 | return data ? JSON.parse(data) : null; 18 | } catch (err) { 19 | console.error('Failed to load node positions:', err); 20 | return null; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /calm-hub-ui/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /calm-hub-ui/vite.config.ts: -------------------------------------------------------------------------------- 1 | /// 2 | import { defineConfig } from 'vitest/config'; 3 | import react from '@vitejs/plugin-react'; 4 | import tailwindcss from '@tailwindcss/vite'; 5 | 6 | // https://vitejs.dev/config/ 7 | export default defineConfig({ 8 | plugins: [react(), tailwindcss()], 9 | test: { 10 | globals: true, 11 | environment: 'jsdom', 12 | environmentMatchGlobs: [['./src/**/*.tsx', 'jsdom']], 13 | setupFiles: ['./vitest.setup.ts'], 14 | }, 15 | build: { 16 | outDir: 'build', 17 | }, 18 | server: { 19 | proxy: { 20 | '/calm': 'http://localhost:8080', 21 | }, 22 | }, 23 | }); 24 | -------------------------------------------------------------------------------- /calm-hub-ui/vitest.setup.ts: -------------------------------------------------------------------------------- 1 | import '@testing-library/jest-dom/vitest'; 2 | import { afterEach } from 'vitest'; 3 | import { cleanup } from '@testing-library/react'; 4 | 5 | // runs a clean after each test case (e.g. clearing jsdom) 6 | afterEach(() => { 7 | cleanup(); 8 | }); 9 | -------------------------------------------------------------------------------- /calm-hub/.dockerignore: -------------------------------------------------------------------------------- 1 | # Ignore everything 2 | * 3 | 4 | # Allow the Maven Wrapper and build files 5 | !pom.xml 6 | !.mvn/ 7 | !mvnw 8 | !src 9 | 10 | # Allow the target directory for the final build 11 | !target/*-runner 12 | !target/*-runner.jar 13 | !target/lib/* 14 | !target/quarkus-app/* 15 | -------------------------------------------------------------------------------- /calm-hub/deploy/docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | mongodb: 3 | image: mongo:latest 4 | container_name: calm_mongodb 5 | ports: 6 | - "27017:27017" 7 | volumes: 8 | - ../mongo/init-mongo.js:/docker-entrypoint-initdb.d/init-mongo.js:ro 9 | networks: 10 | - calm-net 11 | calmhub: 12 | image: finos/calm-hub:latest 13 | container_name: calm-hub 14 | ports: 15 | - "8080:8080" 16 | environment: 17 | - JAVA_OPTS=-Dquarkus.mongodb.connection-string=mongodb://mongodb:27017 18 | networks: 19 | - calm-net 20 | networks: 21 | calm-net: 22 | driver: bridge 23 | -------------------------------------------------------------------------------- /calm-hub/k8s/calm-hub.yml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: calmhub 5 | labels: 6 | app: calmhub 7 | spec: 8 | replicas: 1 9 | selector: 10 | matchLabels: 11 | app: calmhub 12 | template: 13 | metadata: 14 | labels: 15 | app: calmhub 16 | spec: 17 | containers: 18 | - name: calmhub 19 | image: finos/calm-hub:latest 20 | ports: 21 | - containerPort: 8080 22 | 23 | --- 24 | apiVersion: v1 25 | kind: Service 26 | metadata: 27 | name: calmhub 28 | labels: 29 | app: calmhub 30 | spec: 31 | type: LoadBalancer 32 | ports: 33 | - port: 80 34 | targetPort: 8080 35 | selector: 36 | app: calmhub 37 | -------------------------------------------------------------------------------- /calm-hub/k8s/mongo.yml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: mongodb 5 | labels: 6 | app: mongodb 7 | spec: 8 | replicas: 1 9 | selector: 10 | matchLabels: 11 | app: mongodb 12 | template: 13 | metadata: 14 | labels: 15 | app: mongodb 16 | spec: 17 | containers: 18 | - name: mongodb 19 | image: mongo:latest 20 | ports: 21 | - containerPort: 27017 22 | volumeMounts: 23 | - name: init-mongo-script 24 | mountPath: /docker-entrypoint-initdb.d/init-mongo.js 25 | subPath: init-mongo.js 26 | volumes: 27 | - name: init-mongo-script 28 | configMap: 29 | name: mongo-init-config 30 | 31 | --- 32 | apiVersion: v1 33 | kind: Service 34 | metadata: 35 | name: mongodb 36 | labels: 37 | app: mongodb 38 | spec: 39 | ports: 40 | - port: 27017 41 | targetPort: 27017 42 | selector: 43 | app: mongodb 44 | -------------------------------------------------------------------------------- /calm-hub/keycloak-dev/docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | keycloak: 3 | image: quay.io/keycloak/keycloak:26.2 4 | container_name: keycloak 5 | ports: 6 | - "9443:8443" 7 | environment: 8 | KC_BOOTSTRAP_ADMIN_USERNAME: admin 9 | KC_BOOTSTRAP_ADMIN_PASSWORD: ${KC_BOOTSTRAP_ADMIN_PASSWORD} 10 | KC_HTTPS_PORT: 8443 11 | KC_HTTPS_CERTIFICATE_FILE: /opt/keycloak/certs/cert.pem 12 | KC_HTTPS_CERTIFICATE_KEY_FILE: /opt/keycloak/certs/key.pem 13 | volumes: 14 | - ./imports:/opt/keycloak/data/import 15 | - ./certs:/opt/keycloak/certs 16 | command: start-dev --import-realm 17 | restart: unless-stopped -------------------------------------------------------------------------------- /calm-hub/local-dev/docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | mongodb: 3 | image: mongo:latest 4 | container_name: calm_mongodb 5 | ports: 6 | - "27017:27017" 7 | volumes: 8 | - ../mongo/init-mongo.js:/docker-entrypoint-initdb.d/init-mongo.js:ro 9 | networks: 10 | - calm-net 11 | networks: 12 | calm-net: 13 | driver: bridge -------------------------------------------------------------------------------- /calm-hub/src/integration-test/java/integration/EndToEndResource.java: -------------------------------------------------------------------------------- 1 | package integration; 2 | 3 | import io.quarkus.test.common.QuarkusTestResourceLifecycleManager; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | import org.testcontainers.containers.MongoDBContainer; 7 | 8 | import java.util.Map; 9 | 10 | public class EndToEndResource implements QuarkusTestResourceLifecycleManager { 11 | 12 | private MongoDBContainer mongoDBContainer; 13 | 14 | private static final Logger logger = LoggerFactory.getLogger(EndToEndResource.class); 15 | 16 | @Override 17 | public Map start() { 18 | if(mongoDBContainer == null) { 19 | mongoDBContainer = new MongoDBContainer("mongo:4.4.3"); 20 | } 21 | 22 | logger.info("Starting MongoDB container"); 23 | mongoDBContainer.start(); 24 | return Map.of( 25 | "quarkus.mongodb.connection-string", mongoDBContainer.getReplicaSetUrl() 26 | ); 27 | } 28 | 29 | @Override 30 | public void stop() { 31 | mongoDBContainer.stop(); 32 | } 33 | } 34 | 35 | -------------------------------------------------------------------------------- /calm-hub/src/integration-test/java/integration/IntegrationTestProfile.java: -------------------------------------------------------------------------------- 1 | package integration; 2 | 3 | import io.quarkus.test.common.QuarkusTestResource; 4 | import io.quarkus.test.junit.QuarkusTestProfile; 5 | 6 | import java.util.Set; 7 | 8 | @QuarkusTestResource(EndToEndResource.class) 9 | public class IntegrationTestProfile implements QuarkusTestProfile { 10 | 11 | @Override 12 | public Set> getEnabledAlternatives() { 13 | // Optionally, return specific classes you want enabled only for this profile 14 | return Set.of(); 15 | } 16 | 17 | @Override 18 | public String getConfigProfile() { 19 | // Optional: specify a custom profile name if needed 20 | return "integration-test"; 21 | } 22 | } 23 | 24 | -------------------------------------------------------------------------------- /calm-hub/src/main/java/org/finos/calm/config/StandaloneQualifier.java: -------------------------------------------------------------------------------- 1 | package org.finos.calm.config; 2 | 3 | import jakarta.inject.Qualifier; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | import java.lang.annotation.ElementType; 8 | 9 | /** 10 | * Qualifier for standalone mode database instances. 11 | * Used to inject the NitriteDB instance in standalone mode. 12 | */ 13 | @Qualifier 14 | @Retention(RetentionPolicy.RUNTIME) 15 | @Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.TYPE}) 16 | public @interface StandaloneQualifier { 17 | } 18 | -------------------------------------------------------------------------------- /calm-hub/src/main/java/org/finos/calm/domain/Domain.java: -------------------------------------------------------------------------------- 1 | package org.finos.calm.domain; 2 | 3 | /** 4 | * Represents a domain in the CALM system. 5 | * A domain is a logical grouping of controls and shared control schemas 6 | */ 7 | public class Domain { 8 | 9 | /** 10 | * Constructor to create a Domain with a specified name. 11 | * 12 | * @param name the name of the domain 13 | */ 14 | public Domain(String name) { 15 | this.name = name; 16 | } 17 | 18 | /** 19 | * Default constructor for Domain. 20 | * This constructor is used for deserialization purposes. 21 | */ 22 | public Domain() { 23 | 24 | } 25 | 26 | private String name; 27 | 28 | public String getName() { 29 | return name; 30 | } 31 | 32 | public void setName(String name) { 33 | this.name = name; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /calm-hub/src/main/java/org/finos/calm/domain/Standard.java: -------------------------------------------------------------------------------- 1 | package org.finos.calm.domain; 2 | 3 | import java.util.Objects; 4 | 5 | public class Standard extends StandardDetails { 6 | private String standardJson; 7 | 8 | public Standard(String name, String description, String standardJson, Integer id, String version) { 9 | super(name, description, id, version); 10 | this.standardJson = standardJson; 11 | } 12 | 13 | public Standard() { 14 | // Default constructor 15 | } 16 | 17 | public String getStandardJson() { 18 | return standardJson; 19 | } 20 | 21 | public void setStandardJson(String standardJson) { 22 | this.standardJson = standardJson; 23 | } 24 | 25 | @Override 26 | public boolean equals(Object o) { 27 | if (o == null || getClass() != o.getClass()) return false; 28 | if (!super.equals(o)) return false; 29 | Standard standard1 = (Standard) o; 30 | return Objects.equals(standardJson, standard1.standardJson); 31 | } 32 | 33 | @Override 34 | public int hashCode() { 35 | return Objects.hash(super.hashCode(), standardJson); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /calm-hub/src/main/java/org/finos/calm/domain/ValueWrapper.java: -------------------------------------------------------------------------------- 1 | package org.finos.calm.domain; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * Wrapper class for a list of values, this is used to wrap a list of values and provide the option to paginate results later 7 | * 8 | * @param the type of the values 9 | */ 10 | public class ValueWrapper { 11 | private List values; 12 | 13 | public ValueWrapper(List values) { 14 | this.values = values; 15 | } 16 | 17 | public List getValues() { 18 | return values; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /calm-hub/src/main/java/org/finos/calm/domain/adr/Status.java: -------------------------------------------------------------------------------- 1 | package org.finos.calm.domain.adr; 2 | 3 | public enum Status { 4 | draft, 5 | proposed, 6 | accepted, 7 | superseded, 8 | rejected, 9 | deprecated 10 | } 11 | -------------------------------------------------------------------------------- /calm-hub/src/main/java/org/finos/calm/domain/exception/AdrNotFoundException.java: -------------------------------------------------------------------------------- 1 | package org.finos.calm.domain.exception; 2 | 3 | /** 4 | * Exception thrown when the specified ADR is not found. 5 | */ 6 | public class AdrNotFoundException extends Exception { 7 | } 8 | -------------------------------------------------------------------------------- /calm-hub/src/main/java/org/finos/calm/domain/exception/AdrParseException.java: -------------------------------------------------------------------------------- 1 | package org.finos.calm.domain.exception; 2 | 3 | public class AdrParseException extends Exception { 4 | } 5 | -------------------------------------------------------------------------------- /calm-hub/src/main/java/org/finos/calm/domain/exception/AdrPersistenceException.java: -------------------------------------------------------------------------------- 1 | package org.finos.calm.domain.exception; 2 | 3 | public class AdrPersistenceException extends Exception { 4 | } 5 | -------------------------------------------------------------------------------- /calm-hub/src/main/java/org/finos/calm/domain/exception/AdrRevisionNotFoundException.java: -------------------------------------------------------------------------------- 1 | package org.finos.calm.domain.exception; 2 | 3 | /** 4 | * Exception thrown when the specified ADR revision is not found. 5 | */ 6 | public class AdrRevisionNotFoundException extends Exception { 7 | } 8 | -------------------------------------------------------------------------------- /calm-hub/src/main/java/org/finos/calm/domain/exception/ArchitectureNotFoundException.java: -------------------------------------------------------------------------------- 1 | package org.finos.calm.domain.exception; 2 | 3 | /** 4 | * Exception thrown when the specified architecture is not found. 5 | */ 6 | public class ArchitectureNotFoundException extends Exception { 7 | } 8 | -------------------------------------------------------------------------------- /calm-hub/src/main/java/org/finos/calm/domain/exception/ArchitectureVersionExistsException.java: -------------------------------------------------------------------------------- 1 | package org.finos.calm.domain.exception; 2 | 3 | /** 4 | * Exception thrown when an architecture version already exists. 5 | */ 6 | public class ArchitectureVersionExistsException extends Exception { 7 | } 8 | -------------------------------------------------------------------------------- /calm-hub/src/main/java/org/finos/calm/domain/exception/ArchitectureVersionNotFoundException.java: -------------------------------------------------------------------------------- 1 | package org.finos.calm.domain.exception; 2 | 3 | /** 4 | * Exception thrown when the specified architecture version is not found. 5 | */ 6 | public class ArchitectureVersionNotFoundException extends Exception { 7 | } 8 | -------------------------------------------------------------------------------- /calm-hub/src/main/java/org/finos/calm/domain/exception/DomainAlreadyExistsException.java: -------------------------------------------------------------------------------- 1 | package org.finos.calm.domain.exception; 2 | 3 | public class DomainAlreadyExistsException extends Exception { 4 | public DomainAlreadyExistsException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /calm-hub/src/main/java/org/finos/calm/domain/exception/FlowNotFoundException.java: -------------------------------------------------------------------------------- 1 | package org.finos.calm.domain.exception; 2 | 3 | public class FlowNotFoundException extends Exception { 4 | } 5 | -------------------------------------------------------------------------------- /calm-hub/src/main/java/org/finos/calm/domain/exception/FlowVersionExistsException.java: -------------------------------------------------------------------------------- 1 | package org.finos.calm.domain.exception; 2 | 3 | public class FlowVersionExistsException extends Exception { 4 | } 5 | -------------------------------------------------------------------------------- /calm-hub/src/main/java/org/finos/calm/domain/exception/FlowVersionNotFoundException.java: -------------------------------------------------------------------------------- 1 | package org.finos.calm.domain.exception; 2 | 3 | public class FlowVersionNotFoundException extends Exception { 4 | } 5 | -------------------------------------------------------------------------------- /calm-hub/src/main/java/org/finos/calm/domain/exception/NamespaceNotFoundException.java: -------------------------------------------------------------------------------- 1 | package org.finos.calm.domain.exception; 2 | 3 | /** 4 | * Exception thrown when the specified namespace is not found. 5 | */ 6 | public class NamespaceNotFoundException extends Exception { 7 | } 8 | -------------------------------------------------------------------------------- /calm-hub/src/main/java/org/finos/calm/domain/exception/PatternNotFoundException.java: -------------------------------------------------------------------------------- 1 | package org.finos.calm.domain.exception; 2 | 3 | /** 4 | * Exception thrown when the specified pattern is not found. 5 | */ 6 | public class PatternNotFoundException extends Exception { 7 | } 8 | -------------------------------------------------------------------------------- /calm-hub/src/main/java/org/finos/calm/domain/exception/PatternVersionExistsException.java: -------------------------------------------------------------------------------- 1 | package org.finos.calm.domain.exception; 2 | 3 | /** 4 | * Exception thrown when a pattern version already exists. 5 | */ 6 | public class PatternVersionExistsException extends Exception { 7 | } 8 | -------------------------------------------------------------------------------- /calm-hub/src/main/java/org/finos/calm/domain/exception/PatternVersionNotFoundException.java: -------------------------------------------------------------------------------- 1 | package org.finos.calm.domain.exception; 2 | 3 | /** 4 | * Exception thrown when the specified pattern version is not found. 5 | */ 6 | public class PatternVersionNotFoundException extends Exception { 7 | } 8 | -------------------------------------------------------------------------------- /calm-hub/src/main/java/org/finos/calm/domain/exception/StandardNotFoundException.java: -------------------------------------------------------------------------------- 1 | package org.finos.calm.domain.exception; 2 | 3 | public class StandardNotFoundException extends Exception { 4 | } 5 | -------------------------------------------------------------------------------- /calm-hub/src/main/java/org/finos/calm/domain/exception/StandardVersionExistsException.java: -------------------------------------------------------------------------------- 1 | package org.finos.calm.domain.exception; 2 | 3 | public class StandardVersionExistsException extends Exception { 4 | } 5 | -------------------------------------------------------------------------------- /calm-hub/src/main/java/org/finos/calm/domain/exception/StandardVersionNotFoundException.java: -------------------------------------------------------------------------------- 1 | package org.finos.calm.domain.exception; 2 | 3 | public class StandardVersionNotFoundException extends Exception{ 4 | } 5 | -------------------------------------------------------------------------------- /calm-hub/src/main/java/org/finos/calm/domain/exception/UserAccessNotFoundException.java: -------------------------------------------------------------------------------- 1 | package org.finos.calm.domain.exception; 2 | 3 | /** 4 | * Exception thrown when the user access details are not found. 5 | */ 6 | public class UserAccessNotFoundException extends Exception { 7 | } 8 | -------------------------------------------------------------------------------- /calm-hub/src/main/java/org/finos/calm/resources/CalmResourceErrorResponses.java: -------------------------------------------------------------------------------- 1 | package org.finos.calm.resources; 2 | 3 | import jakarta.ws.rs.core.Response; 4 | 5 | public class CalmResourceErrorResponses { 6 | public static Response invalidNamespaceResponse(String namespace) { 7 | return Response.status(Response.Status.NOT_FOUND).entity("Invalid namespace provided: " + namespace).build(); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /calm-hub/src/main/java/org/finos/calm/resources/ResourceValidationConstants.java: -------------------------------------------------------------------------------- 1 | package org.finos.calm.resources; 2 | 3 | import org.owasp.html.HtmlPolicyBuilder; 4 | import org.owasp.html.PolicyFactory; 5 | 6 | public class ResourceValidationConstants { 7 | public static final String NAMESPACE_REGEX = "^[A-Za-z0-9-]+$"; 8 | public static final String NAMESPACE_MESSAGE = "namespace must match pattern '^[A-Za-z0-9-]+$'"; 9 | public static final String VERSION_REGEX = "^(0|[1-9][0-9]*)[-.]?(0|[1-9][0-9]*)[-.]?(0|[1-9][0-9]*)$"; 10 | public static final String VERSION_MESSAGE = "version must match pattern '^(0|[1-9][0-9]*)[-.]?(0|[1-9][0-9]*)[-.]?(0|[1-9][0-9]*)$'"; 11 | public static final PolicyFactory STRICT_SANITIZATION_POLICY = new HtmlPolicyBuilder().toFactory(); 12 | 13 | } 14 | -------------------------------------------------------------------------------- /calm-hub/src/main/java/org/finos/calm/security/PermittedScopes.java: -------------------------------------------------------------------------------- 1 | package org.finos.calm.security; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * Custom annotation to define required scopes for a REST endpoint. 10 | */ 11 | @Target({ ElementType.METHOD, ElementType.TYPE }) 12 | @Retention(RetentionPolicy.RUNTIME) 13 | public @interface PermittedScopes { 14 | String[] value(); 15 | } -------------------------------------------------------------------------------- /calm-hub/src/main/java/org/finos/calm/security/UserRequestAttributes.java: -------------------------------------------------------------------------------- 1 | package org.finos.calm.security; 2 | 3 | public record UserRequestAttributes(String requestMethod, String username, String path, String namespace) { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /calm-hub/src/main/java/org/finos/calm/store/CoreSchemaStore.java: -------------------------------------------------------------------------------- 1 | package org.finos.calm.store; 2 | 3 | import java.util.List; 4 | import java.util.Map; 5 | 6 | public interface CoreSchemaStore { 7 | List getVersions(); 8 | Map getSchemasForVersion(String version); 9 | void createSchemaVersion(String version, Map schemas); 10 | } 11 | -------------------------------------------------------------------------------- /calm-hub/src/main/java/org/finos/calm/store/DomainStore.java: -------------------------------------------------------------------------------- 1 | package org.finos.calm.store; 2 | 3 | import org.finos.calm.domain.Domain; 4 | import org.finos.calm.domain.exception.DomainAlreadyExistsException; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * Interface for managing domains in the CALM system. 10 | * Provides methods to retrieve and create domains. 11 | */ 12 | public interface DomainStore { 13 | /** 14 | * Retrieves a list of all domains in the system. 15 | * 16 | * @return a list of domain names 17 | */ 18 | List getDomains(); 19 | 20 | /** 21 | * Creates a new domain with the specified name. 22 | * 23 | * @param name the name of the domain to create 24 | * @return the created Domain object 25 | * @throws DomainAlreadyExistsException if a domain with the same name already exists 26 | */ 27 | Domain createDomain(String name) throws DomainAlreadyExistsException; 28 | } 29 | -------------------------------------------------------------------------------- /calm-hub/src/main/java/org/finos/calm/store/FlowStore.java: -------------------------------------------------------------------------------- 1 | package org.finos.calm.store; 2 | 3 | import org.finos.calm.domain.*; 4 | import org.finos.calm.domain.exception.NamespaceNotFoundException; 5 | import org.finos.calm.domain.exception.FlowNotFoundException; 6 | import org.finos.calm.domain.exception.FlowVersionExistsException; 7 | import org.finos.calm.domain.exception.FlowVersionNotFoundException; 8 | 9 | import java.util.List; 10 | 11 | public interface FlowStore { 12 | List getFlowsForNamespace(String namespace) throws NamespaceNotFoundException; 13 | Flow createFlowForNamespace(Flow flow) throws NamespaceNotFoundException; 14 | List getFlowVersions(Flow flow) throws NamespaceNotFoundException, FlowNotFoundException; 15 | String getFlowForVersion(Flow flow) throws NamespaceNotFoundException, FlowNotFoundException, FlowVersionNotFoundException; 16 | Flow createFlowForVersion(Flow flow) throws NamespaceNotFoundException, FlowNotFoundException, FlowVersionExistsException; 17 | Flow updateFlowForVersion(Flow flow) throws NamespaceNotFoundException, FlowNotFoundException; 18 | } 19 | -------------------------------------------------------------------------------- /calm-hub/src/main/java/org/finos/calm/store/NamespaceStore.java: -------------------------------------------------------------------------------- 1 | package org.finos.calm.store; 2 | 3 | import java.util.List; 4 | 5 | public interface NamespaceStore { 6 | List getNamespaces(); 7 | boolean namespaceExists(String namespace); 8 | void createNamespace(String namespace); 9 | } 10 | -------------------------------------------------------------------------------- /calm-hub/src/main/java/org/finos/calm/store/StandardStore.java: -------------------------------------------------------------------------------- 1 | package org.finos.calm.store; 2 | 3 | import org.finos.calm.domain.Standard; 4 | import org.finos.calm.domain.StandardDetails; 5 | import org.finos.calm.domain.exception.NamespaceNotFoundException; 6 | import org.finos.calm.domain.exception.StandardNotFoundException; 7 | import org.finos.calm.domain.exception.StandardVersionExistsException; 8 | import org.finos.calm.domain.exception.StandardVersionNotFoundException; 9 | 10 | import java.util.List; 11 | 12 | public interface StandardStore { 13 | List getStandardsForNamespace(String namespace) throws NamespaceNotFoundException; 14 | Standard createStandardForNamespace(Standard standard) throws NamespaceNotFoundException; 15 | List getStandardVersions(StandardDetails standard) throws NamespaceNotFoundException, StandardNotFoundException; 16 | String getStandardForVersion(StandardDetails standardDetails) throws NamespaceNotFoundException, StandardNotFoundException, StandardVersionNotFoundException; 17 | Standard createStandardForVersion(Standard standard) throws NamespaceNotFoundException, StandardNotFoundException, StandardVersionExistsException; 18 | } 19 | -------------------------------------------------------------------------------- /calm-hub/src/main/resources/application-secure.properties: -------------------------------------------------------------------------------- 1 | quarkus.http.ssl-port=8443 2 | quarkus.http.insecure-requests=disabled 3 | quarkus.http.ssl.certificate.key-files=key.pem 4 | quarkus.http.ssl.certificate.key-store-file-type=PEM 5 | quarkus.http.ssl.certificate.files=cert.pem 6 | 7 | quarkus.http.auth.permission.secured.paths=/calm/* 8 | quarkus.http.auth.permission.secured.policy=authenticated 9 | 10 | #calm-hub has to configure with truststore to validate the IdP's server certs. 11 | quarkus.oidc.tls.verification=none 12 | quarkus.oidc.auth-server-url=https://localhost:9443/realms/calm-hub-realm 13 | quarkus.oidc.client-id=calm-hub-producer-app 14 | quarkus.oidc.token.audience=calm-hub-producer-app 15 | quarkus.oidc.tenant-enabled=true -------------------------------------------------------------------------------- /calm-hub/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | quarkus.swagger-ui.always-include=true 2 | quarkus.smallrye-openapi.info-title=Calm Hub 3 | #If using in quarkus dev mode can be 'mongo' or 'standalone' 4 | calm.database.mode=mongo 5 | # Standalone mode configuration 6 | calm.standalone.data-directory=${user.home}/.calm-hub/data 7 | calm.standalone.database-name=calmSchemas 8 | calm.standalone.username=admin 9 | calm.standalone.password=admin 10 | calm.standalone.init-script-path=mongo/init-mongo.js 11 | quarkus.mongodb.connection-string = mongodb://localhost:27017 12 | quarkus.mongodb.database = calmSchemas 13 | 14 | #Disable creating the keycloak container for default profile. 15 | quarkus.oidc.enabled=true 16 | quarkus.keycloak.devservices.enabled=false 17 | quarkus.oidc.tenant-enabled=false 18 | 19 | #quarkus.http.port=8081 -------------------------------------------------------------------------------- /calm-hub/src/test/java/org/finos/calm/resources/AllowPutProfile.java: -------------------------------------------------------------------------------- 1 | package org.finos.calm.resources; 2 | 3 | import io.quarkus.test.junit.QuarkusTestProfile; 4 | 5 | import java.util.Map; 6 | 7 | public class AllowPutProfile implements QuarkusTestProfile { 8 | @Override 9 | public Map getConfigOverrides() { 10 | return Map.of( 11 | "allow.put.operations", "true" 12 | ); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /calm-hub/src/test/java/org/finos/calm/resources/TestCalmErrorResponsesShould.java: -------------------------------------------------------------------------------- 1 | package org.finos.calm.resources; 2 | 3 | import jakarta.ws.rs.core.Response; 4 | import org.junit.jupiter.api.Test; 5 | 6 | import static org.hamcrest.MatcherAssert.assertThat; 7 | import static org.hamcrest.Matchers.equalTo; 8 | 9 | public class TestCalmErrorResponsesShould { 10 | 11 | @Test 12 | void create_an_invalid_namespace_response() { 13 | try (Response response = CalmResourceErrorResponses.invalidNamespaceResponse("finos")) { 14 | assertThat(response.getStatus(), equalTo(404)); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /calm-hub/src/test/resources/application.properties: -------------------------------------------------------------------------------- 1 | # Test configuration 2 | quarkus.arc.remove-unused-beans=false 3 | quarkus.test.native-image-profile=test 4 | quarkus.test.continuous-testing=enabled 5 | quarkus.test.flat-class-path=true 6 | 7 | # Database configuration for tests 8 | quarkus.mongodb.connection-string=mongodb://localhost:27017 9 | quarkus.mongodb.database=test 10 | quarkus.mongodb.devservices.enabled=true 11 | 12 | # Standalone mode configuration 13 | calm.database.mode=mongo 14 | calm.standalone.data-directory=target/test-data 15 | calm.standalone.database-name=calmSchemasTest 16 | calm.standalone.username=admin 17 | calm.standalone.password=admin 18 | calm.standalone.init-script-path=src/test/resources/init-mongo.js 19 | -------------------------------------------------------------------------------- /calm/control-example/one-node-wonder.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/finos/architecture-as-code/main/calm/draft/2024-08/meta/calm.json", 3 | "title": "Demonstration of domain and controls", 4 | "nodes": [ 5 | { 6 | "unique-id": "example-system", 7 | "node-type": "system", 8 | "name": "Example System", 9 | "description": "Example System", 10 | "controls": { 11 | "cbom": { 12 | "description": "Control requirements for delivering patterns", 13 | "requirements": [ 14 | { 15 | "control-requirement": "https://raw.githubusercontent.com/finos/architecture-as-code/main/calm/control-example/pre-prod-review-specification.json", 16 | "control-config": "https://raw.githubusercontent.com/finos/architecture-as-code/main/calm/control-example/pre-prod-review-configuration.json" 17 | } 18 | ] 19 | } 20 | } 21 | } 22 | ], 23 | "relationships": [], 24 | "metadata": [] 25 | } -------------------------------------------------------------------------------- /calm/control-example/pre-prod-review-configuration.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/finos/architecture-as-code/main/calm/control-example/control-specification.json", 3 | "title": "Evidence of pre-production review", 4 | "control-id": "ci-arch-001", 5 | "name": "Architecture review pre-production", 6 | "scope-text": "All workloads going to production", 7 | "scope-rego": "input.metadata.target-deployment.environment == Production", 8 | "description": "As part of the SDLC requirements, each workload going to production is subject to an architecture review" 9 | } -------------------------------------------------------------------------------- /calm/control-example/pre-prod-review-evidence.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/finos/architecture-as-code/main/calm/draft/2024-08/meta/evidence.json", 3 | "evidence": { 4 | "unique-id": "pre-prod-review-evidence", 5 | "control-configuration-url": "https://raw.githubusercontent.com/finos/architecture-as-code/main/calm/control-example/pre-prod-review-configuration.json", 6 | "evidence-paths": [ 7 | "https://evidence.com/12345" 8 | ] 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /calm/control-example/pre-prod-review-specification.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2020-12/schema", 3 | "$id": "https://raw.githubusercontent.com/finos/architecture-as-code/main/calm/control-example/control-specification.json", 4 | "title": "Example Production Control Specification", 5 | "type": "object", 6 | "allOf": [ 7 | { 8 | "$ref": "https://raw.githubusercontent.com/finos/architecture-as-code/main/calm/draft/2024-08/meta/control-requirement.json" 9 | } 10 | ], 11 | "properties": { 12 | "control-id": { 13 | "const": "ci-arch-001" 14 | }, 15 | "name": { 16 | "const": "Architecture review pre-production" 17 | }, 18 | "description": { 19 | "const": "As part of the SDLC requirements, each workload going to production is subject to an architecture review" 20 | }, 21 | "scope-text": { 22 | "const": "All workloads going to production" 23 | }, 24 | "scope-rego": { 25 | "const": "input.metadata.target-deployment.environment == Production" 26 | } 27 | }, 28 | "required": [ 29 | "scope-text", 30 | "scope-rego", 31 | "control-id", 32 | "name", 33 | "description" 34 | ] 35 | } -------------------------------------------------------------------------------- /calm/domains-example/security/cluster-ingress-https.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/finos/architecture-as-code/main/calm/domains-example/security/schema/permitted-connection.json", 3 | "control-id": "security-002", 4 | "name": "Permitted Connection", 5 | "description": "Permits a connection on a relationship specified in the architecture", 6 | "protocol": "HTTPS" 7 | } -------------------------------------------------------------------------------- /calm/domains-example/security/cluster-internal-mtls.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/finos/architecture-as-code/main/calm/domains-example/security/schema/permitted-connection.json", 3 | "control-id": "security-002", 4 | "name": "Permitted Connection", 5 | "description": "Permits a connection on a relationship specified in the architecture", 6 | "protocol": "mTLS" 7 | } -------------------------------------------------------------------------------- /calm/domains-example/security/cluster-micro-segmentation.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/finos/architecture-as-code/main/calm/domains-example/security/schema/micro-segmentation.json", 3 | "control-id": "security-001", 4 | "name": "Micro-segmentation of Kubernetes Cluster", 5 | "description": "Micro-segmentation is in place to prevent lateral movement outside of permitted flows", 6 | "permit-ingress": true, 7 | "permit-egress": false 8 | } -------------------------------------------------------------------------------- /calm/domains-example/security/schema/micro-segmentation.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2020-12/schema", 3 | "$id": "https://raw.githubusercontent.com/finos/architecture-as-code/main/calm/domains-example/security/schema/micro-segmentation.json", 4 | "title": "Micro-segmentation in place", 5 | "type": "object", 6 | "allOf": [ 7 | { 8 | "$ref": "https://raw.githubusercontent.com/finos/architecture-as-code/main/calm/draft/2024-08/meta/control-requirement.json" 9 | } 10 | ], 11 | "properties": { 12 | "control-id": { 13 | "const": "security-001" 14 | }, 15 | "name": { 16 | "const": "Micro-segmentation of Kubernetes Cluster" 17 | }, 18 | "description": { 19 | "const": "Micro-segmentation is in place to prevent lateral movement outside of permitted flows" 20 | }, 21 | "permit-ingress": { 22 | "type": "boolean" 23 | }, 24 | "permit-egress": { 25 | "type": "boolean" 26 | } 27 | }, 28 | "required": [ 29 | "control-id", 30 | "name", 31 | "description", 32 | "permit-ingress", 33 | "permit-egress" 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /calm/draft/1083/meta/calm.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2020-12/schema", 3 | "$id": "https://calm.finos.org/draft/1083/meta/calm.json", 4 | 5 | "$vocabulary": { 6 | "https://json-schema.org/draft/2020-12/vocab/core": true, 7 | "https://json-schema.org/draft/2020-12/vocab/applicator": true, 8 | "https://json-schema.org/draft/2020-12/vocab/validation": true, 9 | "https://json-schema.org/draft/2020-12/vocab/meta-data": true, 10 | "https://json-schema.org/draft/2020-12/vocab/format-annotation": true, 11 | "https://json-schema.org/draft/2020-12/vocab/content": true, 12 | "https://calm.finos.org/draft/1083/meta/core.json": true 13 | }, 14 | "$dynamicAnchor": "meta", 15 | 16 | "title": "Common Architecture Language Model (CALM) Schema", 17 | "allOf": [ 18 | {"$ref": "https://json-schema.org/draft/2020-12/schema"}, 19 | {"$ref": "https://calm.finos.org/draft/1083/meta/core.json"} 20 | ] 21 | } -------------------------------------------------------------------------------- /calm/draft/1083/prototype/interfaces/kafka-topic.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2020-12/schema", 3 | "$id": "https://calm.finos.org/draft/1083/interfaces/kafka-topic", 4 | "title": "Kafka Topic Interface", 5 | "type": "object", 6 | "properties": { 7 | "topic": { 8 | "type": "string", 9 | "description": "The name of the Kafka topic" 10 | }, 11 | "host": { 12 | "type": "string", 13 | "format": "hostname", 14 | "description": "The Kafka broker hostname or IP" 15 | }, 16 | "port": { 17 | "type": "integer", 18 | "description": "The port on which the Kafka broker is exposed", 19 | "minimum": 1, 20 | "maximum": 65535 21 | } 22 | }, 23 | "required": ["topic", "host", "port"] 24 | } -------------------------------------------------------------------------------- /calm/draft/1177/prototype/authentication-control-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "$id": "https://calm.finos.org/draft/1177/prototype/authentication-control-config.json", 3 | "auth-method": "OAuth2", 4 | "token-expiry": { 5 | "unit": "hours", 6 | "value": 1 7 | }, 8 | "mfa-required": false, 9 | "token-revocation": true, 10 | "additional-details": { 11 | "grant-type": "client_credentials", 12 | "token-endpoint": "https://auth.example.com/token" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /calm/draft/1233/prototype/encryption-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "encryption-algorithm": "AES-256", 3 | "key-rotation-period": "90-days", 4 | "data-at-rest": true, 5 | "data-in-transit": true 6 | } 7 | -------------------------------------------------------------------------------- /calm/draft/1233/prototype/example-inline-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "controls": { 3 | "data-security": { 4 | "description": "Data security controls for the system", 5 | "requirements": [ 6 | { 7 | "control-requirement-url": "https://calm.finos.org/draft/1233/prototype/data-encryption-requirement.json", 8 | "control-config": { 9 | "encryption-algorithm": "AES-256", 10 | "key-rotation-period": "90-days", 11 | "data-at-rest": true, 12 | "data-in-transit": true 13 | } 14 | }, 15 | { 16 | "control-requirement-url": "https://calm.finos.org/draft/1233/prototype/access-control-requirement.json", 17 | "control-config-url": "https://calm.finos.org/draft/1233/prototype/rbac-config.json" 18 | } 19 | ] 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /calm/draft/1233/prototype/example-url-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "controls": { 3 | "data-security": { 4 | "description": "Data security controls for the system", 5 | "requirements": [ 6 | { 7 | "control-requirement-url": "https://calm.finos.org/draft/1233/prototype/data-encryption-requirement.json", 8 | "control-config-url": "https://calm.finos.org/draft/1233/prototype/encryption-config.json" 9 | }, 10 | { 11 | "control-requirement-url": "https://calm.finos.org/draft/1233/prototype/access-control-requirement.json", 12 | "control-config-url": "https://calm.finos.org/draft/1233/prototype/rbac-config.json" 13 | } 14 | ] 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /calm/draft/1233/prototype/rbac-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "access-model": "RBAC", 3 | "multi-factor-authentication": true, 4 | "session-timeout": 30, 5 | "password-policy": { 6 | "minimum-length": 12, 7 | "require-special-chars": true, 8 | "require-numbers": true, 9 | "require-mixed-case": true 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /calm/draft/2024-02/meta/calm.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2020-12/schema", 3 | "$id": "https://raw.githubusercontent.com/finos/architecture-as-code/main/calm/draft/2024-02/meta/calm.json", 4 | 5 | "$vocabulary": { 6 | "https://json-schema.org/draft/2020-12/vocab/core": true, 7 | "https://json-schema.org/draft/2020-12/vocab/applicator": true, 8 | "https://json-schema.org/draft/2020-12/vocab/validation": true, 9 | "https://json-schema.org/draft/2020-12/vocab/meta-data": true, 10 | "https://json-schema.org/draft/2020-12/vocab/format-annotation": true, 11 | "https://json-schema.org/draft/2020-12/vocab/content": true, 12 | "https://raw.githubusercontent.com/finos/architecture-as-code/main/calm/draft/2024-02/meta/core.json": true 13 | }, 14 | "$dynamicAnchor": "meta", 15 | 16 | "title": "Common Architecture Language Model (CALM) Schema", 17 | "allOf": [ 18 | {"$ref": "https://json-schema.org/draft/2020-12/schema"}, 19 | {"$ref": "https://raw.githubusercontent.com/finos/architecture-as-code/main/calm/draft/2024-02/meta/core.json"} 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /calm/draft/2024-03/meta/calm.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2020-12/schema", 3 | "$id": "https://raw.githubusercontent.com/finos/architecture-as-code/main/calm/draft/2024-03/meta/calm.json", 4 | 5 | "$vocabulary": { 6 | "https://json-schema.org/draft/2020-12/vocab/core": true, 7 | "https://json-schema.org/draft/2020-12/vocab/applicator": true, 8 | "https://json-schema.org/draft/2020-12/vocab/validation": true, 9 | "https://json-schema.org/draft/2020-12/vocab/meta-data": true, 10 | "https://json-schema.org/draft/2020-12/vocab/format-annotation": true, 11 | "https://json-schema.org/draft/2020-12/vocab/content": true, 12 | "https://raw.githubusercontent.com/finos/architecture-as-code/main/calm/draft/2024-03/meta/core.json": true 13 | }, 14 | "$dynamicAnchor": "meta", 15 | 16 | "title": "Common Architecture Language Model (CALM) Schema", 17 | "allOf": [ 18 | {"$ref": "https://json-schema.org/draft/2020-12/schema"}, 19 | {"$ref": "https://raw.githubusercontent.com/finos/architecture-as-code/main/calm/draft/2024-03/meta/core.json"} 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /calm/draft/2024-04/meta/calm.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2020-12/schema", 3 | "$id": "https://raw.githubusercontent.com/finos/architecture-as-code/main/calm/draft/2024-04/meta/calm.json", 4 | 5 | "$vocabulary": { 6 | "https://json-schema.org/draft/2020-12/vocab/core": true, 7 | "https://json-schema.org/draft/2020-12/vocab/applicator": true, 8 | "https://json-schema.org/draft/2020-12/vocab/validation": true, 9 | "https://json-schema.org/draft/2020-12/vocab/meta-data": true, 10 | "https://json-schema.org/draft/2020-12/vocab/format-annotation": true, 11 | "https://json-schema.org/draft/2020-12/vocab/content": true, 12 | "https://raw.githubusercontent.com/finos/architecture-as-code/main/calm/draft/2024-04/meta/core.json": true 13 | }, 14 | "$dynamicAnchor": "meta", 15 | 16 | "title": "Common Architecture Language Model (CALM) Schema", 17 | "allOf": [ 18 | {"$ref": "https://json-schema.org/draft/2020-12/schema"}, 19 | {"$ref": "https://raw.githubusercontent.com/finos/architecture-as-code/main/calm/draft/2024-04/meta/core.json"} 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /calm/draft/2024-08/meta/calm.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2020-12/schema", 3 | "$id": "https://raw.githubusercontent.com/finos/architecture-as-code/main/calm/draft/2024-08/meta/calm.json", 4 | 5 | "$vocabulary": { 6 | "https://json-schema.org/draft/2020-12/vocab/core": true, 7 | "https://json-schema.org/draft/2020-12/vocab/applicator": true, 8 | "https://json-schema.org/draft/2020-12/vocab/validation": true, 9 | "https://json-schema.org/draft/2020-12/vocab/meta-data": true, 10 | "https://json-schema.org/draft/2020-12/vocab/format-annotation": true, 11 | "https://json-schema.org/draft/2020-12/vocab/content": true, 12 | "https://raw.githubusercontent.com/finos/architecture-as-code/main/calm/draft/2024-08/meta/core.json": true 13 | }, 14 | "$dynamicAnchor": "meta", 15 | 16 | "title": "Common Architecture Language Model (CALM) Schema", 17 | "allOf": [ 18 | {"$ref": "https://json-schema.org/draft/2020-12/schema"}, 19 | {"$ref": "https://raw.githubusercontent.com/finos/architecture-as-code/main/calm/draft/2024-08/meta/core.json"} 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /calm/draft/2024-09/meta/calm.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2020-12/schema", 3 | "$id": "https://calm.finos.org/draft/2024-09/meta/calm.json", 4 | 5 | "$vocabulary": { 6 | "https://json-schema.org/draft/2020-12/vocab/core": true, 7 | "https://json-schema.org/draft/2020-12/vocab/applicator": true, 8 | "https://json-schema.org/draft/2020-12/vocab/validation": true, 9 | "https://json-schema.org/draft/2020-12/vocab/meta-data": true, 10 | "https://json-schema.org/draft/2020-12/vocab/format-annotation": true, 11 | "https://json-schema.org/draft/2020-12/vocab/content": true, 12 | "https://calm.finos.org/draft/2024-09/meta/core.json": true 13 | }, 14 | "$dynamicAnchor": "meta", 15 | 16 | "title": "Common Architecture Language Model (CALM) Schema", 17 | "allOf": [ 18 | {"$ref": "https://json-schema.org/draft/2020-12/schema"}, 19 | {"$ref": "https://calm.finos.org/draft/2024-09/meta/core.json"} 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /calm/draft/2024-10/meta/calm.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2020-12/schema", 3 | "$id": "https://calm.finos.org/draft/2024-10/meta/calm.json", 4 | 5 | "$vocabulary": { 6 | "https://json-schema.org/draft/2020-12/vocab/core": true, 7 | "https://json-schema.org/draft/2020-12/vocab/applicator": true, 8 | "https://json-schema.org/draft/2020-12/vocab/validation": true, 9 | "https://json-schema.org/draft/2020-12/vocab/meta-data": true, 10 | "https://json-schema.org/draft/2020-12/vocab/format-annotation": true, 11 | "https://json-schema.org/draft/2020-12/vocab/content": true, 12 | "https://calm.finos.org/draft/2024-10/meta/core.json": true 13 | }, 14 | "$dynamicAnchor": "meta", 15 | 16 | "title": "Common Architecture Language Model (CALM) Schema", 17 | "allOf": [ 18 | {"$ref": "https://json-schema.org/draft/2020-12/schema"}, 19 | {"$ref": "https://calm.finos.org/draft/2024-10/meta/core.json"} 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /calm/draft/2024-12/meta/calm.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2020-12/schema", 3 | "$id": "https://calm.finos.org/draft/2024-12/meta/calm.json", 4 | 5 | "$vocabulary": { 6 | "https://json-schema.org/draft/2020-12/vocab/core": true, 7 | "https://json-schema.org/draft/2020-12/vocab/applicator": true, 8 | "https://json-schema.org/draft/2020-12/vocab/validation": true, 9 | "https://json-schema.org/draft/2020-12/vocab/meta-data": true, 10 | "https://json-schema.org/draft/2020-12/vocab/format-annotation": true, 11 | "https://json-schema.org/draft/2020-12/vocab/content": true, 12 | "https://calm.finos.org/draft/2024-12/meta/core.json": true 13 | }, 14 | "$dynamicAnchor": "meta", 15 | 16 | "title": "Common Architecture Language Model (CALM) Schema", 17 | "allOf": [ 18 | {"$ref": "https://json-schema.org/draft/2020-12/schema"}, 19 | {"$ref": "https://calm.finos.org/draft/2024-12/meta/core.json"} 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /calm/draft/2025-01/meta/calm.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2020-12/schema", 3 | "$id": "https://calm.finos.org/draft/2025-01/meta/calm.json", 4 | 5 | "$vocabulary": { 6 | "https://json-schema.org/draft/2020-12/vocab/core": true, 7 | "https://json-schema.org/draft/2020-12/vocab/applicator": true, 8 | "https://json-schema.org/draft/2020-12/vocab/validation": true, 9 | "https://json-schema.org/draft/2020-12/vocab/meta-data": true, 10 | "https://json-schema.org/draft/2020-12/vocab/format-annotation": true, 11 | "https://json-schema.org/draft/2020-12/vocab/content": true, 12 | "https://calm.finos.org/draft/2025-01/meta/core.json": true 13 | }, 14 | "$dynamicAnchor": "meta", 15 | 16 | "title": "Common Architecture Language Model (CALM) Schema", 17 | "allOf": [ 18 | {"$ref": "https://json-schema.org/draft/2020-12/schema"}, 19 | {"$ref": "https://calm.finos.org/draft/2025-01/meta/core.json"} 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /calm/draft/2025-03/meta/calm.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2020-12/schema", 3 | "$id": "https://calm.finos.org/draft/2025-03/meta/calm.json", 4 | 5 | "$vocabulary": { 6 | "https://json-schema.org/draft/2020-12/vocab/core": true, 7 | "https://json-schema.org/draft/2020-12/vocab/applicator": true, 8 | "https://json-schema.org/draft/2020-12/vocab/validation": true, 9 | "https://json-schema.org/draft/2020-12/vocab/meta-data": true, 10 | "https://json-schema.org/draft/2020-12/vocab/format-annotation": true, 11 | "https://json-schema.org/draft/2020-12/vocab/content": true, 12 | "https://calm.finos.org/draft/2025-03/meta/core.json": true 13 | }, 14 | "$dynamicAnchor": "meta", 15 | 16 | "title": "Common Architecture Language Model (CALM) Schema", 17 | "allOf": [ 18 | {"$ref": "https://json-schema.org/draft/2020-12/schema"}, 19 | {"$ref": "https://calm.finos.org/draft/2025-03/meta/core.json"} 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /calm/draft/2025-03/prototype/anyof/neither-option.architecture.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://calm.finos.org/draft/2025-03/prototype/anyof/both-options-prototype.pattern.json", 3 | "$id": "https://calm.finos.org/draft/2025-03/prototype/anyof/neither-option.architecture.json", 4 | "title": "Application without Database Pattern Example", 5 | "nodes": [ 6 | { 7 | "unique-id": "application", 8 | "name": "Application", 9 | "description": "An application that optionally connects to one or more DBs", 10 | "node-type": "service" 11 | } 12 | ], 13 | "relationships": [ 14 | { 15 | "unique-id": "connection-options", 16 | "description": "Which databases does your application connect to?", 17 | "relationship-type": { 18 | "options": [] 19 | } 20 | } 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /calm/draft/2025-03/prototype/throughput-control-prototype.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2020-12/schema", 3 | "$id": "https://calm.finos.org/traderx/control-requirement/throughput", 4 | "title": "Throughput Requirement", 5 | "type": "object", 6 | "allOf": [ 7 | { 8 | "$ref": "https://calm.finos.org/draft/2025-03/meta/control-requirement.json" 9 | } 10 | ], 11 | "properties": { 12 | "expected-message-rate": { 13 | "$ref": "https://calm.finos.org/draft/2025-03/meta/units.json#/defs/rate-unit", 14 | "description": "Define the expected message rate that the flow should handle (e.g., 1000 per second)." 15 | } 16 | }, 17 | "required": [ 18 | "expected-message-rate" 19 | ] 20 | } -------------------------------------------------------------------------------- /calm/pom.xml: -------------------------------------------------------------------------------- 1 | 4 | 4.0.0 5 | 6 | 7 | org.finos.architecture-as-code 8 | parent 9 | 1.0.0-SNAPSHOT 10 | 11 | 12 | calm 13 | pom 14 | 15 | Placeholder module for CALM 16 | 17 | -------------------------------------------------------------------------------- /calm/release/1.0-rc1/meta/calm.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2020-12/schema", 3 | "$id": "https://calm.finos.org/release/1.0-rc1/meta/calm.json", 4 | 5 | "$vocabulary": { 6 | "https://json-schema.org/draft/2020-12/vocab/core": true, 7 | "https://json-schema.org/draft/2020-12/vocab/applicator": true, 8 | "https://json-schema.org/draft/2020-12/vocab/validation": true, 9 | "https://json-schema.org/draft/2020-12/vocab/meta-data": true, 10 | "https://json-schema.org/draft/2020-12/vocab/format-annotation": true, 11 | "https://json-schema.org/draft/2020-12/vocab/content": true, 12 | "https://calm.finos.org/release/1.0-rc1/meta/core.json": true 13 | }, 14 | "$dynamicAnchor": "meta", 15 | 16 | "title": "Common Architecture Language Model (CALM) Schema", 17 | "allOf": [ 18 | {"$ref": "https://json-schema.org/draft/2020-12/schema"}, 19 | {"$ref": "https://calm.finos.org/release/1.0-rc1/meta/core.json"} 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /calm/release/1.0-rc1/prototype/anyof/neither-option.architecture.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://calm.finos.org/release/1.0-rc1/prototype/anyof/both-options-prototype.pattern.json", 3 | "$id": "https://calm.finos.org/release/1.0-rc1/prototype/anyof/neither-option.architecture.json", 4 | "title": "Application without Database Pattern Example", 5 | "nodes": [ 6 | { 7 | "unique-id": "application", 8 | "name": "Application", 9 | "description": "An application that optionally connects to one or more DBs", 10 | "node-type": "service" 11 | } 12 | ], 13 | "relationships": [ 14 | { 15 | "unique-id": "connection-options", 16 | "description": "Which databases does your application connect to?", 17 | "relationship-type": { 18 | "options": [] 19 | } 20 | } 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /calm/release/1.0-rc1/prototype/authentication-control-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "$id": "https://calm.finos.org/draft/1177/prototype/authentication-control-config.json", 3 | "auth-method": "OAuth2", 4 | "token-expiry": { 5 | "unit": "hours", 6 | "value": 1 7 | }, 8 | "mfa-required": false, 9 | "token-revocation": true, 10 | "additional-details": { 11 | "grant-type": "client_credentials", 12 | "token-endpoint": "https://auth.example.com/token" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /calm/release/1.0-rc1/prototype/custom-node-type-example.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://calm.finos.org/release/1.0-rc1/meta/calm.json", 3 | "nodes": [ 4 | { 5 | "unique-id": "standard-node", 6 | "node-type": "service", 7 | "name": "Standard Service", 8 | "description": "This node uses a standard node type from the enum" 9 | }, 10 | { 11 | "unique-id": "custom-node", 12 | "node-type": "microservice", 13 | "name": "Custom Microservice", 14 | "description": "This node uses a custom node type that is not in the standard enum" 15 | }, 16 | { 17 | "unique-id": "custom-node-2", 18 | "node-type": "gateway", 19 | "name": "API Gateway", 20 | "description": "Another example of a custom node type" 21 | } 22 | ], 23 | "relationships": [ 24 | { 25 | "unique-id": "rel-1", 26 | "relationship-type": { 27 | "connects": { 28 | "source": { 29 | "node": "custom-node" 30 | }, 31 | "destination": { 32 | "node": "standard-node" 33 | } 34 | } 35 | } 36 | } 37 | ] 38 | } 39 | -------------------------------------------------------------------------------- /calm/release/1.0-rc1/prototype/example-inline-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "controls": { 3 | "data-security": { 4 | "description": "Data security controls for the system", 5 | "requirements": [ 6 | { 7 | "control-requirement-url": "https://calm.finos.org/release/1.0-rc1/prototype/data-encryption-requirement.json", 8 | "control-config": { 9 | "encryption-algorithm": "AES-256", 10 | "key-rotation-period": "90-days", 11 | "data-at-rest": true, 12 | "data-in-transit": true 13 | } 14 | }, 15 | { 16 | "control-requirement-url": "https://calm.finos.org/release/1.0-rc1/prototype/access-control-requirement.json", 17 | "control-config-url": "https://calm.finos.org/release/1.0-rc1/prototype/rbac-config.json" 18 | } 19 | ] 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /calm/release/1.0-rc1/prototype/interfaces/kafka-topic.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2020-12/schema", 3 | "$id": "https://calm.finos.org/draft/1083/interfaces/kafka-topic", 4 | "title": "Kafka Topic Interface", 5 | "type": "object", 6 | "properties": { 7 | "topic": { 8 | "type": "string", 9 | "description": "The name of the Kafka topic" 10 | }, 11 | "host": { 12 | "type": "string", 13 | "format": "hostname", 14 | "description": "The Kafka broker hostname or IP" 15 | }, 16 | "port": { 17 | "type": "integer", 18 | "description": "The port on which the Kafka broker is exposed", 19 | "minimum": 1, 20 | "maximum": 65535 21 | } 22 | }, 23 | "required": ["topic", "host", "port"] 24 | } -------------------------------------------------------------------------------- /calm/release/1.0-rc1/prototype/rbac-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "access-model": "RBAC", 3 | "multi-factor-authentication": true, 4 | "session-timeout": 30, 5 | "password-policy": { 6 | "minimum-length": 12, 7 | "require-special-chars": true, 8 | "require-numbers": true, 9 | "require-mixed-case": true 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /calm/release/1.0-rc1/prototype/throughput-control-prototype.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2020-12/schema", 3 | "$id": "https://calm.finos.org/traderx/control-requirement/throughput", 4 | "title": "Throughput Requirement", 5 | "type": "object", 6 | "allOf": [ 7 | { 8 | "$ref": "https://calm.finos.org/release/1.0-rc1/meta/control-requirement.json" 9 | } 10 | ], 11 | "properties": { 12 | "expected-message-rate": { 13 | "$ref": "https://calm.finos.org/release/1.0-rc1/meta/units.json#/defs/rate-unit", 14 | "description": "Define the expected message rate that the flow should handle (e.g., 1000 per second)." 15 | } 16 | }, 17 | "required": [ 18 | "expected-message-rate" 19 | ] 20 | } -------------------------------------------------------------------------------- /calm/samples/2024-10/traderx/README.md: -------------------------------------------------------------------------------- 1 | ### TraderX Example 2 | 3 | [TraderX](https://github.com/finos/traderX) is a Sample Trading Application, designed to be a distributed reference application in the financial services domain. 4 | 5 | This sample uses the [C4 model](https://github.com/finos/traderX/blob/main/docs/c4/c4-diagram.png) of the TraderX application and shows how to model it using CALM. 6 | 7 | You can see in thus sample how you can use the base CALM vocab is both JSON and YAML documents. -------------------------------------------------------------------------------- /calm/samples/2024-12/traderx/README.md: -------------------------------------------------------------------------------- 1 | ### TraderX Example 2 | 3 | [TraderX](https://github.com/finos/traderX) is a Sample Trading Application, designed to be a distributed reference application in the financial services domain. 4 | 5 | This sample uses the [C4 model](https://github.com/finos/traderX/blob/main/docs/c4/c4-diagram.png) of the TraderX application and shows how to model it using CALM. 6 | 7 | You can see in thus sample how you can use the base CALM vocab is both JSON and YAML documents. -------------------------------------------------------------------------------- /calm/samples/visualization.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/finos/architecture-as-code/a54518a8317eb889ea2541210c0297e30e9d880f/calm/samples/visualization.png -------------------------------------------------------------------------------- /calm/workshop/cached/cluster_start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euo pipefail 4 | 5 | SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 6 | 7 | KUBERNETES_VERSION=1.30.0 8 | 9 | echo "Starting Minikube..." 10 | minikube start --network-plugin=cni --cni=calico --kubernetes-version=$KUBERNETES_VERSION --cpus=4 --profile secure --wait=none --install-addons=false 11 | 12 | echo "Enabling global Calico default-deny policy..." 13 | kubectl apply --filename "${SCRIPT_DIR}/calico-global-deny.yaml" --namespace kube-system 14 | -------------------------------------------------------------------------------- /calm/workshop/controls/micro-segmentation.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://calm.finos.org/workshop/controls/micro-segmentation.requirement.json", 3 | "$id": "https://calm.finos.org/workshop/controls/micro-segmentation.config.json", 4 | "control-id": "security-001", 5 | "name": "Micro-segmentation of Kubernetes Cluster", 6 | "description": "Micro-segmentation in place to prevent lateral movement outside of permitted flows", 7 | "permit-ingress": true, 8 | "permit-egress": false 9 | } -------------------------------------------------------------------------------- /calm/workshop/controls/micro-segmentation.requirement.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2020-12/schema", 3 | "$id": "https://calm.finos.org/workshop/controls/micro-segmentation.requirement.json", 4 | "title": "Micro-segmentation configured Kubernetes Cluster", 5 | "type": "object", 6 | "allOf": [ 7 | { 8 | "$ref": "https://calm.finos.org/release/1.0-rc1/meta/control-requirement.json" 9 | } 10 | ], 11 | "properties": { 12 | "control-id": { 13 | "const": "security-001" 14 | }, 15 | "name": { 16 | "const": "Micro-segmentation of Kubernetes Cluster" 17 | }, 18 | "description": { 19 | "const": "Micro-segmentation in place to prevent lateral movement outside of permitted flows" 20 | }, 21 | "permit-ingress": { 22 | "type": "boolean" 23 | }, 24 | "permit-egress": { 25 | "type": "boolean" 26 | } 27 | }, 28 | "required": [ 29 | "control-id", 30 | "name", 31 | "description", 32 | "permit-ingress", 33 | "permit-egress" 34 | ] 35 | } -------------------------------------------------------------------------------- /calm/workshop/controls/permitted-connection-http.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://calm.finos.org/workshop/controls/permitted-connection.requirement.json", 3 | "control-id": "security-002", 4 | "name": "Permitted Connection", 5 | "description": "Permits a connection on a relationship specified in the architecture", 6 | "reason": "Required to enable flow between architecture components", 7 | "protocol": "HTTP" 8 | } -------------------------------------------------------------------------------- /calm/workshop/controls/permitted-connection-jdbc.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://calm.finos.org/workshop/controls/permitted-connection.requirement.json", 3 | "control-id": "security-002", 4 | "name": "Permitted Connection", 5 | "description": "Permits a connection on a relationship specified in the architecture", 6 | "reason": "Permitted to allow the connection between application and database", 7 | "protocol": "JDBC" 8 | } -------------------------------------------------------------------------------- /calm/workshop/directory.json: -------------------------------------------------------------------------------- 1 | { 2 | "https://calm.finos.org/release/1.0-rc1/meta/control-requirement.json": "../release/1.0-rc1/meta/control-requirement.json", 3 | "https://calm.finos.org/workshop/controls/micro-segmentation.config.json": "controls/micro-segmentation.config.json", 4 | "https://calm.finos.org/workshop/controls/micro-segmentation.requirement.json": "controls/micro-segmentation.requirement.json", 5 | "https://calm.finos.org/workshop/controls/permitted-connection.requirement.json": "controls/permitted-connection.requirement.json", 6 | "https://calm.finos.org/workshop/controls/permitted-connection-http.config.json": "controls/permitted-connection-http.config.json", 7 | "https://calm.finos.org/workshop/controls/permitted-connection-jdbc.config.json": "controls/permitted-connection-jdbc.config.json" 8 | } 9 | -------------------------------------------------------------------------------- /calm/workshop/insecure-example/cluster/cluster_start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euo pipefail 4 | 5 | SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 6 | 7 | KUBERNETES_VERSION=1.30.0 8 | 9 | echo "Starting Minikube..." 10 | minikube start --kubernetes-version=$KUBERNETES_VERSION --cpus=4 --profile insecure --wait=none --install-addons=false 11 | -------------------------------------------------------------------------------- /calm/workshop/insecure-example/kubernetes/application-deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: attendees 5 | spec: 6 | replicas: 1 7 | selector: 8 | matchLabels: 9 | app: attendees 10 | template: 11 | metadata: 12 | labels: 13 | app: attendees 14 | spec: 15 | containers: 16 | - name: app 17 | image: masteringapi/attendees-quarkus:ws-native-db 18 | imagePullPolicy: IfNotPresent 19 | ports: 20 | - containerPort: 8080 -------------------------------------------------------------------------------- /calm/workshop/insecure-example/kubernetes/application-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: attendees-service 5 | spec: 6 | selector: 7 | app: attendees 8 | type: LoadBalancer 9 | ports: 10 | - protocol: TCP 11 | port: 80 12 | targetPort: 8080 -------------------------------------------------------------------------------- /calm/workshop/insecure-example/kubernetes/database-deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: attendees-store 5 | spec: 6 | replicas: 1 7 | selector: 8 | matchLabels: 9 | db: attendees-store 10 | template: 11 | metadata: 12 | labels: 13 | db: attendees-store 14 | spec: 15 | containers: 16 | - name: db 17 | image: postgres 18 | imagePullPolicy: IfNotPresent 19 | ports: 20 | - containerPort: 5432 21 | env: 22 | - name: POSTGRES_DB 23 | value: conference 24 | - name: POSTGRES_USER 25 | value: calm 26 | - name: POSTGRES_PASSWORD 27 | value: demo 28 | resources: 29 | requests: 30 | memory: "128Mi" # Equivalent to shm_size: 128mb in Docker Compose -------------------------------------------------------------------------------- /calm/workshop/insecure-example/kubernetes/database-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: db 5 | spec: 6 | selector: 7 | db: attendees-store 8 | ports: 9 | - protocol: TCP 10 | port: 5432 11 | targetPort: 5432 12 | type: ClusterIP -------------------------------------------------------------------------------- /calm/workshop/insecure-example/kubernetes/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | namespace: conference 5 | 6 | resources: 7 | - namespace.yaml 8 | - application-deployment.yaml 9 | - application-service.yaml 10 | - database-deployment.yaml 11 | - database-service.yaml -------------------------------------------------------------------------------- /calm/workshop/insecure-example/kubernetes/namespace.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: conference -------------------------------------------------------------------------------- /calm/workshop/secure-infra-template-bundle/application-deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: {{ appName }} 5 | spec: 6 | replicas: 1 7 | selector: 8 | matchLabels: 9 | app: {{ appName }} 10 | template: 11 | metadata: 12 | labels: 13 | app: {{ appName }} 14 | spec: 15 | containers: 16 | - name: app 17 | image: {{ applicationImage }} 18 | imagePullPolicy: IfNotPresent 19 | ports: 20 | - containerPort: {{ applicationPort }} -------------------------------------------------------------------------------- /calm/workshop/secure-infra-template-bundle/application-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: {{ appName }}-service 5 | spec: 6 | selector: 7 | app: {{ appName }} 8 | type: LoadBalancer 9 | ports: 10 | - protocol: TCP 11 | port: {{ lbPort }} 12 | targetPort: {{ applicationPort }} -------------------------------------------------------------------------------- /calm/workshop/secure-infra-template-bundle/calico-global-deny.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: crd.projectcalico.org/v1 2 | kind: GlobalNetworkPolicy 3 | metadata: 4 | name: deny-app-policy 5 | spec: 6 | namespaceSelector: has(projectcalico.org/name) && projectcalico.org/name not in {"kube-system"} 7 | types: 8 | - Ingress 9 | - Egress 10 | egress: 11 | # allow all namespaces to communicate to DNS pods 12 | - action: Allow 13 | protocol: UDP 14 | destination: 15 | selector: 'k8s-app == "kube-dns"' 16 | ports: 17 | - 53 18 | - action: Allow 19 | protocol: TCP 20 | destination: 21 | selector: 'k8s-app == "kube-dns"' 22 | ports: 23 | - 53 -------------------------------------------------------------------------------- /calm/workshop/secure-infra-template-bundle/cluster_start.hbs: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euo pipefail 4 | 5 | SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 6 | 7 | KUBERNETES_VERSION=1.30.0 8 | 9 | echo "Starting Minikube..." 10 | minikube start --network-plugin=cni --cni=calico --kubernetes-version=$KUBERNETES_VERSION --cpus=4 --profile secure --wait=none --install-addons=false 11 | 12 | {{#if secure}} 13 | echo "Enabling global Calico default-deny policy..." 14 | kubectl apply --filename "${SCRIPT_DIR}/calico-global-deny.yaml" --namespace kube-system 15 | {{/if}} -------------------------------------------------------------------------------- /calm/workshop/secure-infra-template-bundle/database-deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: {{ databaseName }} 5 | spec: 6 | replicas: 1 7 | selector: 8 | matchLabels: 9 | db: {{ databaseName }} 10 | template: 11 | metadata: 12 | labels: 13 | db: {{ databaseName }} 14 | spec: 15 | containers: 16 | - name: db 17 | image: postgres 18 | imagePullPolicy: IfNotPresent 19 | ports: 20 | - containerPort: {{ databasePort }} 21 | env: 22 | - name: POSTGRES_DB 23 | value: conference 24 | - name: POSTGRES_USER 25 | value: calm 26 | - name: POSTGRES_PASSWORD 27 | value: demo 28 | resources: 29 | requests: 30 | memory: "128Mi" # Equivalent to shm_size: 128mb in Docker Compose -------------------------------------------------------------------------------- /calm/workshop/secure-infra-template-bundle/database-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: db 5 | spec: 6 | selector: 7 | db: {{ databaseName }} 8 | ports: 9 | - protocol: TCP 10 | port: 5432 11 | targetPort: 5432 12 | type: ClusterIP -------------------------------------------------------------------------------- /calm/workshop/secure-infra-template-bundle/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | namespace: {{ namespaceName }} 5 | 6 | resources: 7 | - namespace.yaml 8 | - application-deployment.yaml 9 | - application-service.yaml 10 | - database-deployment.yaml 11 | - database-service.yaml 12 | - permit-app-from-db.yaml 13 | - permit-app-to-db.yaml 14 | - permit-lb-to-app.yaml -------------------------------------------------------------------------------- /calm/workshop/secure-infra-template-bundle/namespace.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: {{ namespaceName }} -------------------------------------------------------------------------------- /calm/workshop/secure-infra-template-bundle/permit-app-from-db.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.k8s.io/v1 2 | kind: NetworkPolicy 3 | metadata: 4 | name: allow-ingress-to-db-from-app 5 | spec: 6 | podSelector: 7 | matchLabels: 8 | db: {{ databaseName }} 9 | ingress: 10 | - from: 11 | - podSelector: 12 | matchLabels: 13 | app: {{ appName }} 14 | policyTypes: 15 | - Ingress -------------------------------------------------------------------------------- /calm/workshop/secure-infra-template-bundle/permit-app-to-db.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.k8s.io/v1 2 | kind: NetworkPolicy 3 | metadata: 4 | name: allow-egress-from-app-to-db 5 | spec: 6 | podSelector: 7 | matchLabels: 8 | app: {{ appName }} 9 | egress: 10 | - to: 11 | - podSelector: 12 | matchLabels: 13 | db: {{ databaseName }} 14 | policyTypes: 15 | - Egress -------------------------------------------------------------------------------- /calm/workshop/secure-infra-template-bundle/permit-lb-to-app.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.k8s.io/v1 2 | kind: NetworkPolicy 3 | metadata: 4 | name: allow-external-ingress-to-app 5 | spec: 6 | podSelector: 7 | matchLabels: 8 | app: {{ appName }} 9 | ingress: 10 | - {} 11 | policyTypes: 12 | - Ingress -------------------------------------------------------------------------------- /cli/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /cli/pom.xml: -------------------------------------------------------------------------------- 1 | 4 | 4.0.0 5 | 6 | 7 | org.finos.architecture-as-code 8 | parent 9 | 1.0.0-SNAPSHOT 10 | 11 | 12 | cli 13 | pom 14 | 15 | Placeholder module for CLI 16 | 17 | -------------------------------------------------------------------------------- /cli/src/cli-config.ts: -------------------------------------------------------------------------------- 1 | import { initLogger } from '@finos/calm-shared'; 2 | import { readFile } from 'fs/promises'; 3 | import { homedir } from 'os'; 4 | import { join } from 'path'; 5 | 6 | export interface CLIConfig { 7 | calmHubUrl?: string 8 | } 9 | 10 | function getUserConfigLocation(): string { 11 | const homeDir = homedir(); 12 | return join(homeDir, '.calm.json'); 13 | } 14 | 15 | export async function loadCliConfig(): Promise { 16 | const logger = initLogger(false, 'calm-cli'); 17 | 18 | const configFilePath = getUserConfigLocation(); 19 | try { 20 | const config = await readFile(configFilePath, 'utf8'); 21 | const parsed = JSON.parse(config) as CLIConfig; 22 | logger.debug('Parsed user config: ' + config); 23 | return parsed; 24 | } 25 | catch (err) { 26 | if (err.code === 'ENOENT') { 27 | logger.debug('No config file found at ' + configFilePath); 28 | return null; 29 | } 30 | logger.error('Unexpected error loading user config: ', err); 31 | return null; 32 | } 33 | } -------------------------------------------------------------------------------- /cli/src/command-helpers/calmhub-input.ts: -------------------------------------------------------------------------------- 1 | import { initLogger } from '@finos/calm-shared'; 2 | import { DocumentLoader } from '@finos/calm-shared/dist/document-loader/document-loader'; 3 | 4 | export async function loadPatternFromCalmHub(patternId: string, docLoader: DocumentLoader, debug: boolean): Promise { 5 | const logger = initLogger(debug, 'calmhub-input'); 6 | try { 7 | logger.info('Loading input pattern from CalmHub with ID: ' + patternId); 8 | 9 | const pattern = await docLoader.loadMissingDocument(patternId, 'pattern'); 10 | 11 | logger.debug('Loaded pattern JSON.'); 12 | return pattern; 13 | } catch (err) { 14 | logger.error('Error loading input from CalmHub. Status code: ', err.response.status); 15 | logger.debug('Error loading input from CalmHub: ', err); 16 | throw new Error(err); 17 | } 18 | } -------------------------------------------------------------------------------- /cli/src/command-helpers/file-input.spec.ts: -------------------------------------------------------------------------------- 1 | import { loadJsonFromFile } from './file-input'; 2 | 3 | const mocks = vi.hoisted(() => { 4 | return { 5 | readFile: vi.fn() 6 | }; 7 | }); 8 | 9 | vi.mock('node:fs/promises', async () => { 10 | return { 11 | readFile: mocks.readFile 12 | }; 13 | }); 14 | 15 | describe('fileInput', () => { 16 | it('should read a file and return its content as an object', async () => { 17 | mocks.readFile.mockReturnValue(JSON.stringify({ key: 'value' })); 18 | 19 | await expect(loadJsonFromFile('test.json', false)).resolves.toEqual({ key: 'value' }); 20 | expect(mocks.readFile).toHaveBeenCalledWith('test.json', 'utf-8'); 21 | }); 22 | 23 | it('should pass along error if a random error is thrown', async () => { 24 | mocks.readFile.mockImplementation(() => { 25 | throw new Error('Random error'); 26 | }); 27 | 28 | await expect(loadJsonFromFile('error.json', false)).rejects.toThrow('Random error'); 29 | expect(mocks.readFile).toHaveBeenCalledWith('error.json', 'utf-8'); 30 | }); 31 | }); -------------------------------------------------------------------------------- /cli/src/command-helpers/file-input.ts: -------------------------------------------------------------------------------- 1 | import * as fs from 'node:fs/promises'; 2 | import { initLogger } from '@finos/calm-shared'; 3 | 4 | export async function loadJsonFromFile(path: string, debug: boolean): Promise { 5 | const logger = initLogger(debug, 'file-input'); 6 | try { 7 | logger.info('Loading json from file: ' + path); 8 | const raw = await fs.readFile(path, 'utf-8'); 9 | 10 | logger.debug('Attempting to load json file: ' + raw); 11 | const pattern = JSON.parse(raw); 12 | 13 | logger.debug('Loaded json file.'); 14 | return pattern; 15 | } catch (err) { 16 | if (err.code === 'ENOENT') { 17 | logger.error('File not found!'); 18 | } else { 19 | logger.error(err); 20 | } 21 | throw new Error(err); 22 | } 23 | } -------------------------------------------------------------------------------- /cli/src/command-helpers/template.ts: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import fs from 'node:fs'; 3 | 4 | export function getUrlToLocalFileMap(urlToLocalFileMapping?: string): Map { 5 | if (!urlToLocalFileMapping) { 6 | return new Map(); 7 | } 8 | 9 | try { 10 | const basePath = path.dirname(urlToLocalFileMapping); 11 | const mappingJson = JSON.parse(fs.readFileSync(urlToLocalFileMapping, 'utf-8')); 12 | 13 | return new Map( 14 | Object.entries(mappingJson).map(([url, relativePath]) => [ 15 | url, 16 | path.resolve(basePath, String(relativePath)) 17 | ]) 18 | ); 19 | } catch (err) { 20 | console.error(`Error reading url to local file mapping file: ${urlToLocalFileMapping}`, err); 21 | process.exit(1); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /cli/src/index.ts: -------------------------------------------------------------------------------- 1 | import { program } from 'commander'; 2 | import { setupCLI } from './cli'; 3 | 4 | setupCLI(program); 5 | program.parse(process.argv); -------------------------------------------------------------------------------- /cli/src/server/cli-server.ts: -------------------------------------------------------------------------------- 1 | import express, { Application } from 'express'; 2 | import { CLIServerRoutes } from './routes/routes'; 3 | import { initLogger, SchemaDirectory } from '@finos/calm-shared'; 4 | 5 | export function startServer(port: string, schemaDirectory: SchemaDirectory, verbose: boolean) { 6 | const app: Application = express(); 7 | const cliServerRoutesInstance = new CLIServerRoutes(schemaDirectory, verbose); 8 | const allRoutes = cliServerRoutesInstance.router; 9 | 10 | app.use(express.json()); 11 | app.use('/', allRoutes); 12 | 13 | app.listen(port, () => { 14 | const logger = initLogger(verbose, 'calm-server'); 15 | logger.info(`CALM Server is running on http://localhost:${port}`); 16 | }); 17 | } -------------------------------------------------------------------------------- /cli/src/server/routes/health-route.spec.ts: -------------------------------------------------------------------------------- 1 | import request from 'supertest'; 2 | 3 | import express, { Application } from 'express'; 4 | import { HealthRouter } from './health-route'; 5 | 6 | describe('HealthRouter', () => { 7 | let app: Application; 8 | 9 | beforeEach(() => { 10 | app = express(); 11 | app.use(express.json()); 12 | 13 | const router: express.Router = express.Router(); 14 | app.use('/health', router); 15 | new HealthRouter(router); 16 | 17 | }); 18 | 19 | test('should return 200 for health check', async () => { 20 | const response = await request(app) 21 | .get('/health'); 22 | expect(response.status).toBe(200); 23 | expect(response.body).toEqual({ status: 'OK' }); 24 | }); 25 | }); 26 | 27 | // }); -------------------------------------------------------------------------------- /cli/src/server/routes/health-route.ts: -------------------------------------------------------------------------------- 1 | import { Router, Request, Response } from 'express'; 2 | 3 | 4 | export class HealthRouter { 5 | 6 | constructor(router: Router) { 7 | router.get('/', this.healthCheck); 8 | } 9 | 10 | private healthCheck(_req: Request, res: Response) { 11 | res.status(200).type('json').send(new StatusResponse('OK')); 12 | } 13 | 14 | } 15 | 16 | class StatusResponse { 17 | status: string; 18 | 19 | constructor(status: string) { 20 | this.status = status; 21 | } 22 | } -------------------------------------------------------------------------------- /cli/src/server/routes/routes.ts: -------------------------------------------------------------------------------- 1 | import { Router } from 'express'; 2 | import { ValidationRouter } from './validation-route'; 3 | import { HealthRouter } from './health-route'; 4 | import { SchemaDirectory } from '@finos/calm-shared'; 5 | 6 | const HEALTH_ROUTE_PATH = '/health'; 7 | const VALIDATE_ROUTE_PATH = '/calm/validate'; 8 | 9 | export class CLIServerRoutes { 10 | router: Router; 11 | 12 | constructor(schemaDirectory: SchemaDirectory, debug: boolean = false) { 13 | this.router = Router(); 14 | const validateRoute = this.router.use(VALIDATE_ROUTE_PATH, this.router); 15 | new ValidationRouter(validateRoute, schemaDirectory, debug); 16 | const healthRoute = this.router.use(HEALTH_ROUTE_PATH, this.router); 17 | new HealthRouter(healthRoute); 18 | } 19 | } -------------------------------------------------------------------------------- /cli/test_fixtures/template/model/flows/flow-document-upload.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://calm.finos.org/draft/2024-12/meta/flow.json", 3 | "$id": "https://calm.finos.org/docuflow/flow/document-upload", 4 | "unique-id": "flow-document-upload", 5 | "name": "Document Upload Flow", 6 | "description": "Flow for uploading a document and storing metadata securely in the DocuFlow system.", 7 | "transitions": [ 8 | { 9 | "relationship-unique-id": "rel-upload-to-storage", 10 | "sequence-number": 1, 11 | "summary": "Upload Service sends document to Storage Service" 12 | }, 13 | { 14 | "relationship-unique-id": "rel-storage-to-db", 15 | "sequence-number": 2, 16 | "summary": "Storage Service stores document metadata in Document Database" 17 | } 18 | ] 19 | } -------------------------------------------------------------------------------- /cli/test_fixtures/template/model/url-to-file-directory.json: -------------------------------------------------------------------------------- 1 | { "https://calm.finos.org/docuflow/flow/document-upload" : "flows/flow-document-upload.json"} -------------------------------------------------------------------------------- /cli/test_fixtures/template/template-bundles/doc-system/basic-transformer.js: -------------------------------------------------------------------------------- 1 | module.exports = class BasicTransformer { 2 | registerTemplateHelpers() { 3 | return {}; 4 | } 5 | 6 | getTransformedModel(rawJson) { 7 | const document = JSON.parse(rawJson); 8 | return { 9 | document: document 10 | }; 11 | } 12 | }; 13 | -------------------------------------------------------------------------------- /cli/test_fixtures/template/template-bundles/doc-system/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "CLI E2E Template Endpoint Test Bundle", 3 | "transformer": "basic-transformer", 4 | "templates": [ 5 | { 6 | "template": "main.html", 7 | "from": "document", 8 | "output": "cli-e2e-output.html", 9 | "output-type": "single" 10 | } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /cli/test_fixtures/template/template-bundles/doc-system/main.html: -------------------------------------------------------------------------------- 1 |

{{title}}

2 |

{{description}}

3 | 4 |

Nodes

5 |
    6 | {{#each nodes}} 7 |
  • {{name}} ({{node-type}}): {{description}}
  • 8 | {{/each}} 9 |
10 | 11 |

Relationships

12 |
    13 | {{#each relationships}} 14 |
  • {{description}}
  • 15 | {{/each}} 16 |
17 |

Flows

18 | {{#each flows}} 19 | {{#if unique-id}} 20 |
21 |

{{name}} ({{unique-id}})

22 |

{{description}}

23 |

Transitions

24 |
    25 | {{#each transitions}} 26 |
  • Sequence {{sequence-number}}: {{summary}} (Relationship: {{relationship-unique-id}})
  • 27 | {{/each}} 28 |
29 |
30 | {{else}} 31 |
{{this}}
32 | {{/if}} 33 | {{/each}} 34 | -------------------------------------------------------------------------------- /cli/test_fixtures/validate_architecture_only_output.json: -------------------------------------------------------------------------------- 1 | { 2 | "jsonSchemaValidationOutputs": [], 3 | "spectralSchemaValidationOutputs": [ 4 | { 5 | "code": "architecture-has-no-placeholder-properties-numerical", 6 | "severity": "warning", 7 | "message": "Numerical placeholder (-1) detected in architecture.", 8 | "path": "/nodes/2/interfaces/0/port", 9 | "schemaPath": "", 10 | "line_start": 32, 11 | "line_end": 32, 12 | "character_start": 18, 13 | "character_end": 20 14 | } 15 | ], 16 | "hasErrors": false, 17 | "hasWarnings": true 18 | } -------------------------------------------------------------------------------- /cli/test_fixtures/validate_output.json: -------------------------------------------------------------------------------- 1 | { 2 | "jsonSchemaValidationOutputs": [], 3 | "spectralSchemaValidationOutputs": [ 4 | { 5 | "code": "architecture-has-no-placeholder-properties-numerical", 6 | "severity": "warning", 7 | "message": "Numerical placeholder (-1) detected in architecture.", 8 | "path": "/nodes/2/interfaces/0/port", 9 | "schemaPath": "", 10 | "line_start": 32, 11 | "line_end": 32, 12 | "character_start": 18, 13 | "character_end": 20 14 | } 15 | ], 16 | "hasErrors": false, 17 | "hasWarnings": true 18 | } -------------------------------------------------------------------------------- /cli/test_fixtures/validate_output_pretty.txt: -------------------------------------------------------------------------------- 1 | 2 | | Issue Type | Issues Found? | Issue Count | 3 | -------------------------------------------- 4 | | Errors | false | 0 | 5 | | Warnings | true | 1 | 6 | 7 | 8 | Warnings: 9 | 10 | | code | severity | message | path | schemaPath | line_start | line_end | character_start | character_end | 11 | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ 12 | | architecture-has-no-placeholder-properties-numerical | warning | Numerical placeholder (-1) detected in architecture. | /nodes/2/interfaces/0/port | | 32 | 32 | 18 | 20 | 13 | -------------------------------------------------------------------------------- /cli/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "exclude": ["**/*.spec.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /cli/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.base.json", 3 | "module": "Preserve", 4 | "moduleResolution": "bundler", 5 | "compilerOptions": { 6 | "outDir": "dist", 7 | }, 8 | "include": ["src", "../vitest-globals.d.ts"], 9 | "lib": [ 10 | "esnext" 11 | ] 12 | 13 | } 14 | -------------------------------------------------------------------------------- /cli/tsup.config.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | import { defineConfig } from 'tsup'; 4 | 5 | export default defineConfig({ 6 | entry: ['src/index.ts'], 7 | format: ['cjs'], 8 | sourcemap: false, 9 | clean: true, 10 | external: ['canvas', 'fsevents', '@apidevtools/json-schema-ref-parser', /node_modules/, 'ts-node'], 11 | noExternal: ['@finos/calm-shared', /tsup/], 12 | bundle: true, 13 | splitting: false, 14 | minify: false, 15 | shims: true, 16 | target: 'es2021', 17 | treeshake: true, 18 | banner: ({ format }) => { 19 | if (format === 'cjs') { 20 | return { 21 | js: '#! /usr/bin/env node' 22 | }; 23 | } 24 | } 25 | }); -------------------------------------------------------------------------------- /cli/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import {defineConfig} from 'vitest/config'; 2 | import {CoverageV8Options} from "vitest/node"; 3 | 4 | const v8CoverageSettings: CoverageV8Options = { 5 | enabled: true, 6 | reporter: ['text', 'json', 'html'], 7 | thresholds: { 8 | branches: 85, 9 | functions: 75, 10 | lines: 75, 11 | statements: 75 12 | } 13 | } 14 | 15 | export default defineConfig({ 16 | test: { 17 | globals: true, 18 | environment: 'node', 19 | coverage: { 20 | provider: 'v8', 21 | ...v8CoverageSettings, 22 | }, 23 | 24 | } 25 | }) -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | /node_modules 3 | 4 | # Production 5 | /build 6 | 7 | # Generated files 8 | .docusaurus 9 | .cache-loader 10 | 11 | # Misc 12 | .DS_Store 13 | .env.local 14 | .env.development.local 15 | .env.test.local 16 | .env.production.local 17 | 18 | npm-debug.log* 19 | yarn-debug.log* 20 | yarn-error.log* 21 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Formal Documentation 2 | 3 | https://calm.finos.org/ 4 | -------------------------------------------------------------------------------- /docs/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [require.resolve('@docusaurus/core/lib/babel/preset')], 3 | }; 4 | -------------------------------------------------------------------------------- /docs/calm/intro.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 1 3 | slug: / 4 | --- 5 | 6 | # CALM 7 | 8 | -------------------------------------------------------------------------------- /docs/community/intro.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 1 3 | slug: / 4 | --- 5 | 6 | # Community 7 | 8 | -------------------------------------------------------------------------------- /docs/docs/core-concepts/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: core-concepts-index 3 | title: Core Concepts 4 | sidebar_position: 2 5 | --- 6 | 7 | # Core Concepts 8 | 9 | Welcome to the Core Concepts section of CALM. This section provides a comprehensive understanding of the primary components that make up CALM, including nodes, interfaces, relationships, and metadata. By mastering these concepts, you can effectively define and manage your software architecture using CALM. 10 | 11 | Explore each concept below: 12 | 13 | - [Nodes](nodes): Learn about the primary building blocks of your architecture. 14 | - [Interfaces](interfaces): Understand how nodes expose interaction points. 15 | - [Relationships](relationships): Discover how nodes connect and interact. 16 | - [Controls](controls): Understand how to apply domain controls to your architecture 17 | - [Metadata](metadata): Learn how to enrich your architecture with additional information. 18 | 19 | Continue through each section to get detailed explanations, examples, and best practices. 20 | -------------------------------------------------------------------------------- /docs/docs/introduction/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: introduction-index 3 | title: Introduction 4 | sidebar_position: 1 5 | --- 6 | 7 | # Introduction to CALM 8 | 9 | Welcome to the Introduction section of CALM. Here, you will find an overview of the Common Architecture Language Model (CALM), its purpose, and its core features. This section will help you understand the motivation behind CALM and how it can bring consistency, automation, and control to your software architecture. 10 | 11 | Explore each topic below to get started: 12 | 13 | - [What is CALM?](what-is-calm): Learn about CALM, its purpose, and how it addresses common challenges in software architecture. 14 | - [Why Use CALM?](why-use-calm): Discover the benefits of using CALM, including standardization, automation, and compliance. 15 | - [Key Features](key-features): Explore the key features of CALM, including patterns, validation, and visualization. 16 | 17 | Continue through each section to gain a deeper understanding of how CALM can transform your approach to architecture. 18 | -------------------------------------------------------------------------------- /docs/docs/working-with-calm/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: working-with-calm-index 3 | title: Working with CALM 4 | sidebar_position: 3 5 | --- 6 | 7 | # Working with CALM 8 | 9 | This section will guide you through the practical aspects of using CALM to define, validate, and visualize your software architecture. From installing the CLI to understanding the key commands, you’ll learn how to integrate CALM into your workflow effectively. 10 | 11 | Explore the topics below to get hands-on experience with CALM: 12 | 13 | - [Installation](installation): Learn how to set up the CALM CLI on your machine. 14 | - [Using the CLI](using-the-cli): Understand the basic usage of the CALM CLI and how to access its commands. 15 | - [Generate](generate): Discover how to generate architectural architectures from predefined patterns. 16 | - [Validate](validate): Learn how to validate your architecture against CALM patterns to ensure compliance. 17 | 18 | Let's get started and make the most of CALM’s capabilities! 19 | -------------------------------------------------------------------------------- /docs/docs/working-with-calm/using-the-cli.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: using-the-cli 3 | title: Using the CLI 4 | sidebar_position: 3 5 | --- 6 | 7 | # Using the CLI 8 | 9 | The CALM CLI provides a set of commands that allow you to interact with CALM’s architecture model. This section will cover the basics of using the CLI, including accessing help, understanding command structure, and common options. 10 | 11 | ## Basic CLI Usage 12 | 13 | Once installed, you can access the CLI by typing `calm` in your terminal. This command will display the help text and available commands: 14 | 15 | ```shell 16 | calm 17 | ``` 18 | 19 | You should see output similar to the following: 20 | 21 | ```shell 22 | Usage: calm [options] [command] 23 | 24 | A set of tools for interacting with the Common Architecture Language Model (CALM) 25 | 26 | Options: -V, --version output the version number -h, --help display help for command 27 | 28 | Commands: 29 | generate [options] Generate an architecture from a CALM pattern file. 30 | validate [options] Validate that an architecture conforms to a given CALM pattern. 31 | help [command] display help for command 32 | 33 | ``` -------------------------------------------------------------------------------- /docs/pom.xml: -------------------------------------------------------------------------------- 1 | 4 | 4.0.0 5 | 6 | 7 | org.finos.architecture-as-code 8 | parent 9 | 1.0.0-SNAPSHOT 10 | 11 | 12 | docs 13 | pom 14 | 15 | Placeholder module for Docs 16 | 17 | -------------------------------------------------------------------------------- /docs/sidebars.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Creating a sidebar enables you to: 3 | - create an ordered group of docs 4 | - render a sidebar for each doc of that group 5 | - provide next/previous navigation 6 | 7 | The sidebars can be generated from the filesystem, or explicitly defined here. 8 | 9 | Create as many sidebars as you want. 10 | */ 11 | 12 | // @ts-check 13 | 14 | /** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ 15 | const sidebars = { 16 | // By default, Docusaurus generates a sidebar from the docs folder structure 17 | docsSidebar: [{type: 'autogenerated', dirName: '.'}], 18 | 19 | // But you can create a sidebar manually 20 | /* 21 | tutorialSidebar: [ 22 | 'intro', 23 | 'hello', 24 | { 25 | type: 'category', 26 | label: 'Tutorial', 27 | items: ['tutorial-basics/create-a-document'], 28 | }, 29 | ], 30 | */ 31 | }; 32 | 33 | export default sidebars; 34 | -------------------------------------------------------------------------------- /docs/src/components/HomepageFeatures/styles.module.css: -------------------------------------------------------------------------------- 1 | .features { 2 | display: flex; 3 | align-items: center; 4 | padding: 2rem 0; 5 | width: 100%; 6 | } 7 | 8 | .featureSvg { 9 | height: 200px; 10 | width: 200px; 11 | } 12 | -------------------------------------------------------------------------------- /docs/src/css/custom.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Any CSS included here will be global. The classic template 3 | * bundles Infima by default. Infima is a CSS framework designed to 4 | * work well for content-centric websites. 5 | */ 6 | 7 | /* You can override the default Infima variables here. */ 8 | :root { 9 | --ifm-color-primary: #2e8555; 10 | --ifm-color-primary-dark: #29784c; 11 | --ifm-color-primary-darker: #277148; 12 | --ifm-color-primary-darkest: #205d3b; 13 | --ifm-color-primary-light: #33925d; 14 | --ifm-color-primary-lighter: #359962; 15 | --ifm-color-primary-lightest: #3cad6e; 16 | --ifm-code-font-size: 95%; 17 | --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1); 18 | } 19 | 20 | /* For readability concerns, you should choose a lighter palette in dark mode. */ 21 | [data-theme="dark"] { 22 | --ifm-color-primary: #25c2a0; 23 | --ifm-color-primary-dark: #21af90; 24 | --ifm-color-primary-darker: #1fa588; 25 | --ifm-color-primary-darkest: #1a8870; 26 | --ifm-color-primary-light: #29d5b0; 27 | --ifm-color-primary-lighter: #32d8b4; 28 | --ifm-color-primary-lightest: #4fddbf; 29 | --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3); 30 | } 31 | -------------------------------------------------------------------------------- /docs/src/pages/index.module.css: -------------------------------------------------------------------------------- 1 | /** 2 | * CSS files with the .module.css suffix will be treated as CSS modules 3 | * and scoped locally. 4 | */ 5 | 6 | .heroBanner { 7 | padding: 4rem 0; 8 | text-align: center; 9 | position: relative; 10 | overflow: hidden; 11 | } 12 | 13 | @media screen and (max-width: 996px) { 14 | .heroBanner { 15 | padding: 2rem; 16 | } 17 | } 18 | 19 | .buttons { 20 | display: flex; 21 | align-items: center; 22 | justify-content: center; 23 | } 24 | -------------------------------------------------------------------------------- /docs/src/pages/markdown-page.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Markdown page example 3 | --- 4 | 5 | # Markdown page example 6 | 7 | You don't need React to write simple standalone pages. 8 | -------------------------------------------------------------------------------- /docs/static/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/finos/architecture-as-code/a54518a8317eb889ea2541210c0297e30e9d880f/docs/static/.nojekyll -------------------------------------------------------------------------------- /docs/static/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/finos/architecture-as-code/a54518a8317eb889ea2541210c0297e30e9d880f/docs/static/img/favicon.ico -------------------------------------------------------------------------------- /docs/static/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/finos/architecture-as-code/a54518a8317eb889ea2541210c0297e30e9d880f/docs/static/img/logo.png -------------------------------------------------------------------------------- /docs/static/img/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /prettier.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @see https://prettier.io/docs/en/configuration.html 3 | * @type {import("prettier").Config} 4 | */ 5 | const config = { 6 | trailingComma: "es5", 7 | tabWidth: 4, 8 | semi: true, 9 | singleQuote: true, 10 | }; 11 | 12 | // eslint-disable-next-line no-undef 13 | module.exports = config; 14 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "config:recommended", 5 | ":dependencyDashboard" 6 | ], 7 | "lockFileMaintenance": { 8 | "enabled": false, 9 | "schedule": ["before 7am on monday"] 10 | }, 11 | "packageRules": [ 12 | { 13 | "matchUpdateTypes": ["lockFileMaintenance"], 14 | "automerge": true, 15 | "automergeType": "pr", 16 | "groupName": "lockfile-maintenance" 17 | }, 18 | { 19 | "groupName": "minor-dependencies", 20 | "matchUpdateTypes": ["minor"], 21 | "automerge": true, 22 | "automergeType": "pr" 23 | }, 24 | { 25 | "matchUpdateTypes": ["patch"], 26 | "enabled": false 27 | } 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /shared/pom.xml: -------------------------------------------------------------------------------- 1 | 4 | 4.0.0 5 | 6 | 7 | org.finos.architecture-as-code 8 | parent 9 | 1.0.0-SNAPSHOT 10 | 11 | 12 | shared 13 | pom 14 | 15 | Placeholder module for Shared 16 | 17 | -------------------------------------------------------------------------------- /shared/src/commands/validate/spectral.result.ts: -------------------------------------------------------------------------------- 1 | import { ValidationOutput } from './validation.output'; 2 | 3 | export class SpectralResult { 4 | public errors: boolean; 5 | public warnings: boolean; 6 | public spectralIssues: ValidationOutput[]; 7 | 8 | constructor(warnings: boolean, errors: boolean, spectralIssues: ValidationOutput[]) { 9 | this.warnings = warnings; 10 | this.errors = errors; 11 | this.spectralIssues = spectralIssues; 12 | } 13 | 14 | } -------------------------------------------------------------------------------- /shared/src/consts.ts: -------------------------------------------------------------------------------- 1 | export const CALM_META_SCHEMA_DIRECTORY = __dirname + '/calm/release'; -------------------------------------------------------------------------------- /shared/src/docify/docifier.ts: -------------------------------------------------------------------------------- 1 | import { TemplateProcessor } from '../template/template-processor.js'; 2 | 3 | export type DocifyMode = 'SAD' | 'WEBSITE'; 4 | 5 | export class Docifier { 6 | private static readonly TEMPLATE_BUNDLE_PATHS: Record = { 7 | SAD: __dirname + '/template-bundles/sad', 8 | WEBSITE: __dirname + '/template-bundles/docusaurus', 9 | }; 10 | 11 | private templateProcessor: TemplateProcessor; 12 | 13 | constructor(mode: DocifyMode, inputPath: string, outputPath: string, urlToLocalPathMapping: Map) { 14 | if (mode === 'SAD') { 15 | throw new Error('Mode "SAD" is not supported.'); 16 | } 17 | 18 | const templateBundlePath = Docifier.TEMPLATE_BUNDLE_PATHS[mode]; 19 | 20 | if (!templateBundlePath) { 21 | throw new Error(`Invalid mode: ${mode}`); 22 | } 23 | 24 | this.templateProcessor = new TemplateProcessor(inputPath, templateBundlePath, outputPath, urlToLocalPathMapping); 25 | } 26 | 27 | public async docify(): Promise { 28 | await this.templateProcessor.processTemplate(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /shared/src/docify/template-bundles/docusaurus/c4-container.hbs: -------------------------------------------------------------------------------- 1 | ```mermaid 2 | C4Container 3 | {{#each C4model.elements}} 4 | {{#if (eq this.elementType "System")}} 5 | System_Boundary("{{this.name}}","{{this.description}}"){ 6 | {{#each this.children}} 7 | {{#with (lookup ../../this.C4model.elements this)}} 8 | Container({{this.uniqueId}},"{{this.name}}","","{{this.description}}") 9 | {{/with}} 10 | {{/each}} 11 | } 12 | {{/if}} 13 | {{#if (eq this.elementType "Person")}} 14 | Person({{this.uniqueId}},"{{this.name}}","{{this.description}}") 15 | {{/if}} 16 | 17 | {{#if (eq this.elementType "Container")}} 18 | {{#unless parentId}} 19 | Container({{this.uniqueId}},"{{this.name}}","","{{this.description}}") 20 | {{/unless}} 21 | {{/if}} 22 | {{/each}} 23 | 24 | {{#each C4model.relationships}} 25 | Rel({{this.source}},{{this.destination}},"{{this.relationshipType}}") 26 | {{/each}} 27 | 28 | UpdateLayoutConfig($c4ShapeInRow="2", $c4BoundaryInRow="0") 29 | ``` -------------------------------------------------------------------------------- /shared/src/docify/template-bundles/docusaurus/control-requirement.hbs: -------------------------------------------------------------------------------- 1 | --- 2 | title: {{kebabToTitleCase id}} 3 | --- 4 | ### Specification 5 | 6 | ```json 7 | {{{json this}}} 8 | ``` -------------------------------------------------------------------------------- /shared/src/docify/template-bundles/docusaurus/controls.hbs: -------------------------------------------------------------------------------- 1 | ## Controls 2 | 3 | {{#if controls.length}} 4 | {{#each controls}} 5 | 6 | ### {{kebabToTitleCase controlId}} 7 | 8 | {{description}} 9 | 10 | {{#each requirements}} 11 | {{> table-template.html data=controlConfigUrl}} 12 | {{/each}} 13 | 14 | {{/each}} 15 | {{else}} 16 | _No controls defined._ 17 | {{/if}} 18 | -------------------------------------------------------------------------------- /shared/src/docify/template-bundles/docusaurus/flow-sequence.hbs: -------------------------------------------------------------------------------- 1 | ```mermaid 2 | sequenceDiagram 3 | title {{name}} 4 | {{#each transitions}} 5 | {{#if direction}} 6 | {{#if (eq direction 'destination-to-source')}} 7 | {{target}} -->> {{source}}: {{summary}} 8 | {{else}} 9 | {{source}} ->> {{target}}: {{summary}} 10 | {{/if}} 11 | {{else}} 12 | {{source}} ->> {{target}}: {{summary}} 13 | {{/if}} 14 | {{/each}} 15 | ``` -------------------------------------------------------------------------------- /shared/src/docify/template-bundles/docusaurus/flow.mdx.hbs: -------------------------------------------------------------------------------- 1 | --- 2 | id: {{id}} 3 | title: {{title}} 4 | --- 5 | 6 | ## Flow Overview 7 | {{description}} 8 | 9 | {{> flow-sequence.hbs}} 10 | 11 | {{> controls.hbs}} -------------------------------------------------------------------------------- /shared/src/docify/template-bundles/docusaurus/index.md.hbs: -------------------------------------------------------------------------------- 1 | --- 2 | id: index 3 | title: Welcome to CALM Documentation 4 | sidebar_position: 1 5 | slug: / 6 | --- 7 | 8 | # Welcome to CALM Documentation 9 | 10 | This documentation is generated from the **CALM Architecture-as-Code** model. 11 | 12 | ## High Level Architecture 13 | {{> c4-container.hbs}} 14 | 15 | ### Nodes 16 | {{#each nodes}} 17 | - [{{title}}](nodes/{{slug}}) 18 | {{/each}} 19 | 20 | {{#if (notEmpty flows) }} 21 | ### Flows 22 | {{#each flows}} 23 | - [{{title}}](flows/{{slug}}) 24 | {{/each}} 25 | {{/if}} 26 | 27 | {{#if (notEmpty controls) }} 28 | ### Controls 29 | | ID | Name | Description | Domain | Scope | Applied To | 30 | |-------|------------------|------------------------------|-----------|--------------|---------------------------| 31 | {{#controls}} 32 | |{{id}}|{{name}}|{{description}}|{{domain}}|{{scope}}|{{appliedTo}}| 33 | {{/controls}} 34 | {{/if}} 35 | -------------------------------------------------------------------------------- /shared/src/docify/template-bundles/docusaurus/json-doc.mdx.hbs: -------------------------------------------------------------------------------- 1 | ```json 2 | {{{json this}}} -------------------------------------------------------------------------------- /shared/src/docify/template-bundles/docusaurus/node.mdx.hbs: -------------------------------------------------------------------------------- 1 | --- 2 | id: {{id}} 3 | title: {{title}} 4 | --- 5 | 6 | ## Node Details 7 |
8 | | Field | Value | 9 | |---------------------|--------------------------| 10 | | **Unique ID** | {{id}} | 11 | | **Node Type** | {{nodeType}} | 12 | | **Name** | {{name}} | 13 | | **Description** | {{description}} | 14 | | **Data Classification** | {{dataClassification}} | 15 | | **Run As** | {{runAs}} | 16 |
17 | 18 | ## Interfaces 19 | {{#if interfaces.length}} 20 | | Unique ID | Host | Port | Url | 21 | |-----------|------|------|-----| 22 | {{#each interfaces}} 23 | | {{uniqueId}} | {{host}} | {{port}} | {{url}} | 24 | {{/each}} 25 | {{else}} 26 | _No interfaces defined._ 27 | {{/if}} 28 | 29 | 30 | ## Related Nodes 31 | 32 | {{> relationships.hbs}} 33 | 34 | {{> controls.hbs}} -------------------------------------------------------------------------------- /shared/src/docify/template-bundles/docusaurus/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "arch-docs", 3 | "version": "1.0.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "docusaurus start", 7 | "build": "docusaurus build", 8 | "serve": "docusaurus serve", 9 | "clear": "rm -rf node_modules/.cache build" 10 | }, 11 | "dependencies": { 12 | "@docusaurus/core": "^3.7.0", 13 | "@docusaurus/preset-classic": "^3.7.0", 14 | "@docusaurus/plugin-content-docs": "^3.7.0", 15 | "@docusaurus/theme-classic": "^3.7.0", 16 | "@docusaurus/theme-live-codeblock": "^3.7.0", 17 | "@docusaurus/theme-mermaid": "^3.7.0", 18 | "docusaurus-plugin-search-local": "^2.1.2", 19 | "react": "^18.0.0", 20 | "react-dom": "^18.0.0", 21 | "unist-util-visit": "^5.0.0" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /shared/src/docify/template-bundles/docusaurus/remark-replace-links.js: -------------------------------------------------------------------------------- 1 | import { visit } from 'unist-util-visit'; 2 | /* eslint-disable no-undef */ 3 | module.exports = function remarkReplaceLinks() { 4 | return (tree) => { 5 | visit(tree, 'link', (node) => { 6 | if (node.url && node.url.startsWith('https://calm.finos.org/traderx/control-requirements/')) { 7 | const lastSegment = node.url.split('/').pop(); 8 | node.url = `/control-requirements/${lastSegment}`; 9 | 10 | // Ensure node.properties exists before setting attributes 11 | node.data = node.data || {}; 12 | node.data.hProperties = node.data.hProperties || {}; 13 | node.data.hProperties.target = '_blank'; 14 | node.data.hProperties.rel = 'noopener noreferrer'; 15 | } 16 | }); 17 | }; 18 | }; 19 | -------------------------------------------------------------------------------- /shared/src/docify/template-bundles/docusaurus/row-template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | {{kebabToTitleCase @key}} 4 | 5 | 6 | {{#if (isObject this)}} 7 | {{> table-template.html data=this}} 8 | {{else if (isArray this)}} 9 |
    10 | {{#each this}} 11 |
  • {{this}}
  • 12 | {{/each}} 13 |
14 | {{else}} 15 | {{this}} 16 | {{/if}} 17 | 18 | 19 | -------------------------------------------------------------------------------- /shared/src/docify/template-bundles/docusaurus/sidebar.js.hbs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | docs: [ 3 | { 4 | type: 'doc', 5 | id: 'index', 6 | label: 'Home', 7 | }, 8 | {{#if (notEmpty nodes)}} 9 | { 10 | type: 'category', 11 | label: 'Nodes', 12 | items: [ 13 | {{#each nodes}} 14 | 'nodes/{{this.id}}'{{#unless @last}},{{/unless}} 15 | {{/each}} 16 | ], 17 | }, 18 | {{/if}} 19 | {{#if (notEmpty flows)}} 20 | { 21 | type: 'category', 22 | label: 'Flows', 23 | items: [ 24 | {{#each flows}} 25 | 'flows/{{this.id}}'{{#unless @last}},{{/unless}} 26 | {{/each}} 27 | ], 28 | } 29 | {{/if}} 30 | ] 31 | }; 32 | -------------------------------------------------------------------------------- /shared/src/docify/template-bundles/docusaurus/table-template.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | {{#each data}} 11 | {{> row-template.html}} 12 | {{/each}} 13 | 14 |
KeyValue
15 |
16 | -------------------------------------------------------------------------------- /shared/src/model/control-requirement.ts: -------------------------------------------------------------------------------- 1 | import {CalmControlRequirementSchema} from '../types/control-requirement-types.js'; 2 | 3 | export class CalmControlRequirement { 4 | constructor( 5 | public controlId: string, 6 | public name: string, 7 | public description: string 8 | ) {} 9 | 10 | static fromJson(data: CalmControlRequirementSchema): CalmControlRequirement { 11 | return new CalmControlRequirement( 12 | data['control-id'], 13 | data['name'], 14 | data['description'] 15 | ); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /shared/src/model/metadata.ts: -------------------------------------------------------------------------------- 1 | import { CalmMetadataSchema } from '../types/metadata-types.js'; 2 | 3 | export class CalmMetadata { 4 | constructor(public data: Record) {} 5 | 6 | static fromJson(data: CalmMetadataSchema): CalmMetadata { 7 | if(!data) return new CalmMetadata({}); 8 | 9 | const flattenedData = data.reduce((acc, curr) => { 10 | return { ...acc, ...curr }; 11 | }, {} as Record); 12 | 13 | return new CalmMetadata(flattenedData); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /shared/src/parser/parser.ts: -------------------------------------------------------------------------------- 1 | import { CalmCore } from '../model/core.js'; 2 | import fs from 'fs'; 3 | import { initLogger } from '../logger.js'; 4 | import {CalmCoreSchema} from '../types/core-types.js'; 5 | 6 | export class CalmParser { 7 | 8 | private static logger = initLogger(process.env.DEBUG === 'true', 'calm-parser'); 9 | 10 | parse(coreCalmFilePath: string): CalmCore { 11 | const logger = CalmParser.logger; 12 | try { 13 | const data = fs.readFileSync(coreCalmFilePath, 'utf8'); 14 | const dereferencedData: CalmCoreSchema = JSON.parse(data); // TODO: this needs to use SchemaDirectory to parse the other documents e.g. flows. 15 | dereferencedData.flows = []; // If this ends up being string documents then this will break CalmFlow.fromJson 16 | dereferencedData.controls = {}; // If this ends up being string documents then this will break CalmControl.fromJson 17 | return CalmCore.fromJson(dereferencedData); 18 | } catch (error) { 19 | logger.error('Failed to parse calm.json:'+ error); 20 | throw error; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /shared/src/spectral/functions/architecture/ids-are-unique.ts: -------------------------------------------------------------------------------- 1 | import { JSONPath } from 'jsonpath-plus'; 2 | import { detectDuplicates } from '../helper-functions'; 3 | 4 | /** 5 | * Checks that the input value exists as a node with a matching unique ID. 6 | */ 7 | export function idsAreUnique(input, _, context) { 8 | if (!input) { 9 | return []; 10 | } 11 | // get uniqueIds of all nodes 12 | const nodeIdMatches = JSONPath({path: '$.nodes[*].unique-id', json: context.document.data, resultType: 'all'}); 13 | const relationshipIdMatches = JSONPath({path: '$.relationships[*].unique-id', json: context.document.data, resultType: 'all'}); 14 | const interfaceIdMatches = JSONPath({path: '$.nodes[*].interfaces[*].unique-id', json: context.document.data, resultType: 'all'}); 15 | 16 | const seenIds = new Set(); 17 | 18 | const messages = []; 19 | 20 | detectDuplicates(nodeIdMatches, seenIds, messages); 21 | detectDuplicates(relationshipIdMatches, seenIds, messages); 22 | detectDuplicates(interfaceIdMatches, seenIds, messages); 23 | 24 | return messages; 25 | }; -------------------------------------------------------------------------------- /shared/src/spectral/functions/architecture/interface-id-exists.ts: -------------------------------------------------------------------------------- 1 | import { JSONPath } from 'jsonpath-plus'; 2 | 3 | /** 4 | * Checks that the input value exists as an interface with matching unique ID defined under a node in the document. 5 | */ 6 | export function interfaceIdExists(input, _, context) { 7 | if (!input) { 8 | return []; 9 | } 10 | 11 | // get uniqueIds of all interfaces 12 | const names = JSONPath({path: '$.nodes[*].interfaces[*].unique-id', json: context.document.data}); 13 | const results = []; 14 | 15 | if (!names.includes(input)) { 16 | results.push({ 17 | message: `'${input}' does not refer to the unique-id of an existing interface.`, 18 | path: [...context.path] 19 | }); 20 | } 21 | return results; 22 | } -------------------------------------------------------------------------------- /shared/src/spectral/functions/architecture/node-has-relationship.ts: -------------------------------------------------------------------------------- 1 | import { JSONPath } from 'jsonpath-plus'; 2 | 3 | /** 4 | * Checks that the given input, a unique ID, is referenced by at least one relationship. 5 | */ 6 | export function nodeHasRelationship(input, _, context) { 7 | const nodeName = input; 8 | 9 | const relationshipLabels = JSONPath({path: '$.relationships[*].relationship-type..*@string()', json: context.document.data}); 10 | const results = []; 11 | if (!relationshipLabels) { 12 | return [{ 13 | message: `Node with ID '${nodeName}' is not referenced by any relationships.`, 14 | path: [...context.path] 15 | }]; 16 | } 17 | if (!relationshipLabels.includes(nodeName)) { 18 | results.push({ 19 | message: `Node with ID '${nodeName}' is not referenced by any relationships.`, 20 | path: [...context.path] 21 | }); 22 | } 23 | return results; 24 | } -------------------------------------------------------------------------------- /shared/src/spectral/functions/architecture/node-id-exists.ts: -------------------------------------------------------------------------------- 1 | import { JSONPath } from 'jsonpath-plus'; 2 | 3 | /** 4 | * Checks that the input value exists as a node with a matching unique ID. 5 | */ 6 | export function nodeIdExists(input, _, context) { 7 | if (!input) { 8 | return []; 9 | } 10 | // get uniqueIds of all nodes 11 | const names = JSONPath({path: '$.nodes[*].unique-id', json: context.document.data}); 12 | const results = []; 13 | 14 | if (!names.includes(input)) { 15 | results.push({ 16 | message: `'${input}' does not refer to the unique-id of an existing node.`, 17 | path: [...context.path] 18 | }); 19 | } 20 | return results; 21 | } -------------------------------------------------------------------------------- /shared/src/spectral/functions/helper-functions.ts: -------------------------------------------------------------------------------- 1 | 2 | export function detectDuplicates(matches, seenIds, messages) { 3 | for (const match of matches) { 4 | const id = match['value']; 5 | 6 | if (seenIds.has(id)) { 7 | messages.push({ 8 | message: `Duplicate unique-id detected. ID: ${id}, path: ${match['pointer']}`, 9 | path: [match['pointer']] 10 | }); 11 | } 12 | else { 13 | seenIds.add(id); 14 | } 15 | } 16 | } 17 | 18 | export function numericalPlaceHolder(input, _, context) { 19 | if (input == -1) { 20 | return [{ 21 | message: 'Value was equal to -1 - placeholder property detected', 22 | path: [...context.path] 23 | }]; 24 | } 25 | } -------------------------------------------------------------------------------- /shared/src/spectral/functions/pattern/interface-id-exists.ts: -------------------------------------------------------------------------------- 1 | import { JSONPath } from 'jsonpath-plus'; 2 | 3 | /** 4 | * Checks that the input value exists as an interface with matching unique ID defined under a node in the document. 5 | */ 6 | export function interfaceIdExists(input, _, context) { 7 | if (!input) { 8 | return []; 9 | } 10 | // get uniqueIds of all interfaces 11 | const uniqueIds = JSONPath({path: '$..interfaces.prefixItems[*].properties.unique-id.const', json: context.document.data}); 12 | const results = []; 13 | 14 | if (!uniqueIds.includes(input)) { 15 | results.push({ 16 | message: `'${input}' does not refer to the unique-id of an existing interface.`, 17 | path: [...context.path] 18 | }); 19 | } 20 | return results; 21 | } -------------------------------------------------------------------------------- /shared/src/spectral/functions/pattern/is-defined-in-oneof-or-anyof.ts: -------------------------------------------------------------------------------- 1 | import { JSONPath } from 'jsonpath-plus'; 2 | /** 3 | * Checks that the input value should be defined in a oneOf or anyOf block. 4 | */ 5 | export function isDefinedInOneOfOrAnyOf(input, { calmType }: { calmType: 'nodes' | 'relationships'}, context) { 6 | if (!input || typeof input !== 'string') { 7 | return []; 8 | } 9 | 10 | const names = JSONPath({ path: `$.properties.${calmType}.prefixItems[*].properties.unique-id.const`, json: context.document.data }); 11 | const oneofs = JSONPath({ path: `$.properties.${calmType}.prefixItems[*].oneOf[*].properties.unique-id.const`, json: context.document.data }); 12 | const anyofs = JSONPath({ path: `$.properties.${calmType}.prefixItems[*].anyOf[*].properties.unique-id.const`, json: context.document.data }); 13 | 14 | const results = []; 15 | 16 | if (names.includes(input) && !oneofs.includes(input) && !anyofs.includes(input)) { 17 | results.push({ 18 | message: `'${input}' is part of a pattern option and must be defined in a oneOf or anyOf block.`, 19 | path: [...context.path], 20 | }); 21 | } 22 | return results; 23 | }; 24 | -------------------------------------------------------------------------------- /shared/src/spectral/functions/pattern/node-has-relationship.ts: -------------------------------------------------------------------------------- 1 | import { JSONPath } from 'jsonpath-plus'; 2 | 3 | /** 4 | * Checks that the given input, a unique ID, is referenced by at least one relationship. 5 | */ 6 | export default (input, _, context) => { 7 | const nodeId = input; 8 | 9 | const referencedNodeIds = JSONPath({path: '$..relationship-type..*@string()', json: context.document.data}); 10 | 11 | const results = []; 12 | if (!referencedNodeIds) { 13 | return [{ 14 | message: `Node with ID '${nodeId}' is not referenced by any relationships.`, 15 | path: [...context.path] 16 | }]; 17 | } 18 | if (!referencedNodeIds.includes(nodeId)) { 19 | results.push({ 20 | message: `Node with ID '${nodeId}' is not referenced by any relationships.`, 21 | path: [...context.path] 22 | }); 23 | } 24 | return results; 25 | }; -------------------------------------------------------------------------------- /shared/src/spectral/functions/pattern/node-id-exists.ts: -------------------------------------------------------------------------------- 1 | import { JSONPath } from 'jsonpath-plus'; 2 | /** 3 | * Checks that the input value exists as a node with a matching unique ID. 4 | */ 5 | export default (input, _, context) => { 6 | if (!input || typeof input !== 'string') { 7 | return []; 8 | } 9 | 10 | const names = JSONPath({ path: '$.properties.nodes.prefixItems[*].properties.unique-id.const', json: context.document.data }); 11 | const oneofs = JSONPath({ path: '$.properties.nodes.prefixItems[*].oneOf[*].properties.unique-id.const', json: context.document.data }); 12 | const anyofs = JSONPath({ path: '$.properties.nodes.prefixItems[*].anyOf[*].properties.unique-id.const', json: context.document.data }); 13 | 14 | // get uniqueIds of all nodes 15 | const results = []; 16 | 17 | if (!names.includes(input) && !oneofs.includes(input) && !anyofs.includes(input)) { 18 | results.push({ 19 | message: `'${input}' does not refer to the unique-id of an existing node.`, 20 | path: [...context.path], 21 | }); 22 | } 23 | return results; 24 | }; 25 | -------------------------------------------------------------------------------- /shared/src/template/types.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | export interface IndexFile { 3 | name: string; 4 | transformer?: string; 5 | templates: TemplateEntry[]; 6 | } 7 | 8 | export interface TemplateEntry { 9 | template: string; 10 | from: string; 11 | output: string; 12 | 'output-type': string; 13 | partials?: string[]; 14 | } 15 | 16 | export interface CalmTemplateTransformer { 17 | getTransformedModel(calmJson: string): any; 18 | registerTemplateHelpers(): Record any>; 19 | } 20 | -------------------------------------------------------------------------------- /shared/src/test/mocks/server.ts: -------------------------------------------------------------------------------- 1 | import { setupServer } from 'msw/node'; 2 | import { createHandlers, HandlerConfig } from './handlers'; 3 | 4 | export const server = setupServer(); 5 | 6 | // Function to reset handlers for each test 7 | export function resetHandlers(mocks: HandlerConfig[]) { 8 | server.resetHandlers(...createHandlers(mocks)); 9 | } 10 | -------------------------------------------------------------------------------- /shared/src/test/setup-msw.ts: -------------------------------------------------------------------------------- 1 | import { server } from './mocks/server'; 2 | 3 | // Start the server before all tests 4 | beforeAll(() => server.listen({ onUnhandledRequest: 'error' })); 5 | 6 | // Reset handlers after each test 7 | afterEach(() => server.resetHandlers()); 8 | 9 | // Close the server after all tests 10 | afterAll(() => server.close()); 11 | -------------------------------------------------------------------------------- /shared/src/types/control-requirement-types.ts: -------------------------------------------------------------------------------- 1 | export type CalmControlRequirementSchema = { 2 | 'control-id': string; 3 | name: string; 4 | description: string; 5 | }; 6 | -------------------------------------------------------------------------------- /shared/src/types/control-types.ts: -------------------------------------------------------------------------------- 1 | export type CalmControlDetailSchema = { 2 | 'control-requirement-url': string; 3 | 'control-config-url': string; 4 | } 5 | | { 6 | 'control-requirement-url': string; 7 | 'control-config': Record; 8 | }; 9 | 10 | export type CalmControlSchema = { 11 | description: string; 12 | requirements: CalmControlDetailSchema[]; 13 | }; 14 | 15 | export type CalmControlsSchema = { 16 | [controlId: string]: CalmControlSchema; 17 | }; 18 | -------------------------------------------------------------------------------- /shared/src/types/flow-types.ts: -------------------------------------------------------------------------------- 1 | import {CalmControlsSchema} from './control-types.js'; 2 | import {CalmMetadataSchema} from './metadata-types.js'; 3 | 4 | export type CalmFlowTransitionDirectionSchema = 'source-to-destination' | 'destination-to-source'; 5 | 6 | export type CalmFlowTransitionSchema = { 7 | 'relationship-unique-id': string; 8 | 'sequence-number': number; 9 | summary: string; 10 | direction?: CalmFlowTransitionDirectionSchema; 11 | }; 12 | 13 | export type CalmFlowSchema = { 14 | 'unique-id': string; 15 | name: string; 16 | description: string; 17 | 'requirement-url'?: string; 18 | transitions: CalmFlowTransitionSchema[]; 19 | controls?: CalmControlsSchema; 20 | metadata?: CalmMetadataSchema; 21 | }; 22 | -------------------------------------------------------------------------------- /shared/src/types/metadata-types.ts: -------------------------------------------------------------------------------- 1 | export type CalmMetadataSchema = Record[]; 2 | -------------------------------------------------------------------------------- /shared/src/types/units-types.ts: -------------------------------------------------------------------------------- 1 | export type CalmTimeUnitSchema = { 2 | unit: 'nanoseconds' | 'milliseconds' | 'seconds' | 'minutes' | 'hours' | 'days' | 'weeks' | 'months' | 'years'; 3 | value: number; 4 | }; 5 | 6 | export type CalmCronExpressionSchema = string; 7 | -------------------------------------------------------------------------------- /shared/test_fixtures/calm.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2020-12/schema", 3 | "$id": "https://raw.githubusercontent.com/finos/architecture-as-code/main/calm/draft/2024-03/meta/calm.json", 4 | 5 | "$vocabulary": { 6 | "https://json-schema.org/draft/2020-12/vocab/core": true, 7 | "https://json-schema.org/draft/2020-12/vocab/applicator": true, 8 | "https://json-schema.org/draft/2020-12/vocab/validation": true, 9 | "https://json-schema.org/draft/2020-12/vocab/meta-data": true, 10 | "https://json-schema.org/draft/2020-12/vocab/format-annotation": true, 11 | "https://json-schema.org/draft/2020-12/vocab/content": true, 12 | "https://raw.githubusercontent.com/finos/architecture-as-code/main/calm/draft/2024-03/meta/core.json": true 13 | }, 14 | "$dynamicAnchor": "meta", 15 | 16 | "title": "Common Architecture Language Model (CALM) Schema", 17 | "allOf": [ 18 | {"$ref": "https://json-schema.org/draft/2020-12/schema"}, 19 | {"$ref": "https://raw.githubusercontent.com/finos/architecture-as-code/main/calm/draft/2024-03/meta/core.json"} 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /shared/test_fixtures/calm/calm.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2020-12/schema", 3 | "$id": "https://raw.githubusercontent.com/finos/architecture-as-code/main/calm/draft/2024-03/meta/calm.json", 4 | 5 | "$vocabulary": { 6 | "https://json-schema.org/draft/2020-12/vocab/core": true, 7 | "https://json-schema.org/draft/2020-12/vocab/applicator": true, 8 | "https://json-schema.org/draft/2020-12/vocab/validation": true, 9 | "https://json-schema.org/draft/2020-12/vocab/meta-data": true, 10 | "https://json-schema.org/draft/2020-12/vocab/format-annotation": true, 11 | "https://json-schema.org/draft/2020-12/vocab/content": true, 12 | "https://raw.githubusercontent.com/finos/architecture-as-code/main/calm/draft/2024-03/meta/core.json": true 13 | }, 14 | "$dynamicAnchor": "meta", 15 | 16 | "title": "Common Architecture Language Model (CALM) Schema", 17 | "allOf": [ 18 | {"$ref": "https://json-schema.org/draft/2020-12/schema"}, 19 | {"$ref": "https://raw.githubusercontent.com/finos/architecture-as-code/main/calm/draft/2024-03/meta/core.json"} 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /shared/test_fixtures/calm/file-to-ignore.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/finos/architecture-as-code/a54518a8317eb889ea2541210c0297e30e9d880f/shared/test_fixtures/calm/file-to-ignore.txt -------------------------------------------------------------------------------- /shared/test_fixtures/markdown.md: -------------------------------------------------------------------------------- 1 | # This is a markdown file 2 | -------------------------------------------------------------------------------- /shared/test_fixtures/schema-directory/missing-inner-ref.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft-2020-12/schema", 3 | "$id": "https://calm.com/missing-inner-ref.json", 4 | "title": "Relative references schema", 5 | "type": "object", 6 | "defs": { 7 | "top-level": { 8 | "$ref": "https://missing-schema#/defs/not-found", 9 | "properties": { 10 | "top-level": { 11 | "const": "test" 12 | } 13 | } 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /shared/test_fixtures/schema-directory/recursive.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/finos/architecture-as-code/main/calm/draft/2024-03/meta/calm.json", 3 | "$id": "https://calm.com/recursive.json", 4 | "title": "API Gateway Pattern", 5 | "type": "object", 6 | "defs": { 7 | "circular": { 8 | "$ref": "https://calm.com/recursive.json#/defs/circular", 9 | "properties": { 10 | "prop": { 11 | "const": "test" 12 | } 13 | } 14 | }, 15 | "top-level": { 16 | "$ref": "https://calm.com/recursive.json#/defs/circular", 17 | "properties": { 18 | "top-level": { 19 | "const": "test" 20 | } 21 | } 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /shared/test_fixtures/schema-directory/references.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft-2020-12/schema", 3 | "$id": "https://calm.com/references.json", 4 | "title": "Relative references schema", 5 | "type": "object", 6 | "defs": { 7 | "inner": { 8 | "type": "object", 9 | "properties": { 10 | "inner-prop": { 11 | "const": "test" 12 | } 13 | } 14 | }, 15 | "top-level": { 16 | "$ref": "https://calm.com/references.json#/defs/inner", 17 | "properties": { 18 | "top-level": { 19 | "const": "test" 20 | } 21 | } 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /shared/test_fixtures/schema-directory/relative-ref.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft-2020-12/schema", 3 | "$id": "https://calm.com/relative.json", 4 | "title": "Relative references schema", 5 | "type": "object", 6 | "defs": { 7 | "inner": { 8 | "type": "object", 9 | "properties": { 10 | "inner-prop": { 11 | "const": "test" 12 | } 13 | } 14 | }, 15 | "top-level": { 16 | "$ref": "#/defs/inner", 17 | "properties": { 18 | "top-level": { 19 | "const": "test" 20 | } 21 | } 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /shared/test_fixtures/template/bundles/bad-transformer/bad-transformer.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | import {CalmTemplateTransformer} from '@finos/calm-shared'; 3 | 4 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 5 | class BadTransformer implements CalmTemplateTransformer { 6 | 7 | registerTemplateHelpers(): Record any> { 8 | return { 9 | uppercase: (text: string) => text.toUpperCase(), 10 | lowercase: (text: string) => text.toLowerCase(), 11 | }; 12 | } 13 | 14 | getTransformedModel(inputJson: string): any { 15 | return JSON.parse(inputJson); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /shared/test_fixtures/template/bundles/bad-transformer/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Simple Test Bundle", 3 | "transformer": "bad-transformer", 4 | "templates": [ 5 | { "template": "main.hbs", "from": "document", "output": "{{id}}.txt", "output-type": "repeated" } 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /shared/test_fixtures/template/bundles/bad-transformer/main.hbs: -------------------------------------------------------------------------------- 1 | User: {{name}} 2 | -------------------------------------------------------------------------------- /shared/test_fixtures/template/bundles/default-transformer/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Simple Test Bundle", 3 | "templates": [ 4 | { "template": "main.hbs", "from": "document", "output": "doc-system-one-pager.md", "output-type": "single" } 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /shared/test_fixtures/template/bundles/dereferencing-transformer/deref-transformer.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | import { CalmTemplateTransformer } from '@finos/calm-shared'; 3 | 4 | export default class DerefTransformer implements CalmTemplateTransformer { 5 | registerTemplateHelpers() { 6 | return {}; 7 | } 8 | getTransformedModel(doc: string): any { 9 | const parsed = JSON.parse(doc); 10 | return { document: parsed }; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /shared/test_fixtures/template/bundles/dereferencing-transformer/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Bundle with Dereferencing", 3 | "transformer": "deref-transformer", 4 | "templates": [ 5 | { 6 | "template": "main.hbs", 7 | "from": "document", 8 | "output": "deref-output.html", 9 | "output-type": "single" 10 | } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /shared/test_fixtures/template/bundles/dereferencing-transformer/main.hbs: -------------------------------------------------------------------------------- 1 |

{{title}}

2 |

{{description}}

3 | 4 |

Nodes

5 |
    6 | {{#each nodes}} 7 |
  • {{name}} ({{node-type}}): {{description}}
  • 8 | {{/each}} 9 |
10 | 11 |

Relationships

12 |
    13 | {{#each relationships}} 14 |
  • {{description}}
  • 15 | {{/each}} 16 |
17 | 18 |

Flows

19 | {{#each flows}} 20 | {{#if unique-id}} 21 |
22 |

{{name}} ({{unique-id}})

23 |

{{description}}

24 |

Transitions

25 |
    26 | {{#each transitions}} 27 |
  • Sequence {{sequence-number}}: {{summary}} (Relationship: {{relationship-unique-id}})
  • 28 | {{/each}} 29 |
30 |
31 | {{else}} 32 |
{{this}}
33 | {{/if}} 34 | {{/each}} 35 | -------------------------------------------------------------------------------- /shared/test_fixtures/template/bundles/failing-transformer/failing-transformer.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | import {CalmTemplateTransformer} from '@finos/calm-shared'; 3 | 4 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 5 | class FailingTransformer implements CalmTemplateTransformer { 6 | 7 | registerTemplateHelpers(): Record any> { 8 | return { 9 | uppercase: (text: string) => text.toUpperCase(), 10 | lowercase: (text: string) => text.toLowerCase(), 11 | }; 12 | } 13 | 14 | getTransformedModel(inputJson: string): any { 15 | console.log(inputJson); 16 | throw new Error('Fail!'); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /shared/test_fixtures/template/bundles/failing-transformer/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Simple Test Bundle", 3 | "transformer": "failing-transformer", 4 | "templates": [ 5 | { "template": "main.hbs", "from": "document", "output": "{{id}}.txt", "output-type": "repeated" } 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /shared/test_fixtures/template/bundles/failing-transformer/main.hbs: -------------------------------------------------------------------------------- 1 | User: {{name}} 2 | -------------------------------------------------------------------------------- /shared/test_fixtures/template/bundles/infrastructure-transformer/application-deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: {{ appName }} 5 | spec: 6 | replicas: 1 7 | selector: 8 | matchLabels: 9 | app: {{ appName }} 10 | template: 11 | metadata: 12 | labels: 13 | app: {{ appName }} 14 | spec: 15 | containers: 16 | - name: app 17 | image: {{ applicationImage }} 18 | ports: 19 | - containerPort: {{ applicationPort }} -------------------------------------------------------------------------------- /shared/test_fixtures/template/bundles/infrastructure-transformer/application-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: {{ appName }}-service 5 | spec: 6 | selector: 7 | app: {{ appName }} 8 | type: LoadBalancer 9 | ports: 10 | - protocol: TCP 11 | port: {{ lbPort }} 12 | targetPort: {{ applicationPort }} -------------------------------------------------------------------------------- /shared/test_fixtures/template/bundles/infrastructure-transformer/calico-global-deny.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: crd.projectcalico.org/v1 2 | kind: GlobalNetworkPolicy 3 | metadata: 4 | name: deny-app-policy 5 | spec: 6 | namespaceSelector: has(projectcalico.org/name) && projectcalico.org/name not in {"kube-system"} 7 | types: 8 | - Ingress 9 | - Egress 10 | egress: 11 | # allow all namespaces to communicate to DNS pods 12 | - action: Allow 13 | protocol: UDP 14 | destination: 15 | selector: 'k8s-app == "kube-dns"' 16 | ports: 17 | - 53 18 | - action: Allow 19 | protocol: TCP 20 | destination: 21 | selector: 'k8s-app == "kube-dns"' 22 | ports: 23 | - 53 -------------------------------------------------------------------------------- /shared/test_fixtures/template/bundles/infrastructure-transformer/cluster_start.hbs: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euo pipefail 4 | 5 | SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 6 | 7 | KUBERNETES_VERSION=1.30.0 8 | 9 | echo "Starting Minikube..." 10 | minikube start --network-plugin=cni --cni=calico --kubernetes-version=$KUBERNETES_VERSION 11 | 12 | {{#if secure}} 13 | echo "Enabling global Calico default-deny policy..." 14 | kubectl apply --filename "${SCRIPT_DIR}/calico-global-deny.yaml" --namespace kube-system 15 | {{/if}} -------------------------------------------------------------------------------- /shared/test_fixtures/template/bundles/infrastructure-transformer/database-deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: {{ databaseName }} 5 | spec: 6 | replicas: 1 7 | selector: 8 | matchLabels: 9 | db: {{ databaseName }} 10 | template: 11 | metadata: 12 | labels: 13 | db: {{ databaseName }} 14 | spec: 15 | containers: 16 | - name: db 17 | image: postgres 18 | ports: 19 | - containerPort: {{ databasePort }} 20 | env: 21 | - name: POSTGRES_DB 22 | value: conference 23 | - name: POSTGRES_USER 24 | value: calm 25 | - name: POSTGRES_PASSWORD 26 | value: demo 27 | resources: 28 | requests: 29 | memory: "128Mi" # Equivalent to shm_size: 128mb in Docker Compose -------------------------------------------------------------------------------- /shared/test_fixtures/template/bundles/infrastructure-transformer/database-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: db 5 | spec: 6 | selector: 7 | db: {{ databaseName }} 8 | ports: 9 | - protocol: TCP 10 | port: 5432 11 | targetPort: 5432 12 | type: ClusterIP -------------------------------------------------------------------------------- /shared/test_fixtures/template/bundles/infrastructure-transformer/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | namespace: {{ namespaceName }} 5 | 6 | resources: 7 | - namespace.yaml 8 | - application-deployment.yaml 9 | - application-service.yaml 10 | - database-deployment.yaml 11 | - database-service.yaml 12 | - permit-app-from-db.yaml 13 | - permit-app-to-db.yaml 14 | - permit-lb-to-app.yaml -------------------------------------------------------------------------------- /shared/test_fixtures/template/bundles/infrastructure-transformer/namespace.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: {{ namespaceName }} -------------------------------------------------------------------------------- /shared/test_fixtures/template/bundles/infrastructure-transformer/permit-app-from-db.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.k8s.io/v1 2 | kind: NetworkPolicy 3 | metadata: 4 | name: allow-ingress-to-db-from-app 5 | spec: 6 | podSelector: 7 | matchLabels: 8 | db: {{ databaseName }} 9 | ingress: 10 | - from: 11 | - podSelector: 12 | matchLabels: 13 | app: {{ appName }} 14 | policyTypes: 15 | - Ingress -------------------------------------------------------------------------------- /shared/test_fixtures/template/bundles/infrastructure-transformer/permit-app-to-db.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.k8s.io/v1 2 | kind: NetworkPolicy 3 | metadata: 4 | name: allow-egress-from-app-to-db 5 | spec: 6 | podSelector: 7 | matchLabels: 8 | app: {{ appName }} 9 | egress: 10 | - to: 11 | - podSelector: 12 | matchLabels: 13 | db: {{ databaseName }} 14 | policyTypes: 15 | - Egress -------------------------------------------------------------------------------- /shared/test_fixtures/template/bundles/infrastructure-transformer/permit-lb-to-app.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.k8s.io/v1 2 | kind: NetworkPolicy 3 | metadata: 4 | name: allow-external-ingress-to-app 5 | spec: 6 | podSelector: 7 | matchLabels: 8 | app: {{ appName }} 9 | ingress: 10 | - {} 11 | policyTypes: 12 | - Ingress -------------------------------------------------------------------------------- /shared/test_fixtures/template/bundles/js-transformer/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "JS Transformer Bundle", 3 | "transformer": "transformer", 4 | "templates": [ 5 | { 6 | "template": "main.hbs", 7 | "from": "document", 8 | "output": "js-transformer.txt", 9 | "output-type": "single" 10 | } 11 | ] 12 | } 13 | 14 | 15 | -------------------------------------------------------------------------------- /shared/test_fixtures/template/bundles/js-transformer/main.hbs: -------------------------------------------------------------------------------- 1 | ## {{name}} 2 | {{description}} 3 | 4 | ## Nodes 5 | {{#each nodes}} 6 | ### {{uppercase node-type}}: {{name}} 7 | - **ID:** {{unique-id}} 8 | - **Description:** {{description}} 9 | {{/each}} 10 | -------------------------------------------------------------------------------- /shared/test_fixtures/template/bundles/js-transformer/transformer.js: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line no-undef 2 | module.exports = class JsTransformer { 3 | registerTemplateHelpers() { 4 | return { 5 | uppercase: (text) => text.toUpperCase() 6 | }; 7 | } 8 | 9 | getTransformedModel(rawJson) { 10 | const document = JSON.parse(rawJson); 11 | return { 12 | document: { 13 | id: document.id, 14 | name: document.title, 15 | description: document.description, 16 | nodes: document.nodes || [], 17 | relationships: document.relationships || [] 18 | } 19 | }; 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /shared/test_fixtures/template/bundles/missing-transformer/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Simple Test Bundle", 3 | "transformer": "no-transformer", 4 | "templates": [ 5 | { "template": "main.hbs", "from": "document", "output": "{{id}}.txt", "output-type": "repeated" } 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /shared/test_fixtures/template/bundles/missing-transformer/main.hbs: -------------------------------------------------------------------------------- 1 | User: {{name}} 2 | -------------------------------------------------------------------------------- /shared/test_fixtures/template/bundles/repeated/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Simple Test Bundle", 3 | "transformer": "repeated-transformer", 4 | "templates": [ 5 | { "template": "main.hbs", "from": "nodes", "output": "{{id}}.txt", "output-type": "repeated" } 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /shared/test_fixtures/template/bundles/repeated/main.hbs: -------------------------------------------------------------------------------- 1 | Node: {{name}} -------------------------------------------------------------------------------- /shared/test_fixtures/template/bundles/repeated/repeated-transformer.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | import { CalmTemplateTransformer } from '@finos/calm-shared'; 3 | 4 | export default class RepeatedTransformer implements CalmTemplateTransformer { 5 | 6 | registerTemplateHelpers(): Record any> { 7 | return { 8 | uppercase: (text: string) => text.toUpperCase(), 9 | lowercase: (text: string) => text.toLowerCase(), 10 | }; 11 | } 12 | getTransformedModel(inputJson: string): any { 13 | const parsedData = JSON.parse(inputJson); 14 | 15 | if (parsedData.nodes && Array.isArray(parsedData.nodes)) { 16 | parsedData.nodes = parsedData.nodes.map(node => ({ 17 | ...node, 18 | id: node['unique-id'] || node.id // Ensure id is set to unique-id if available 19 | })); 20 | } 21 | 22 | return parsedData; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /shared/test_fixtures/template/bundles/single-file/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Simple Test Bundle", 3 | "transformer": "simple-transformer", 4 | "templates": [ 5 | { "template": "main.hbs", "from": "document", "output": "users.txt", "output-type": "single" } 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /shared/test_fixtures/template/bundles/single-file/main.hbs: -------------------------------------------------------------------------------- 1 | {{#each nodes}} 2 | Node: {{name}} 3 | {{/each}} -------------------------------------------------------------------------------- /shared/test_fixtures/template/bundles/single-file/simple-transformer.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | import {CalmTemplateTransformer} from '@finos/calm-shared'; 3 | 4 | export default class SimpleTransformer implements CalmTemplateTransformer { 5 | 6 | registerTemplateHelpers(): Record any> { 7 | return { 8 | uppercase: (text: string) => text.toUpperCase(), 9 | lowercase: (text: string) => text.toLowerCase(), 10 | }; 11 | } 12 | 13 | getTransformedModel(inputJson: string): any { 14 | return { 15 | document: JSON.parse(inputJson) 16 | }; 17 | 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /shared/test_fixtures/template/bundles/with-partials/footer.hbs: -------------------------------------------------------------------------------- 1 | =========================================== 2 | This document is auto-generated based on architecture standards. 3 | ===== END OF REPORT ===== 4 | -------------------------------------------------------------------------------- /shared/test_fixtures/template/bundles/with-partials/header.hbs: -------------------------------------------------------------------------------- 1 | ===== ARCHITECTURE REVIEW ===== 2 | System generated report based on CALM Model 3 | =========================================== 4 | -------------------------------------------------------------------------------- /shared/test_fixtures/template/bundles/with-partials/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "CALM-Based Partial Template Bundle", 3 | "transformer": "with-partials-transformer", 4 | "templates": [ 5 | { 6 | "template": "main.hbs", 7 | "from": "document", 8 | "output": "actual-with-partials.txt", 9 | "output-type": "single", 10 | "partials": ["header.hbs", "footer.hbs"] 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /shared/test_fixtures/template/bundles/with-partials/main.hbs: -------------------------------------------------------------------------------- 1 | {{> header.hbs }} 2 | 3 | ## Nodes 4 | {{#each nodes}} 5 | ### {{uppercase node-type}}: {{name}} 6 | - **ID:** {{unique-id}} 7 | - **Description:** {{description}} 8 | {{/each}} 9 | 10 | ## Relationships 11 | {{#each relationships}} 12 | {{#if relationship-type.connects}} 13 | ### {{relationship-type.connects.source.node}} → {{relationship-type.connects.destination.node}} 14 | - **Description:** {{description}} 15 | {{/if}} 16 | {{/each}} 17 | 18 | {{> footer.hbs }} 19 | -------------------------------------------------------------------------------- /shared/test_fixtures/template/data/controls/architect.configuration.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://calm.finos.org/controls/owner-responsibility.requirement.json", 3 | "$id": "https://calm.finos.org/controls/architect.configuration.json", 4 | "control-id": "ownership-003", 5 | "name": "Architect Responsibility", 6 | "description": "Captures who is responsible for architecture", 7 | "owner-type": "System Owner", 8 | "owner": { 9 | "name": "Mr Architect", 10 | "email": "mr.architect@finos.org" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /shared/test_fixtures/template/data/controls/business-owner.configuration.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://calm.finos.org/controls/owner-responsibility.requirement.json", 3 | "$id": "https://calm.finos.org/controls/business-owner.configuration.json", 4 | "control-id": "ownership-001", 5 | "name": "Business Owner Responsibility", 6 | "description": "Captures who is responsible from business perspective", 7 | "owner-type": "Business Owner", 8 | "owner": { 9 | "name": "Jo Bloggs", 10 | "email": "jo.bloggs@finos.org" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /shared/test_fixtures/template/data/controls/owner-responsibility.requirement.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2020-12/schema", 3 | "$id": "https://calm.finos.org/controls/owner-responsibility.requirement.json", 4 | "title": "Owner Responsibility", 5 | "type": "object", 6 | "allOf": [ 7 | { 8 | "$ref": "https://calm.finos.org/release/1.0-rc1/meta/control-requirement.json" 9 | } 10 | ], 11 | "properties": { 12 | "description": { 13 | "const": "Captures who is responsible" 14 | }, 15 | "owner-type": { 16 | "type": "string", 17 | "description": "Type of responsibility held by the owner" 18 | }, 19 | "owner": { 20 | "type": "object", 21 | "properties": { 22 | "name": { 23 | "type": "string", 24 | "description": "Full name of the owner" 25 | }, 26 | "email": { 27 | "type": "string", 28 | "format": "email", 29 | "description": "Email contact for the owner" 30 | } 31 | }, 32 | "required": ["name", "email"] 33 | } 34 | }, 35 | "required": ["owner-type", "owner"] 36 | } 37 | -------------------------------------------------------------------------------- /shared/test_fixtures/template/data/controls/system-owner.configuration.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://calm.finos.org/controls/owner-responsibility.requirement.json", 3 | "$id": "https://calm.finos.org/controls/system-owner.configuration.json", 4 | "control-id": "ownership-002", 5 | "name": "System Owner Responsibility", 6 | "description": "Captures who is responsible from system ownership", 7 | "owner-type": "System Owner", 8 | "owner": { 9 | "name": "Jane Doe", 10 | "email": "jane.doe@finos.org" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /shared/test_fixtures/template/data/flow-document-upload.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://calm.finos.org/draft/2024-12/meta/flow.json", 3 | "$id": "https://calm.finos.org/docuflow/flow/document-upload", 4 | "unique-id": "flow-document-upload", 5 | "name": "Document Upload Flow", 6 | "description": "Flow for uploading a document and storing metadata securely in the DocuFlow system.", 7 | "transitions": [ 8 | { 9 | "relationship-unique-id": "rel-upload-to-storage", 10 | "sequence-number": 1, 11 | "summary": "Upload Service sends document to Storage Service" 12 | }, 13 | { 14 | "relationship-unique-id": "rel-storage-to-db", 15 | "sequence-number": 2, 16 | "summary": "Storage Service stores document metadata in Document Database" 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /shared/test_fixtures/template/data/simple-nodes.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Simple Two Node Example", 3 | "description": "A minimal CALM document with two nodes and one relationship.", 4 | "nodes": [ 5 | { 6 | "unique-id": "service-a", 7 | "name": "Service A", 8 | "description": "An API service that sends requests to Service B.", 9 | "node-type": "service" 10 | }, 11 | { 12 | "unique-id": "service-b", 13 | "name": "Service B", 14 | "description": "A backend service that processes requests from Service A.", 15 | "node-type": "service" 16 | } 17 | ] 18 | } -------------------------------------------------------------------------------- /shared/test_fixtures/template/expected-output/js-transformer.txt: -------------------------------------------------------------------------------- 1 | ## DocuFlow System 2 | DocuFlow is a document management system that allows users to upload, process, and store documents securely. 3 | 4 | ## Nodes 5 | ### SYSTEM: DocuFlow 6 | - **ID:** document-system 7 | - **Description:** Main document management system 8 | ### SERVICE: Upload Service 9 | - **ID:** svc-upload 10 | - **Description:** Handles user document uploads 11 | ### SERVICE: Storage Service 12 | - **ID:** svc-storage 13 | - **Description:** Stores and retrieves documents securely 14 | ### DATABASE: Document Database 15 | - **ID:** db-docs 16 | - **Description:** Stores metadata and document references 17 | -------------------------------------------------------------------------------- /shared/test_fixtures/template/expected-output/with-partials.txt: -------------------------------------------------------------------------------- 1 | ===== ARCHITECTURE REVIEW ===== 2 | System generated report based on CALM Model 3 | =========================================== 4 | 5 | ## Nodes 6 | ### SYSTEM: DocuFlow 7 | - **ID:** document-system 8 | - **Description:** Main document management system 9 | ### SERVICE: Upload Service 10 | - **ID:** svc-upload 11 | - **Description:** Handles user document uploads 12 | ### SERVICE: Storage Service 13 | - **ID:** svc-storage 14 | - **Description:** Stores and retrieves documents securely 15 | ### DATABASE: Document Database 16 | - **ID:** db-docs 17 | - **Description:** Stores metadata and document references 18 | 19 | ## Relationships 20 | ### svc-upload → svc-storage 21 | - **Description:** Upload Service sends documents to Storage Service for long-term storage 22 | ### svc-storage → db-docs 23 | - **Description:** Storage Service stores document metadata in the Document Database 24 | 25 | =========================================== 26 | This document is auto-generated based on architecture standards. 27 | ===== END OF REPORT ===== 28 | -------------------------------------------------------------------------------- /shared/test_fixtures/template/expected-output/workshop/infrastructure/cluster/calico-global-deny.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: crd.projectcalico.org/v1 2 | kind: GlobalNetworkPolicy 3 | metadata: 4 | name: deny-app-policy 5 | spec: 6 | namespaceSelector: has(projectcalico.org/name) && projectcalico.org/name not in {"kube-system"} 7 | types: 8 | - Ingress 9 | - Egress 10 | egress: 11 | # allow all namespaces to communicate to DNS pods 12 | - action: Allow 13 | protocol: UDP 14 | destination: 15 | selector: 'k8s-app == "kube-dns"' 16 | ports: 17 | - 53 18 | - action: Allow 19 | protocol: TCP 20 | destination: 21 | selector: 'k8s-app == "kube-dns"' 22 | ports: 23 | - 53 -------------------------------------------------------------------------------- /shared/test_fixtures/template/expected-output/workshop/infrastructure/cluster/cluster_start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euo pipefail 4 | 5 | SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 6 | 7 | KUBERNETES_VERSION=1.30.0 8 | 9 | echo "Starting Minikube..." 10 | minikube start --network-plugin=cni --cni=calico --kubernetes-version=$KUBERNETES_VERSION 11 | 12 | echo "Enabling global Calico default-deny policy..." 13 | kubectl apply --filename "${SCRIPT_DIR}/calico-global-deny.yaml" --namespace kube-system 14 | -------------------------------------------------------------------------------- /shared/test_fixtures/template/expected-output/workshop/infrastructure/kubernetes/application-deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: attendees 5 | spec: 6 | replicas: 1 7 | selector: 8 | matchLabels: 9 | app: attendees 10 | template: 11 | metadata: 12 | labels: 13 | app: attendees 14 | spec: 15 | containers: 16 | - name: app 17 | image: masteringapi/attendees-quarkus:ws-native-db 18 | ports: 19 | - containerPort: 8080 -------------------------------------------------------------------------------- /shared/test_fixtures/template/expected-output/workshop/infrastructure/kubernetes/application-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: attendees-service 5 | spec: 6 | selector: 7 | app: attendees 8 | type: LoadBalancer 9 | ports: 10 | - protocol: TCP 11 | port: 80 12 | targetPort: 8080 -------------------------------------------------------------------------------- /shared/test_fixtures/template/expected-output/workshop/infrastructure/kubernetes/database-deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: attendees-store 5 | spec: 6 | replicas: 1 7 | selector: 8 | matchLabels: 9 | db: attendees-store 10 | template: 11 | metadata: 12 | labels: 13 | db: attendees-store 14 | spec: 15 | containers: 16 | - name: db 17 | image: postgres 18 | ports: 19 | - containerPort: 5432 20 | env: 21 | - name: POSTGRES_DB 22 | value: conference 23 | - name: POSTGRES_USER 24 | value: calm 25 | - name: POSTGRES_PASSWORD 26 | value: demo 27 | resources: 28 | requests: 29 | memory: "128Mi" # Equivalent to shm_size: 128mb in Docker Compose -------------------------------------------------------------------------------- /shared/test_fixtures/template/expected-output/workshop/infrastructure/kubernetes/database-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: db 5 | spec: 6 | selector: 7 | db: attendees-store 8 | ports: 9 | - protocol: TCP 10 | port: 5432 11 | targetPort: 5432 12 | type: ClusterIP -------------------------------------------------------------------------------- /shared/test_fixtures/template/expected-output/workshop/infrastructure/kubernetes/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | namespace: conference 5 | 6 | resources: 7 | - namespace.yaml 8 | - application-deployment.yaml 9 | - application-service.yaml 10 | - database-deployment.yaml 11 | - database-service.yaml 12 | - permit-app-from-db.yaml 13 | - permit-app-to-db.yaml 14 | - permit-lb-to-app.yaml -------------------------------------------------------------------------------- /shared/test_fixtures/template/expected-output/workshop/infrastructure/kubernetes/namespace.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: conference -------------------------------------------------------------------------------- /shared/test_fixtures/template/expected-output/workshop/infrastructure/kubernetes/permit-app-from-db.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.k8s.io/v1 2 | kind: NetworkPolicy 3 | metadata: 4 | name: allow-ingress-to-db-from-app 5 | spec: 6 | podSelector: 7 | matchLabels: 8 | db: attendees-store 9 | ingress: 10 | - from: 11 | - podSelector: 12 | matchLabels: 13 | app: attendees 14 | policyTypes: 15 | - Ingress -------------------------------------------------------------------------------- /shared/test_fixtures/template/expected-output/workshop/infrastructure/kubernetes/permit-app-to-db.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.k8s.io/v1 2 | kind: NetworkPolicy 3 | metadata: 4 | name: allow-egress-from-app-to-db 5 | spec: 6 | podSelector: 7 | matchLabels: 8 | app: attendees 9 | egress: 10 | - to: 11 | - podSelector: 12 | matchLabels: 13 | db: attendees-store 14 | policyTypes: 15 | - Egress -------------------------------------------------------------------------------- /shared/test_fixtures/template/expected-output/workshop/infrastructure/kubernetes/permit-lb-to-app.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.k8s.io/v1 2 | kind: NetworkPolicy 3 | metadata: 4 | name: allow-external-ingress-to-app 5 | spec: 6 | podSelector: 7 | matchLabels: 8 | app: attendees 9 | ingress: 10 | - {} 11 | policyTypes: 12 | - Ingress -------------------------------------------------------------------------------- /shared/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "exclude": ["**/*.spec.ts", "src/docify/**/*"] 4 | } 5 | -------------------------------------------------------------------------------- /shared/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.base.json", 3 | "module": "Preserve", 4 | "moduleResolution": "bundler", 5 | "compilerOptions": { 6 | "outDir": "dist" 7 | }, 8 | "include": ["src", "../vitest-globals.d.ts"], 9 | "lib": ["esnext"] 10 | } 11 | -------------------------------------------------------------------------------- /shared/tsup.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'tsup'; 2 | 3 | export default defineConfig({ 4 | entry: [ 5 | 'src/docify/template-bundles/docusaurus/docusaurus-transformer.ts' 6 | ], 7 | format: ['esm'], 8 | clean: true, 9 | outDir: 'dist/template-bundles/docusaurus', 10 | target: 'node18', 11 | outExtension: () => ({ js: '.js' }) 12 | }); 13 | -------------------------------------------------------------------------------- /shared/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import {defineConfig} from 'vitest/config'; 2 | 3 | import {CoverageV8Options} from "vitest/node"; 4 | 5 | const v8CoverageSettings: CoverageV8Options = { 6 | enabled: true, 7 | reporter: ['text', 'json', 'html'], 8 | thresholds: { 9 | branches: 85, 10 | functions: 75, 11 | lines: 75, 12 | statements: 75 13 | } 14 | } 15 | 16 | export default defineConfig({ 17 | test: { 18 | globals: true, 19 | environment: 'node', 20 | coverage: { 21 | provider: 'v8', 22 | ...v8CoverageSettings, 23 | }, 24 | } 25 | }) -------------------------------------------------------------------------------- /template-bundles/control/control-requirement.hbs: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "{{ $schema }}", 3 | "title": "Evidence of pre-production review", 4 | "control-id": "{{ control-id }}", 5 | "name": "{{ name }}", 6 | "scope-text": "{{ scope-text }}", 7 | "scope-rego": "{{ scope-rego }}", 8 | "description": "{{ description }}" 9 | } 10 | -------------------------------------------------------------------------------- /template-bundles/control/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "control-generation", 3 | "transformer": "control-transformer", 4 | "templates": [ 5 | { 6 | "template": "control-requirement.hbs", 7 | "from": "control-specification", 8 | "output": "{{id}}.json", 9 | "output-type": "single" 10 | } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /tsconfig.base.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2021", 4 | "module": "ESNext", 5 | "moduleResolution": "Node", 6 | "declaration": true, 7 | "sourceMap": true, 8 | "outDir": "dist", 9 | "strict": false, 10 | "esModuleInterop": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "skipLibCheck": true, 13 | "resolveJsonModule": true, 14 | "types": [ 15 | "vitest", 16 | "node", 17 | ] 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /vitest-globals.d.ts: -------------------------------------------------------------------------------- 1 | /// --------------------------------------------------------------------------------