Skip to content

Commit fc021db

Browse files
committed
Render pydantic v2 types
1 parent 59debff commit fc021db

File tree

7 files changed

+100
-146
lines changed

7 files changed

+100
-146
lines changed
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
FROM python:3.9-slim
2+
3+
WORKDIR /app
4+
5+
# Install the latest version of datamodel-code-generator
6+
RUN pip install --no-cache-dir 'datamodel-code-generator>=0.25.0'
7+
8+
# Copy the OpenAPI file
9+
COPY api.json /app/api.json
10+
11+
# Generate the models with Pydantic v2 compatibility
12+
RUN datamodel-codegen --input api.json --input-file-type openapi --output /app/models.py \
13+
--output-model-type pydantic_v2.BaseModel \
14+
--target-python-version 3.11 \
15+
--field-constraints
16+
17+
CMD ["cat", "/app/models.py"]
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
FROM node:18-slim
2+
3+
WORKDIR /app
4+
5+
# Install dependencies
6+
RUN npm install openapi-typescript
7+
8+
# Copy files
9+
COPY api.json /app/api.json
10+
COPY generate.js /app/generate.js
11+
12+
# Generate the models
13+
RUN node generate.js
14+
15+
CMD ["cat", "/app/models.ts"]

Containers/generate.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
const fs = require('fs');
2+
const { exec } = require('child_process');
3+
4+
// Generate the base TypeScript file from OpenAPI
5+
exec('npx openapi-typescript /app/api.json -o /app/models.ts', (error, stdout, stderr) => {
6+
if (error) {
7+
console.error('Error generating TypeScript types:', error);
8+
console.error(stderr);
9+
process.exit(1);
10+
}
11+
console.log(stdout);
12+
console.log('TypeScript base models generated successfully');
13+
14+
// Now create an index.ts file that exports all the components.schemas
15+
const indexContent = `// Auto-generated index file
16+
// Export all schemas from the OpenAPI specification
17+
export * from './models';
18+
export { components } from './models';
19+
20+
// Re-export schemas as top-level types for easier importing
21+
import { components } from './models';
22+
export type Schemas = components['schemas'];
23+
24+
// Make each schema available as a top-level export
25+
type SchemaNames = keyof components['schemas'];
26+
type ExtractSchema<K extends SchemaNames> = components['schemas'][K];
27+
28+
${Object.keys(require('/app/api.json').components.schemas)
29+
.map(schemaName => `export type ${schemaName} = ExtractSchema<'${schemaName}'>`)
30+
.join('\n')
31+
}
32+
`;
33+
34+
fs.writeFileSync('/app/index.ts', indexContent);
35+
console.log('TypeScript index with type exports generated successfully');
36+
});

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ For a completely containerized build without requiring local installations:
5151
chmod +x build-docker.sh build-types.sh
5252

