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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,16 @@
deploy-key: "${{ github.ref_name == 'main' && 'dev' || 'demo' }}"
#deploy-key: dev

build-image-sandbox:
uses: ./.github/workflows/_docker-build-image.yml
secrets: inherit
with:
app-name: sandbox
tag-name: ${{ github.ref_name }}
deploy-key: "${{ github.ref_name == 'main' && 'dev' || 'demo' }}"
#deploy-key: dev

deploy:

Check warning

Code scanning / CodeQL

Workflow does not contain permissions Medium

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {}
needs: [build-image-doj]
uses: ./.github/workflows/_terraform-apply.yml
secrets: inherit
Expand Down
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
# 10x Forms Platform

Test bed for 10x forms tooling, completed as part of the [10x Forms Platform](https://github.com/orgs/GSA-TTS/projects/38?pane=issue&itemId=58755590&issue=GSA-TTS%7C10x-projects%7C29) project.
> **Notice:**
> As of early May 2025, Forms Platform development is paused pending administrative prioritization.

Forms Platform is the product of extensive research and development completed by [10x](https://10x.gsa.gov/), a federal venture studio housed within the [General Services Administration](https://www.gsa.gov/)'s [Technology Transformation Services](https://tts.gsa.gov/).

Forms Platform aims to solve a persistent challenge faced by federal agencies: delivery of compliant and user-friendly forms, delivered cost-effectively, seamlessly integrated with diverse agency workflows, and accessible to non-technical program office staff. Deployed broadly, Forms Platform would serve as a common interface to federal government services, tailored to public sector needs.

If you would like to connect with the team or are interested in Forms Platform for your agency, please contact us at [[email protected]](mailto:[email protected]).

## Overview

Expand Down
2 changes: 2 additions & 0 deletions apps/sandbox/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
dist/
*.db
48 changes: 48 additions & 0 deletions apps/sandbox/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# @gsa-tts/forms-server-doj

## 0.1.4

### Patch Changes

- Updated dependencies
- @gsa-tts/[email protected]

## 0.1.3

### Patch Changes

- Updated dependencies
- @gsa-tts/[email protected]

## 0.1.2

### Patch Changes

- Initial prerelease
- Updated dependencies
- @gsa-tts/[email protected]
- @gsa-tts/[email protected]
- @gsa-tts/[email protected]

## 0.1.1

### Patch Changes

- Initial prerelease
- Updated dependencies
- @gsa-tts/[email protected]
- @gsa-tts/[email protected]
- @gsa-tts/[email protected]

## 0.1.0

### Minor Changes

- Initial prerelease

### Patch Changes

- Updated dependencies
- @gsa-tts/[email protected]
- @gsa-tts/[email protected]
- @gsa-tts/[email protected]
3 changes: 3 additions & 0 deletions apps/sandbox/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# @gsa-tts/forms-sandbox

Sandbox application to evaluate platform functionality.
14 changes: 14 additions & 0 deletions apps/sandbox/build.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import esbuild from 'esbuild';

esbuild
.build({
bundle: true,
entryPoints: ['./src/index.ts'],
format: 'esm',
minify: true,
outdir: './dist',
platform: 'node',
sourcemap: true,
target: 'esnext',
})
.catch(() => process.exit(1));
25 changes: 25 additions & 0 deletions apps/sandbox/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"name": "@gsa-tts/forms-sandbox",
"version": "0.1.4",
"description": "Form server sandbox for evaluating functionality.",
"type": "module",
"license": "CC0",
"main": "src/index.ts",
"private": true,
"scripts": {
"build": "tsup src/* --format esm",
"clean": "rimraf dist tsconfig.tsbuildinfo coverage",
"dev": "tsup src/* --watch --format esm",
"start": "VCAP_SERVICES='{\"aws-rds\": [{ \"credentials\": { \"uri\": \"\" }}]}' node dist/index.js",
"test": "vitest run --coverage"
},
"dependencies": {
"@gsa-tts/forms-database": "workspace:*",
"@gsa-tts/forms-infra-core": "workspace:*",
"@gsa-tts/forms-server": "workspace:*"
},
"devDependencies": {
"@types/supertest": "^6.0.2",
"supertest": "^7.0.0"
}
}
52 changes: 52 additions & 0 deletions apps/sandbox/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { createPostgresDatabaseContext } from '@gsa-tts/forms-database/context';
import { getAWSSecretsManagerVault } from '@gsa-tts/forms-infra-core';

import { createCustomServer } from './server.js';

const port = process.env.PORT || 4321;

const getCloudGovServerSecrets = () => {
if (process.env.VCAP_SERVICES === undefined) {
return;
}
const services = JSON.parse(process.env.VCAP_SERVICES || '{}');
return {
//loginGovClientSecret: services['user-provided']?.credentials?.SECRET_LOGIN_GOV_PRIVATE_KEY,
dbUri: services['aws-rds'][0].credentials.uri as string,
};
};

const getAppRunnerSecrets = async () => {
const secrets = {
dbHost: process.env.DB_HOST,
dbPort: process.env.DB_PORT,
dbName: process.env.DB_NAME,
dbSecretArn: process.env.DB_SECRET_ARN,
}
if (secrets.dbHost === undefined || secrets.dbPort === undefined || secrets.dbName === undefined || secrets.dbSecretArn === undefined) {
return;
}

const vault = getAWSSecretsManagerVault();
const dbSecret = await vault.getSecret(secrets.dbSecretArn);
if (dbSecret === undefined) {
console.error('Error getting secret:', secrets.dbSecretArn);
return;
}
const secret = JSON.parse(dbSecret);
return {
dbUri: `postgresql://${secret.username}:${secret.password}@${secret.dbHost}:${secret.dbPort}/${secret.dbName}`
};
};

const secrets = getCloudGovServerSecrets() || (await getAppRunnerSecrets());
if (secrets === undefined) {
console.error('Error getting secrets');
process.exit(1);
}

const db = await createPostgresDatabaseContext(secrets.dbUri, true);
const server = await createCustomServer(db);
server.listen(port, () => {
console.log(`Server running on http://localhost:${port}`);
});
19 changes: 19 additions & 0 deletions apps/sandbox/src/server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { type DatabaseContext } from '@gsa-tts/forms-database';
import { createServer } from '@gsa-tts/forms-server';

export const createCustomServer = async (db: DatabaseContext): Promise<any> => {
return createServer({
agencyBranding: false,
title: 'Form Service Sandbox',
db,
loginGovOptions: {
loginGovUrl: 'https://idp.int.identitysandbox.gov',
clientId:
'urn:gov:gsa:openidconnect.profiles:sp:sso:gsa:tts-10x-atj-dev-server-doj',
//clientSecret: '', // secrets.loginGovClientSecret,
},
isUserAuthorized: async (email: string) => {
return email.endsWith('.gov');
},
});
};
16 changes: 16 additions & 0 deletions apps/sandbox/tests/integration.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import request from 'supertest';
import { describe, expect, test } from 'vitest';

import { createInMemoryDatabaseContext } from '@gsa-tts/forms-database/context';

import { createCustomServer } from '../src/server';

describe('Form Service Sandbox', () => {
test('renders the home page', async () => {
const db = await createInMemoryDatabaseContext();
const app = await createCustomServer(db);
const response = await request(app).get('/');
expect(response.ok).toBe(true);
expect(response.text).toMatch(/Form Service Sandbox/);
});
});
11 changes: 11 additions & 0 deletions apps/sandbox/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"module": "NodeNext",
"moduleResolution": "NodeNext",
"outDir": "./dist",
"emitDeclarationOnly": true
},
"include": ["./src"],
"references": []
}
10 changes: 10 additions & 0 deletions apps/sandbox/vitest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { defineConfig } from 'vitest/config';

import sharedTestConfig from '../../vitest.shared';

export default defineConfig({
...sharedTestConfig,
test: {
...sharedTestConfig.test,
},
});
1 change: 1 addition & 0 deletions apps/server-doj/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { createServer } from '@gsa-tts/forms-server';

export const createCustomServer = async (db: DatabaseContext): Promise<any> => {
return createServer({
agencyBranding: true,
title: 'DOJ Form Service',
db,
loginGovOptions: {
Expand Down
2 changes: 1 addition & 1 deletion documents/adr/0005-build-system.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# 5. Infrastructure as Code
# 5. Build system

Date: 2023-10-12

Expand Down
2 changes: 1 addition & 1 deletion documents/adr/0008-initial-form-handling-strategy.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,4 @@ By starting small and not pulling in dependencies we do not immediately need, we

However, we risk losing the opportunity to quickly get "big wins" by pulling in a more fully-baked interview format at the onset. As a result, such solutions should be considered near-term integration opportunities.

This ADR will need to be continually reevaluated through the course of 10x phase 3 work.
This ADR should be continually reevaluated through the course of development.
2 changes: 1 addition & 1 deletion documents/adr/0013-database-strategy.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ The database should be commonly used and easy to host in many varied deployment

## Decision

We will use PostgreSQL in our production deployments. We will use [Testcontainers](https://testcontainers.com/) to unit-test database gateway logic against a PostgreSQL container. Integration testing will be handled with an in-memory SQLite database.
We will use PostgreSQL in our production deployments. We will use [Testcontainers](https://testcontainers.com/) to unit-test database gateway logic against a PostgreSQL container. Integration testing will be handled with ephemeral in-memory SQLite databases.

[Knex.js](https://knexjs.org/) is the most widely-used node.js query builder, and has backend adapters for most common relational databases. We will utilize Knex.js for its migration support. Knex.js may also be used for queries, as appropriate.

Expand Down
2 changes: 2 additions & 0 deletions documents/adr/0014-authentication.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ Date: 2024-09-04

Approved

> *NOTE: Lucia [is deprecated](https://github.com/lucia-auth/lucia/discussions/1714), and there were some challenges integrating it cleanly into the [auth package](../../packages/auth/). As a result, this ADR should be reconsidered.*

## Context

Forms Platform requires a method of authenticating users. We are inclined to default to [Login.gov](https://login.gov/), a government-wide federated service hosted by [TTS](https://www.gsa.gov/about-us/organization/federal-acquisition-service/technology-transformation-services), unless circumstances prevent its usage.
Expand Down
2 changes: 2 additions & 0 deletions documents/adr/0015-rest-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ Date: 2024-09-04

Approved

> *NOTE: The strategy for API management would benefit from being reconsidered.*

## Context

During the prototyping and demo phase, Forms Platform used client-side services that persisted to browser local storage. As we introduce a backend to support agency users, we need to structure an API for the content authoring operations of the platform.
Expand Down
58 changes: 58 additions & 0 deletions documents/adr/0017-use-named-exports.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# 17. Use Named Exports Over Default Exports and Avoid index Files

Date: 04-16-2025

## Status

Approved

## Context

During the recent refactoring of the `Form` and `FormManager` components in the `design` package, we noticed inconsistencies in the use of default exports, named exports, and `index` files. Some files used default exports, while others used named exports. Additionally, many directories relied on `index.tsx` files, which made it harder to locate specific components or utilities in the codebase.

Default exports can sometimes lead to ambiguity, especially when renaming imports, and they do not provide the same level of tooling support (e.g., auto-imports in IDEs) as named exports. Named exports, on the other hand, make it clear what is being exported and allow for more consistent imports across the codebase.

Similarly, `index` files can introduce ambiguity when navigating the codebase, as multiple `index.tsx` files across different directories make it harder to locate specific functionality. Using descriptive file names improves clarity and maintainability.

## Decision

We will adopt the following guidelines for exports and file structure moving forward:
1. **Use named exports** for all components, utilities, and types.
2. **Avoid default exports**, except in cases where a file exports a single, primary entity (e.g., a React component that represents the main purpose of the file).
3. **Avoid `index` files** in favor of descriptive file names (e.g., `FormManager.tsx` instead of `index.tsx`).

## Consequences

- **Consistency:** The codebase will have a consistent export style and file structure, making it easier to read and maintain.
- **Tooling Support:** Named exports improve IDE tooling, such as auto-imports and refactoring.
- **Clarity:** Named exports and descriptive file names make it clear what is being exported and where functionality resides, reducing ambiguity.
- **Ease of Navigation:** Avoiding `index` files ensures that file names are descriptive, making it easier to locate specific components or utilities.

## Examples

### Before (Default Export):

```tsx
// FormManager.tsx
export default function FormManager() { ... }

// Importing in other files
import FormManager from './FormManager';
```

### After (Named Export):
```tsx

// FormManager.tsx
export function FormManager() { ... }

// Importing in other files
import { FormManager } from './FormManager';
```

## Related Changes

This decision was applied during the refactoring of the `Form` and `FormManager` components in the design package. As part of this refactor:

- Default exports were replaced with named exports.
- `index.tsx` files were replaced with descriptive file names (e.g., `FormManager.tsx`, `PatternComponents.tsx`).
11 changes: 11 additions & 0 deletions documents/pending-loose-ends.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Pending loose ends

## Context

In early May 2025, Forms Platform development was suspended indefinitely. At that point, the project team was preparing for a pilot production launch, which would have brought functionality to an MVP status and wrapped up technical loose ends. This document describes that unfinished technical work that should be considered project development resumes.

## Task list

- ADR [0014-authentication](./adr/0014-authentication.md) describes an authentication approach that leverages the Lucia library. Lucia usage should be reconsidered when integrating with a production identity provider.
- ADR [0015-rest-api](./adr/0015-rest-api.md) describes a simple strategy for bootstrapping an API with Astro. At some point, before extensive API growth but probably perhaps pilot, a more thought out strategy should be considered.
- ADRs [0007-initial-css-strategy](./adr/0007-initial-css-strategy.md) and [0016-unused-css](./adr/0016-unused-css.md) describe CSS approaches. This approach should be reconsidered when the ability to integrate with agency USWDS themes and component encapsulation become important qualities.
29 changes: 29 additions & 0 deletions e2e/src/preview.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { test, expect } from './fixtures/import-file.fixture.js';
import { FormCreatePage } from './models/form-create-page.js';

test.describe('Form builder preview', () => {
let formPage: FormCreatePage;
let formUrl: string;

test.beforeEach(async ({ page, formUrl: url }) => {
formPage = new FormCreatePage(page);
formUrl = url;
await formPage.navigateTo(formUrl);
});

test('Preview navigation', async ({ page }) => {
await page.getByRole('link', { name: 'Preview' }).click();
expect(page.url()).toContain('preview');
const nextPageIndex = 1;
const menu = page.getByRole('navigation', { name: 'Section navigation' });
await expect(menu).toBeVisible();
const links = menu.getByRole('link');

const numLinks = await links.count();
await expect(page.getByRole('heading', { name: `${nextPageIndex} of ${numLinks}`})).toBeVisible();
expect(numLinks).toBeGreaterThanOrEqual(nextPageIndex + 1);

await links.nth(nextPageIndex).click();
await expect(page.getByRole('heading', { name: `${nextPageIndex + 1} of ${numLinks}`})).toBeVisible();
});
});
Loading
Loading