5353
# Run the Smithy build in Docker
54-
./build-docker.sh
54+
./build-openapi.sh
5555
```
5656

5757
This will:

build-docker.sh

Lines changed: 0 additions & 44 deletions
This file was deleted.

build-types.sh

Lines changed: 8 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -69,32 +69,14 @@ fi
6969
if [[ "$PYTHON" == "true" ]]; then
7070
echo "Generating Python models..."
7171

72-
# Create a temporary directory for our Dockerfile and context
72+
# Create a temporary directory for our container context
7373
TEMP_DIR=$(mktemp -d)
7474

7575
# Copy the OpenAPI file to the temp directory
7676
cp "$OPENAPI_FILE" "$TEMP_DIR/api.json"
7777

78-
# Create a Dockerfile for Python
79-
cat > "$TEMP_DIR/Dockerfile" << EOF
80-
FROM python:3.9-slim
81-
82-
WORKDIR /app
83-
84-
# Install dependencies
85-
RUN pip install --no-cache-dir datamodel-code-generator
86-
87-
# Copy the OpenAPI file
88-
COPY api.json /app/api.json
89-
90-
# Generate the models
91-
RUN datamodel-codegen --input api.json --input-file-type openapi --output /app/models.py
92-
93-
CMD ["cat", "/app/models.py"]
94-
EOF
95-
96-
# Build and run the container, capturing the output directly
97-
(cd "$TEMP_DIR" && docker build -t equaliq-python-codegen .)
78+
# Build and run the container using the external Containerfile
79+
docker build -t equaliq-python-codegen -f Containers/Containerfile-python-codegen "$TEMP_DIR"
9880

9981
# Ensure the python package directory exists
10082
mkdir -p "$PYTHON_PACKAGE_DIR"
@@ -122,73 +104,17 @@ fi
122104
if [[ "$TYPESCRIPT" == "true" ]]; then
123105
echo "Generating TypeScript types..."
124106

125-
# Create a temporary directory for our Dockerfile and context
107+
# Create a temporary directory for our container context
126108
TEMP_DIR=$(mktemp -d)
127109

128110
# Copy the OpenAPI file to the temp directory
129111
cp "$OPENAPI_FILE" "$TEMP_DIR/api.json"
130112

131-
# Create a typescript generator script
132-
cat > "$TEMP_DIR/generate.js" << EOF
133-
const fs = require('fs');
134-
const { exec } = require('child_process');
135-
136-
// Generate the base TypeScript file from OpenAPI
137-
exec('npx openapi-typescript /app/api.json -o /app/models.ts', (error, stdout, stderr) => {
138-
if (error) {
139-
console.error('Error generating TypeScript types:', error);
140-
console.error(stderr);
141-
process.exit(1);
142-
}
143-
console.log(stdout);
144-
console.log('TypeScript base models generated successfully');
145-
146-
// Now create an index.ts file that exports all the components.schemas
147-
const indexContent = \`// Auto-generated index file
148-
// Export all schemas from the OpenAPI specification
149-
export * from './models';
150-
export { components } from './models';
151-
152-
// Re-export schemas as top-level types for easier importing
153-
import { components } from './models';
154-
export type Schemas = components['schemas'];
155-
156-
// Make each schema available as a top-level export
157-
type SchemaNames = keyof components['schemas'];
158-
type ExtractSchema<K extends SchemaNames> = components['schemas'][K];
159-
160-
\${Object.keys(require('/app/api.json').components.schemas)
161-
.map(schemaName => \`export type \${schemaName} = ExtractSchema<'\${schemaName}'>\`)
162-
.join('\\n')
163-
}
164-
\`;
165-
166-
fs.writeFileSync('/app/index.ts', indexContent);
167-
console.log('TypeScript index with type exports generated successfully');
168-
});
169-
EOF
170-
171-
# Create a Dockerfile for TypeScript
172-
cat > "$TEMP_DIR/Dockerfile" << EOF
173-
FROM node:18-slim
174-
175-
WORKDIR /app
176-
177-
# Install dependencies
178-
RUN npm install openapi-typescript
179-
180-
# Copy files
181-
COPY api.json /app/api.json
182-
COPY generate.js /app/generate.js
183-
184-
# Generate the models
185-
RUN node generate.js
186-
187-
CMD ["cat", "/app/models.ts"]
188-
EOF
113+
# Copy the generate.js file to the temp directory
114+
cp "Containers/generate.js" "$TEMP_DIR/generate.js"
189115

190-
# Build and run the container, capturing the output directly
191-
(cd "$TEMP_DIR" && docker build -t equaliq-ts-codegen .)
116+
# Build and run the container using the external Containerfile
117+
docker build -t equaliq-ts-codegen -f Containers/Containerfile-typescript-codegen "$TEMP_DIR"
192118

193119
# Ensure the typescript package directory exists
194120
mkdir -p "$TYPESCRIPT_PACKAGE_DIR"

python/api_model/types/models.py

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
# generated by datamodel-codegen:
22
# filename: api.json
3-
# timestamp: 2025-04-06T09:43:17+00:00
3+
# timestamp: 2025-04-07T07:17:17+00:00
44

55
from __future__ import annotations
66

77
from enum import Enum
88
from typing import Any, List, Optional
99

10-
from pydantic import BaseModel, constr
10+
from pydantic import BaseModel, Field, RootModel
1111

1212

1313
class AccountType(Enum):
@@ -28,6 +28,10 @@ class ContractStatus(Enum):
2828
awaiting_upload = 'awaiting_upload'
2929

3030

31+
class SharedWithItem(RootModel[str]):
32+
root: str = Field(..., pattern='^[A-Za-z0-9-]+$')
33+
34+
3135
class ContractType(Enum):
3236
recording = 'recording'
3337
publishing = 'publishing'
@@ -37,27 +41,27 @@ class ContractType(Enum):
3741

3842

3943
class DeleteContractRequestContent(BaseModel):
40-
contractId: constr(regex=r'^[A-Za-z0-9-]+$')
44+
contractId: str = Field(..., pattern='^[A-Za-z0-9-]+$')
4145

4246

4347
class DeleteContractResponseContent(BaseModel):
4448
success: bool
4549

4650

4751
class GetContractReadURLRequestContent(BaseModel):
48-
contractId: constr(regex=r'^[A-Za-z0-9-]+$')
52+
contractId: str = Field(..., pattern='^[A-Za-z0-9-]+$')
4953

5054

5155
class GetContractReadURLResponseContent(BaseModel):
5256
url: str
5357

5458

5559
class GetContractRequestContent(BaseModel):
56-
contractId: constr(regex=r'^[A-Za-z0-9-]+$')
60+
contractId: str = Field(..., pattern='^[A-Za-z0-9-]+$')
5761

5862

5963
class GetProfileRequestContent(BaseModel):
60-
userId: Optional[constr(regex=r'^[A-Za-z0-9-]+$')] = None
64+
userId: Optional[str] = Field(None, pattern='^[A-Za-z0-9-]+$')
6165

6266

6367
class GetUploadURLRequestContent(BaseModel):
@@ -91,19 +95,19 @@ class ResourceNotFoundErrorResponseContent(BaseModel):
9195

9296

9397
class ShareContractRequestContent(BaseModel):
94-
contractId: constr(regex=r'^[A-Za-z0-9-]+$')
98+
contractId: str = Field(..., pattern='^[A-Za-z0-9-]+$')
9599
emailsToAdd: Optional[List[str]] = None
96100
emailsToRemove: Optional[List[str]] = None
97101

98102

99103
class SharedUserDetails(BaseModel):
100-
userId: constr(regex=r'^[A-Za-z0-9-]+$')
104+
userId: str = Field(..., pattern='^[A-Za-z0-9-]+$')
101105
email: str
102106
sharedTime: float
103107

104108

105109
class UpdateContractRequestContent(BaseModel):
106-
contractId: constr(regex=r'^[A-Za-z0-9-]+$')
110+
contractId: str = Field(..., pattern='^[A-Za-z0-9-]+$')
107111
name: str
108112

109113

@@ -123,12 +127,12 @@ class UpdateProfileRequestContent(BaseModel):
123127
class UpdateProfileResponseContent(BaseModel):
124128
success: bool
125129
message: str
126-
userId: constr(regex=r'^[A-Za-z0-9-]+$')
130+
userId: str = Field(..., pattern='^[A-Za-z0-9-]+$')
127131
updatedFields: Optional[List[str]] = None
128132

129133

130134
class UserProfile(BaseModel):
131-
userId: Optional[constr(regex=r'^[A-Za-z0-9-]+$')] = None
135+
userId: Optional[str] = Field(None, pattern='^[A-Za-z0-9-]+$')
132136
firstName: Optional[str] = None
133137
lastName: Optional[str] = None
134138
displayName: Optional[str] = None
@@ -142,18 +146,18 @@ class ValidationErrorResponseContent(BaseModel):
142146

143147

144148
class ContractSummaryItem(BaseModel):
145-
contractId: constr(regex=r'^[A-Za-z0-9-]+$')
149+
contractId: str = Field(..., pattern='^[A-Za-z0-9-]+$')
146150
name: str
147151
uploadedOn: float
148152
type: ContractType
149153
status: ContractStatus
150154
isOwner: bool
151-
ownerId: constr(regex=r'^[A-Za-z0-9-]+$')
152-
sharedWith: Optional[List[constr(regex=r'^[A-Za-z0-9-]+$')]] = None
155+
ownerId: str = Field(..., pattern='^[A-Za-z0-9-]+$')
156+
sharedWith: Optional[List[SharedWithItem]] = None
153157

154158

155159
class GetProfileResponseContent(BaseModel):
156-
userId: constr(regex=r'^[A-Za-z0-9-]+$')
160+
userId: str = Field(..., pattern='^[A-Za-z0-9-]+$')
157161
profile: UserProfile
158162

159163

@@ -177,19 +181,19 @@ class QASections(BaseModel):
177181

178182
class ShareContractResponseContent(BaseModel):
179183
success: bool
180-
contractId: constr(regex=r'^[A-Za-z0-9-]+$')
184+
contractId: str = Field(..., pattern='^[A-Za-z0-9-]+$')
181185
sharedWith: List[SharedUserDetails]
182186
added: Optional[List[str]] = None
183187
removed: Optional[List[str]] = None
184188
invalidRemoves: Optional[List[str]] = None
185189

186190

187191
class GetContractResponseContent(BaseModel):
188-
contractId: constr(regex=r'^[A-Za-z0-9-]+$')
192+
contractId: str = Field(..., pattern='^[A-Za-z0-9-]+$')
189193
name: str
190194
type: ContractType
191195
terms: Any
192196
iq_qa: QASections
193197
isOwner: bool
194-
ownerId: constr(regex=r'^[A-Za-z0-9-]+$')
195-
sharedWith: List[constr(regex=r'^[A-Za-z0-9-]+$')]
198+
ownerId: str = Field(..., pattern='^[A-Za-z0-9-]+$')
199+
sharedWith: List[SharedWithItem]

0 commit comments

Comments
 (0)