@johnlwin-test/create-repro 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +175 -0
- package/README.md +116 -0
- package/package.json +33 -0
- package/src/cli.js +308 -0
- package/src/operations.js +268 -0
- package/src/regions.js +235 -0
- package/src/sdks/java/generate.js +79 -0
- package/src/sdks/java/templates/LOG.md +0 -0
- package/src/sdks/java/templates/Main.java +24 -0
- package/src/sdks/java/templates/README.md +14 -0
- package/src/sdks/java/templates/pom.xml +61 -0
- package/src/sdks/java/utils/maven.js +24 -0
- package/src/sdks/javascript/environments/browser/generate.js +208 -0
- package/src/sdks/javascript/environments/browser/templates/index.html +27 -0
- package/src/sdks/javascript/environments/browser/templates/index.js +62 -0
- package/src/sdks/javascript/environments/node/generate.js +55 -0
- package/src/sdks/javascript/environments/node/templates/index.js +12 -0
- package/src/sdks/javascript/environments/react-native/generate.js +286 -0
- package/src/sdks/javascript/environments/react-native/templates/App.js +147 -0
- package/src/services.js +310 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// import fs from "fs";
|
|
2
|
+
// import path from "path";
|
|
3
|
+
// import { fileURLToPath } from "url";
|
|
4
|
+
|
|
5
|
+
// const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
6
|
+
|
|
7
|
+
// export const generateMavenWrapper = (projectDir) => {
|
|
8
|
+
// // Copy Maven wrapper files from template directory
|
|
9
|
+
// const wrapperFiles = [
|
|
10
|
+
// "mvnw",
|
|
11
|
+
// "mvnw.cmd",
|
|
12
|
+
// ".mvn/wrapper/maven-wrapper.jar",
|
|
13
|
+
// ".mvn/wrapper/maven-wrapper.properties",
|
|
14
|
+
// ];
|
|
15
|
+
// wrapperFiles.forEach((file) => {
|
|
16
|
+
// const source = path.join(__dirname, "templates/maven", file);
|
|
17
|
+
// const dest = path.join(projectDir, file);
|
|
18
|
+
// fs.copyFileSync(source, dest);
|
|
19
|
+
// });
|
|
20
|
+
// // Make mvnw executable
|
|
21
|
+
// if (process.platform !== "win32") {
|
|
22
|
+
// fs.chmodSync(path.join(projectDir, "mvnw"), 0o755);
|
|
23
|
+
// }
|
|
24
|
+
// };
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import { readFileSync } from "fs";
|
|
3
|
+
import { fileURLToPath } from "url";
|
|
4
|
+
import { dirname, join } from "path";
|
|
5
|
+
import { getServiceDisplayName } from "../../../../services.js";
|
|
6
|
+
|
|
7
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
|
|
9
|
+
export const generateBrowserProject = (answers, projectDir) => {
|
|
10
|
+
// Use actual client name from SDK package, with fallback to derived name
|
|
11
|
+
const serviceName = answers.service.replace("@aws-sdk/client-", "");
|
|
12
|
+
const clientName = answers.clientName || (getServiceDisplayName(answers.service)
|
|
13
|
+
.split(" ")
|
|
14
|
+
.map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
|
|
15
|
+
.join("") + "Client");
|
|
16
|
+
|
|
17
|
+
// 1. Generate main JavaScript file
|
|
18
|
+
const jsTemplate = readFileSync(
|
|
19
|
+
join(__dirname, "templates/index.js"),
|
|
20
|
+
"utf-8"
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
// Extract operation name without "Command" suffix for template
|
|
24
|
+
const operationName = answers.operationCommand
|
|
25
|
+
? answers.operationCommand.replace(/Command$/, '')
|
|
26
|
+
: answers.operation;
|
|
27
|
+
|
|
28
|
+
const populatedJs = jsTemplate
|
|
29
|
+
.replace(/{{serviceClient}}/g, clientName)
|
|
30
|
+
.replace(/{{service}}/g, answers.service)
|
|
31
|
+
.replace(/{{operation}}/g, operationName);
|
|
32
|
+
|
|
33
|
+
// 2. Generate HTML file
|
|
34
|
+
const htmlTemplate = readFileSync(
|
|
35
|
+
join(__dirname, "templates/index.html"),
|
|
36
|
+
"utf-8"
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
// 3. Create package.json
|
|
40
|
+
const pkg = {
|
|
41
|
+
name: answers.projectName.toLowerCase().replace(/ /g, "-"),
|
|
42
|
+
version: "1.0.0",
|
|
43
|
+
private: true,
|
|
44
|
+
type: "module",
|
|
45
|
+
scripts: {
|
|
46
|
+
start: "vite --open",
|
|
47
|
+
build: "vite build",
|
|
48
|
+
},
|
|
49
|
+
devDependencies: {
|
|
50
|
+
vite: "^5.0.0",
|
|
51
|
+
},
|
|
52
|
+
dependencies: {
|
|
53
|
+
[answers.service]: "latest",
|
|
54
|
+
"@aws-sdk/client-cognito-identity": "latest",
|
|
55
|
+
"@aws-sdk/credential-provider-cognito-identity": "latest",
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
// 4. Write files
|
|
60
|
+
fs.writeFileSync(join(projectDir, "index.js"), populatedJs);
|
|
61
|
+
fs.writeFileSync(join(projectDir, "index.html"), htmlTemplate);
|
|
62
|
+
fs.writeFileSync(
|
|
63
|
+
join(projectDir, "package.json"),
|
|
64
|
+
JSON.stringify(pkg, null, 2)
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
// 5. Add security documentation
|
|
68
|
+
const cognitoSetup = `# Cognito Setup for Browser Authentication
|
|
69
|
+
|
|
70
|
+
This browser-based AWS SDK project requires authentication via Amazon Cognito Identity Pool.
|
|
71
|
+
|
|
72
|
+
## Why Cognito?
|
|
73
|
+
|
|
74
|
+
Browser applications cannot securely store AWS credentials. Cognito Identity Pool provides:
|
|
75
|
+
- Temporary, scoped credentials for browser clients
|
|
76
|
+
- No long-term credentials in client code
|
|
77
|
+
- Fine-grained access control via IAM roles
|
|
78
|
+
|
|
79
|
+
## Quick Setup Steps
|
|
80
|
+
|
|
81
|
+
### 1. Create a Cognito Identity Pool for Testing
|
|
82
|
+
|
|
83
|
+
1. Go to [AWS Console > Cognito > Identity Pools](https://console.aws.amazon.com/cognito/v2/identity)
|
|
84
|
+
2. Click "Create identity pool"
|
|
85
|
+
3. Enter a pool name (e.g., "test-${serviceName}-pool")
|
|
86
|
+
4. Enable "Unauthenticated identities" for testing
|
|
87
|
+
5. Click "Create pool"
|
|
88
|
+
6. **Note down the Identity Pool ID** (format: \`region:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\`)
|
|
89
|
+
|
|
90
|
+
### 2. Add a Policy to the Unauthenticated IAM Role
|
|
91
|
+
|
|
92
|
+
The policy should be specific to the operations you want to test.
|
|
93
|
+
|
|
94
|
+
1. In the Cognito Identity Pool page, go to the "User access" tab
|
|
95
|
+
2. Click on the "Unauthenticated role" link (opens IAM console)
|
|
96
|
+
3. Click "Add permissions" > "Create inline policy"
|
|
97
|
+
4. Use the JSON editor and add a policy for ${serviceName}:
|
|
98
|
+
|
|
99
|
+
**Example policy for ${serviceName} (adjust based on your operation):**
|
|
100
|
+
|
|
101
|
+
\`\`\`json
|
|
102
|
+
{
|
|
103
|
+
"Version": "2012-10-17",
|
|
104
|
+
"Statement": [
|
|
105
|
+
{
|
|
106
|
+
"Effect": "Allow",
|
|
107
|
+
"Action": [
|
|
108
|
+
"${serviceName}:List*",
|
|
109
|
+
"${serviceName}:Describe*",
|
|
110
|
+
"${serviceName}:Get*"
|
|
111
|
+
],
|
|
112
|
+
"Resource": "*"
|
|
113
|
+
}
|
|
114
|
+
]
|
|
115
|
+
}
|
|
116
|
+
\`\`\`
|
|
117
|
+
|
|
118
|
+
**For specific operations**, be more restrictive:
|
|
119
|
+
\`\`\`json
|
|
120
|
+
{
|
|
121
|
+
"Version": "2012-10-17",
|
|
122
|
+
"Statement": [
|
|
123
|
+
{
|
|
124
|
+
"Effect": "Allow",
|
|
125
|
+
"Action": [
|
|
126
|
+
"${serviceName}:${answers.operation.split('-').map(w => w.charAt(0).toUpperCase() + w.slice(1)).join('')}"
|
|
127
|
+
],
|
|
128
|
+
"Resource": "*"
|
|
129
|
+
}
|
|
130
|
+
]
|
|
131
|
+
}
|
|
132
|
+
\`\`\`
|
|
133
|
+
|
|
134
|
+
5. Name the policy (e.g., "test-${serviceName}-policy")
|
|
135
|
+
6. Click "Create policy"
|
|
136
|
+
|
|
137
|
+
### 3. Update index.js Configuration
|
|
138
|
+
|
|
139
|
+
Open \`index.js\` and update these constants:
|
|
140
|
+
|
|
141
|
+
\`\`\`javascript
|
|
142
|
+
const REGION = "${answers.region}"; // Your AWS region
|
|
143
|
+
const IDENTITY_POOL_ID = "YOUR_IDENTITY_POOL_ID"; // From step 1
|
|
144
|
+
\`\`\`
|
|
145
|
+
|
|
146
|
+
## Testing Your Setup
|
|
147
|
+
|
|
148
|
+
1. Install dependencies:
|
|
149
|
+
\`\`\`bash
|
|
150
|
+
npm install
|
|
151
|
+
\`\`\`
|
|
152
|
+
|
|
153
|
+
2. Start the development server:
|
|
154
|
+
\`\`\`bash
|
|
155
|
+
npm start
|
|
156
|
+
\`\`\`
|
|
157
|
+
|
|
158
|
+
3. Open your browser (should open automatically)
|
|
159
|
+
4. Check the browser console for any errors
|
|
160
|
+
5. If successful, you should see the API response displayed
|
|
161
|
+
|
|
162
|
+
## Common Errors and Solutions
|
|
163
|
+
|
|
164
|
+
### "Configuration Error: Please update REGION and IDENTITY_POOL_ID"
|
|
165
|
+
- You haven't updated the placeholders in \`index.js\`
|
|
166
|
+
- Update \`REGION\` and \`IDENTITY_POOL_ID\` with your actual values
|
|
167
|
+
|
|
168
|
+
### "NotAuthorizedException" or "AccessDeniedException"
|
|
169
|
+
- The IAM role doesn't have sufficient permissions
|
|
170
|
+
- Review and update the IAM policy attached to your Cognito unauthenticated role
|
|
171
|
+
- Ensure the policy includes the specific ${serviceName} actions you're testing
|
|
172
|
+
|
|
173
|
+
### "InvalidIdentityPoolConfigurationException"
|
|
174
|
+
- The Identity Pool ID or region is incorrect
|
|
175
|
+
- Verify the Identity Pool ID format: \`region:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\`
|
|
176
|
+
- Ensure the region matches where you created the Identity Pool
|
|
177
|
+
|
|
178
|
+
## Security Best Practices
|
|
179
|
+
|
|
180
|
+
- **Use unauthenticated access only for testing**
|
|
181
|
+
- **Implement authenticated access for production** using Cognito User Pools
|
|
182
|
+
- **Apply least-privilege IAM policies** - only grant necessary permissions
|
|
183
|
+
- **Monitor Cognito usage** in CloudWatch
|
|
184
|
+
- **Set up CloudTrail** to audit API calls
|
|
185
|
+
- **Consider using Cognito User Pools** for user authentication in production
|
|
186
|
+
|
|
187
|
+
## Reference Links
|
|
188
|
+
|
|
189
|
+
- [Create a Cognito Identity Pool](https://docs.aws.amazon.com/cognito/latest/developerguide/tutorial-create-identity-pool.html)
|
|
190
|
+
- [Cognito Identity Pools Documentation](https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-identity.html)
|
|
191
|
+
- [AWS SDK for JavaScript v3 - Browser](https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/getting-started-browser.html)
|
|
192
|
+
- [IAM Policies for ${serviceName}](https://docs.aws.amazon.com/service-authorization/latest/reference/list_${serviceName.toLowerCase()}.html)
|
|
193
|
+
|
|
194
|
+
## Production Considerations
|
|
195
|
+
|
|
196
|
+
For production applications:
|
|
197
|
+
|
|
198
|
+
1. **Use Cognito User Pools** for user authentication
|
|
199
|
+
2. **Implement authenticated identities** instead of unauthenticated
|
|
200
|
+
3. **Use fine-grained IAM policies** based on user attributes
|
|
201
|
+
4. **Enable MFA** for sensitive operations
|
|
202
|
+
5. **Implement proper error handling** and user feedback
|
|
203
|
+
6. **Use environment variables** for configuration (not hardcoded values)
|
|
204
|
+
7. **Monitor and log** all authentication and authorization events
|
|
205
|
+
`;
|
|
206
|
+
|
|
207
|
+
fs.writeFileSync(join(projectDir, "COGNITO_SETUP.md"), cognitoSetup);
|
|
208
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>AWS SDK Browser Repro</title>
|
|
7
|
+
<style>
|
|
8
|
+
#aws-output,
|
|
9
|
+
#error {
|
|
10
|
+
white-space: pre-wrap;
|
|
11
|
+
font-family: monospace;
|
|
12
|
+
margin: 20px;
|
|
13
|
+
padding: 10px;
|
|
14
|
+
border: 1px solid #ccc;
|
|
15
|
+
}
|
|
16
|
+
#error {
|
|
17
|
+
color: red;
|
|
18
|
+
}
|
|
19
|
+
</style>
|
|
20
|
+
</head>
|
|
21
|
+
<body>
|
|
22
|
+
<h1>Test Environment for AWS SDK for JavaScript</h1>
|
|
23
|
+
<div id="error"></div>
|
|
24
|
+
<div id="aws-output"></div>
|
|
25
|
+
<script type="module" src="./index.js"></script>
|
|
26
|
+
</body>
|
|
27
|
+
</html>
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { {{serviceClient}}, {{operation}}Command } from '{{service}}';
|
|
2
|
+
import { fromCognitoIdentityPool } from "@aws-sdk/credential-provider-cognito-identity";
|
|
3
|
+
import { CognitoIdentityClient } from "@aws-sdk/client-cognito-identity";
|
|
4
|
+
|
|
5
|
+
// Browser-safe UI rendering
|
|
6
|
+
const displayResults = (data) => {
|
|
7
|
+
const output = document.getElementById('aws-output');
|
|
8
|
+
output.textContent = JSON.stringify(data, null, 2);
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const displayError = (message) => {
|
|
12
|
+
const errorElement = document.getElementById('error');
|
|
13
|
+
errorElement.textContent = message;
|
|
14
|
+
console.error(message);
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
// AWS Client Configuration
|
|
18
|
+
// TODO: Replace with your actual Cognito Identity Pool ID and region
|
|
19
|
+
const REGION = "YOUR_REGION"; // e.g., "us-west-2"
|
|
20
|
+
const IDENTITY_POOL_ID = "YOUR_IDENTITY_POOL_ID"; // e.g., "us-west-2:12345678-1234-1234-1234-123456789012"
|
|
21
|
+
|
|
22
|
+
// Validate configuration before initializing client
|
|
23
|
+
if (REGION === "YOUR_REGION" || IDENTITY_POOL_ID === "YOUR_IDENTITY_POOL_ID") {
|
|
24
|
+
displayError(
|
|
25
|
+
"Configuration Error: Please update REGION and IDENTITY_POOL_ID in index.js.\n\n" +
|
|
26
|
+
"Refer to COGNITO_SETUP.md for detailed setup instructions."
|
|
27
|
+
);
|
|
28
|
+
throw new Error("Missing Cognito configuration. See COGNITO_SETUP.md for setup instructions.");
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const client = new {{serviceClient}}({
|
|
32
|
+
region: REGION,
|
|
33
|
+
credentials: fromCognitoIdentityPool({
|
|
34
|
+
client: new CognitoIdentityClient({ region: REGION }),
|
|
35
|
+
identityPoolId: IDENTITY_POOL_ID,
|
|
36
|
+
}),
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// Main execution flow
|
|
40
|
+
(async () => {
|
|
41
|
+
try {
|
|
42
|
+
const response = await client.send(new {{operation}}Command({}));
|
|
43
|
+
displayResults(response);
|
|
44
|
+
} catch (error) {
|
|
45
|
+
console.error('Operation failed:', error);
|
|
46
|
+
|
|
47
|
+
let errorMessage = `Operation failed: ${error.message}`;
|
|
48
|
+
|
|
49
|
+
// Provide helpful error messages for common issues
|
|
50
|
+
if (error.name === 'NotAuthorizedException' || error.name === 'AccessDeniedException') {
|
|
51
|
+
errorMessage += '\n\nThis may be due to insufficient IAM permissions. ' +
|
|
52
|
+
'Check the IAM role attached to your Cognito Identity Pool. ' +
|
|
53
|
+
'Refer to COGNITO_SETUP.md for permission setup.';
|
|
54
|
+
} else if (error.name === 'InvalidIdentityPoolConfigurationException') {
|
|
55
|
+
errorMessage += '\n\nInvalid Cognito Identity Pool configuration. ' +
|
|
56
|
+
'Verify your IDENTITY_POOL_ID and REGION are correct. ' +
|
|
57
|
+
'Refer to COGNITO_SETUP.md for setup instructions.';
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
displayError(errorMessage);
|
|
61
|
+
}
|
|
62
|
+
})();
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import { readFileSync } from "fs";
|
|
3
|
+
import { fileURLToPath } from "url";
|
|
4
|
+
import { dirname, join } from "path";
|
|
5
|
+
import { getServiceDisplayName } from "../../../../services.js";
|
|
6
|
+
|
|
7
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
|
|
9
|
+
const generateIndexJs = (answers) => {
|
|
10
|
+
// Use actual client name from SDK package, with fallback to derived name
|
|
11
|
+
const clientName = answers.clientName || (getServiceDisplayName(answers.service)
|
|
12
|
+
.split(" ")
|
|
13
|
+
.map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
|
|
14
|
+
.join("") + "Client");
|
|
15
|
+
|
|
16
|
+
return `
|
|
17
|
+
import { ${clientName}, ${answers.operationCommand} } from '${answers.service}';
|
|
18
|
+
|
|
19
|
+
const main = async () => {
|
|
20
|
+
try {
|
|
21
|
+
const client = new ${clientName}();
|
|
22
|
+
const input = {}; // Add your input parameters here
|
|
23
|
+
const command = new ${answers.operationCommand}(input);
|
|
24
|
+
const response = await client.send(command);
|
|
25
|
+
console.log('Success:', response);
|
|
26
|
+
} catch (error) {
|
|
27
|
+
console.error('Error:', error);
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
main();
|
|
31
|
+
`;
|
|
32
|
+
};
|
|
33
|
+
export const generateNodeProject = (answers, projectDir) => {
|
|
34
|
+
// Create index.js with actual values
|
|
35
|
+
const indexContent = generateIndexJs(answers);
|
|
36
|
+
fs.writeFileSync(join(projectDir, "index.js"), indexContent);
|
|
37
|
+
// Create package.json
|
|
38
|
+
const pkg = {
|
|
39
|
+
name: answers.projectName.toLowerCase().replace(/ /g, "-"),
|
|
40
|
+
version: "1.0.0",
|
|
41
|
+
type: "module",
|
|
42
|
+
dependencies: {
|
|
43
|
+
[answers.service]: "latest",
|
|
44
|
+
"@aws-sdk/credential-provider-node": "latest",
|
|
45
|
+
dotenv: "^16.0.0",
|
|
46
|
+
},
|
|
47
|
+
scripts: {
|
|
48
|
+
start: "node -r dotenv/config index.js",
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
fs.writeFileSync(
|
|
52
|
+
join(projectDir, "package.json"),
|
|
53
|
+
JSON.stringify(pkg, null, 2)
|
|
54
|
+
);
|
|
55
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// ```
|
|
2
|
+
// import { {{serviceClient}}, {{operation}}Command } from '{{service}}';
|
|
3
|
+
// const client = new {{serviceClient}}({
|
|
4
|
+
// region: '{{region}}',
|
|
5
|
+
// });
|
|
6
|
+
// const input = {
|
|
7
|
+
// // {{operation}}Input
|
|
8
|
+
// };
|
|
9
|
+
// const command = new {{operation}}Command(input);
|
|
10
|
+
// const response = await client.send(command);
|
|
11
|
+
// console.log(response);
|
|
12
|
+
// ```;
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import { readFileSync } from "fs";
|
|
3
|
+
import { fileURLToPath } from "url";
|
|
4
|
+
import { dirname, join } from "path";
|
|
5
|
+
import { execSync } from "child_process";
|
|
6
|
+
import { getServiceDisplayName } from "../../../../services.js";
|
|
7
|
+
|
|
8
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
|
|
10
|
+
export const generateReactNativeProject = (answers, projectDir) => {
|
|
11
|
+
console.log("\nInitializing React Native project (this may take a few minutes)...");
|
|
12
|
+
|
|
13
|
+
// Use actual client name from SDK package, with fallback to derived name
|
|
14
|
+
const serviceName = answers.service.replace("@aws-sdk/client-", "");
|
|
15
|
+
const clientName = answers.clientName || (getServiceDisplayName(answers.service)
|
|
16
|
+
.split(" ")
|
|
17
|
+
.map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
|
|
18
|
+
.join("") + "Client");
|
|
19
|
+
|
|
20
|
+
// React Native requires alphanumeric project names (no hyphens, underscores, or special chars)
|
|
21
|
+
const projectName = answers.projectName.replace(/[^a-zA-Z0-9]/g, "");
|
|
22
|
+
|
|
23
|
+
// Update projectDir to match the sanitized name
|
|
24
|
+
const parentDir = dirname(projectDir);
|
|
25
|
+
const actualProjectDir = join(parentDir, projectName);
|
|
26
|
+
|
|
27
|
+
try {
|
|
28
|
+
// Initialize React Native project using the community CLI
|
|
29
|
+
// Use version 0.76 which is compatible with Xcode 15
|
|
30
|
+
// This creates the ios/ and android/ directories with all necessary files
|
|
31
|
+
execSync(`npx @react-native-community/cli@latest init ${projectName} --version 0.76.6 --skip-install`, {
|
|
32
|
+
stdio: 'inherit',
|
|
33
|
+
cwd: parentDir
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
console.log("\nReact Native project initialized successfully!");
|
|
37
|
+
console.log("Customizing project files...\n");
|
|
38
|
+
|
|
39
|
+
} catch (error) {
|
|
40
|
+
throw new Error(`Failed to initialize React Native project: ${error.message}`);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// 1. Generate App.js
|
|
44
|
+
const appTemplate = readFileSync(
|
|
45
|
+
join(__dirname, "templates/App.js"),
|
|
46
|
+
"utf-8"
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
// Extract operation name without "Command" suffix for template
|
|
50
|
+
const operationName = answers.operationCommand
|
|
51
|
+
? answers.operationCommand.replace(/Command$/, '')
|
|
52
|
+
: answers.operation;
|
|
53
|
+
|
|
54
|
+
const populatedApp = appTemplate
|
|
55
|
+
.replace(/{{serviceClient}}/g, clientName)
|
|
56
|
+
.replace(/{{service}}/g, answers.service)
|
|
57
|
+
.replace(/{{operation}}/g, operationName)
|
|
58
|
+
.replace(/{{operationCommand}}/g, answers.operationCommand);
|
|
59
|
+
|
|
60
|
+
// 2. Update package.json with AWS SDK dependencies
|
|
61
|
+
const pkgPath = join(actualProjectDir, "package.json");
|
|
62
|
+
const existingPkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
63
|
+
|
|
64
|
+
existingPkg.dependencies = {
|
|
65
|
+
...existingPkg.dependencies,
|
|
66
|
+
[answers.service]: "latest",
|
|
67
|
+
"@aws-sdk/client-cognito-identity": "latest",
|
|
68
|
+
"@aws-sdk/credential-provider-cognito-identity": "latest",
|
|
69
|
+
"react-native-get-random-values": "^1.11.0",
|
|
70
|
+
"react-native-url-polyfill": "^2.0.0",
|
|
71
|
+
"web-streams-polyfill": "^4.0.0",
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
fs.writeFileSync(pkgPath, JSON.stringify(existingPkg, null, 2));
|
|
75
|
+
|
|
76
|
+
// 3. Replace App.js with our custom implementation
|
|
77
|
+
fs.writeFileSync(join(actualProjectDir, "App.js"), populatedApp);
|
|
78
|
+
|
|
79
|
+
// 4. Add Cognito setup documentation
|
|
80
|
+
const cognitoSetup = `# Cognito Setup for React Native Authentication
|
|
81
|
+
|
|
82
|
+
This React Native AWS SDK project requires authentication via Amazon Cognito Identity Pool.
|
|
83
|
+
|
|
84
|
+
## Why Cognito?
|
|
85
|
+
|
|
86
|
+
Mobile applications should not store AWS credentials directly. Cognito Identity Pool provides:
|
|
87
|
+
- Temporary, scoped credentials for mobile clients
|
|
88
|
+
- No long-term credentials in application code
|
|
89
|
+
- Fine-grained access control via IAM roles
|
|
90
|
+
|
|
91
|
+
## Quick Setup Steps
|
|
92
|
+
|
|
93
|
+
### 1. Install Dependencies
|
|
94
|
+
|
|
95
|
+
\`\`\`bash
|
|
96
|
+
npm install
|
|
97
|
+
\`\`\`
|
|
98
|
+
|
|
99
|
+
### 2. Create a Cognito Identity Pool for Testing
|
|
100
|
+
|
|
101
|
+
### 2. Create a Cognito Identity Pool for Testing
|
|
102
|
+
|
|
103
|
+
1. Go to [AWS Console > Cognito > Identity Pools](https://console.aws.amazon.com/cognito/v2/identity)
|
|
104
|
+
2. Click "Create identity pool"
|
|
105
|
+
3. Enter a pool name (e.g., "test-${serviceName}-pool")
|
|
106
|
+
4. Enable "Unauthenticated identities" for testing
|
|
107
|
+
5. Click "Create pool"
|
|
108
|
+
6. **Note down the Identity Pool ID** (format: \`region:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\`)
|
|
109
|
+
|
|
110
|
+
### 3. Add a Policy to the Unauthenticated IAM Role
|
|
111
|
+
|
|
112
|
+
### 3. Add a Policy to the Unauthenticated IAM Role
|
|
113
|
+
|
|
114
|
+
The policy should be specific to the operations you want to test.
|
|
115
|
+
|
|
116
|
+
1. In the Cognito Identity Pool page, go to the "User access" tab
|
|
117
|
+
2. Click on the "Unauthenticated role" link (opens IAM console)
|
|
118
|
+
3. Click "Add permissions" > "Create inline policy"
|
|
119
|
+
4. Use the JSON editor and add a policy for ${serviceName}:
|
|
120
|
+
|
|
121
|
+
**Example policy for ${serviceName} (adjust based on your operation):**
|
|
122
|
+
|
|
123
|
+
\`\`\`json
|
|
124
|
+
{
|
|
125
|
+
"Version": "2012-10-17",
|
|
126
|
+
"Statement": [
|
|
127
|
+
{
|
|
128
|
+
"Effect": "Allow",
|
|
129
|
+
"Action": [
|
|
130
|
+
"${serviceName}:List*",
|
|
131
|
+
"${serviceName}:Describe*",
|
|
132
|
+
"${serviceName}:Get*"
|
|
133
|
+
],
|
|
134
|
+
"Resource": "*"
|
|
135
|
+
}
|
|
136
|
+
]
|
|
137
|
+
}
|
|
138
|
+
\`\`\`
|
|
139
|
+
|
|
140
|
+
**For specific operations**, be more restrictive:
|
|
141
|
+
\`\`\`json
|
|
142
|
+
{
|
|
143
|
+
"Version": "2012-10-17",
|
|
144
|
+
"Statement": [
|
|
145
|
+
{
|
|
146
|
+
"Effect": "Allow",
|
|
147
|
+
"Action": [
|
|
148
|
+
"${serviceName}:${answers.operation.split('-').map(w => w.charAt(0).toUpperCase() + w.slice(1)).join('')}"
|
|
149
|
+
],
|
|
150
|
+
"Resource": "*"
|
|
151
|
+
}
|
|
152
|
+
]
|
|
153
|
+
}
|
|
154
|
+
\`\`\`
|
|
155
|
+
|
|
156
|
+
5. Name the policy (e.g., "test-${serviceName}-policy")
|
|
157
|
+
6. Click "Create policy"
|
|
158
|
+
|
|
159
|
+
### 4. Update App.js Configuration
|
|
160
|
+
|
|
161
|
+
Open \`App.js\` and update these constants:
|
|
162
|
+
|
|
163
|
+
\`\`\`javascript
|
|
164
|
+
const REGION = "${answers.region}"; // Your AWS region
|
|
165
|
+
const IDENTITY_POOL_ID = "YOUR_IDENTITY_POOL_ID"; // From step 2
|
|
166
|
+
\`\`\`
|
|
167
|
+
|
|
168
|
+
### 5. Install iOS Dependencies (macOS only)
|
|
169
|
+
|
|
170
|
+
\`\`\`bash
|
|
171
|
+
cd ios && pod install && cd ..
|
|
172
|
+
\`\`\`
|
|
173
|
+
|
|
174
|
+
## Running the App
|
|
175
|
+
|
|
176
|
+
### For iOS (macOS only):
|
|
177
|
+
\`\`\`bash
|
|
178
|
+
npm run ios
|
|
179
|
+
\`\`\`
|
|
180
|
+
|
|
181
|
+
### For Android:
|
|
182
|
+
\`\`\`bash
|
|
183
|
+
npm run android
|
|
184
|
+
\`\`\`
|
|
185
|
+
|
|
186
|
+
The app will launch in the simulator/emulator. Tap the "Click to make a call" button to test the AWS SDK operation.
|
|
187
|
+
|
|
188
|
+
## Setting Up React Native Development Environment
|
|
189
|
+
|
|
190
|
+
If you haven't set up React Native before:
|
|
191
|
+
|
|
192
|
+
### iOS Development (macOS only)
|
|
193
|
+
|
|
194
|
+
1. Install Xcode from the Mac App Store
|
|
195
|
+
2. Install Xcode Command Line Tools:
|
|
196
|
+
\`\`\`bash
|
|
197
|
+
xcode-select --install
|
|
198
|
+
\`\`\`
|
|
199
|
+
3. Install CocoaPods:
|
|
200
|
+
\`\`\`bash
|
|
201
|
+
sudo gem install cocoapods
|
|
202
|
+
\`\`\`
|
|
203
|
+
|
|
204
|
+
### Android Development
|
|
205
|
+
|
|
206
|
+
1. Install Android Studio
|
|
207
|
+
2. Install Android SDK (API level 31 or higher)
|
|
208
|
+
3. Set up environment variables in \`~/.bash_profile\` or \`~/.zshrc\`:
|
|
209
|
+
\`\`\`bash
|
|
210
|
+
export ANDROID_HOME=$HOME/Library/Android/sdk
|
|
211
|
+
export PATH=$PATH:$ANDROID_HOME/emulator
|
|
212
|
+
export PATH=$PATH:$ANDROID_HOME/platform-tools
|
|
213
|
+
\`\`\`
|
|
214
|
+
|
|
215
|
+
For detailed setup instructions, see [React Native Environment Setup](https://reactnative.dev/docs/environment-setup)
|
|
216
|
+
|
|
217
|
+
## Common Errors and Solutions
|
|
218
|
+
|
|
219
|
+
### "Configuration Error: Please update REGION and IDENTITY_POOL_ID"
|
|
220
|
+
- You haven't updated the placeholders in \`App.js\`
|
|
221
|
+
- Update \`REGION\` and \`IDENTITY_POOL_ID\` with your actual values
|
|
222
|
+
|
|
223
|
+
### "NotAuthorizedException" or "AccessDeniedException"
|
|
224
|
+
- The IAM role doesn't have sufficient permissions
|
|
225
|
+
- Review and update the IAM policy attached to your Cognito unauthenticated role
|
|
226
|
+
- Ensure the policy includes the specific ${serviceName} actions you're testing
|
|
227
|
+
|
|
228
|
+
### "InvalidIdentityPoolConfigurationException"
|
|
229
|
+
- The Identity Pool ID or region is incorrect
|
|
230
|
+
- Verify the Identity Pool ID format: \`region:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\`
|
|
231
|
+
- Ensure the region matches where you created the Identity Pool
|
|
232
|
+
|
|
233
|
+
### Build Errors
|
|
234
|
+
|
|
235
|
+
**iOS:**
|
|
236
|
+
- Run \`cd ios && pod install && cd ..\` to install iOS dependencies
|
|
237
|
+
- Clean build folder in Xcode: Product > Clean Build Folder
|
|
238
|
+
- Delete \`ios/Pods\` and \`ios/Podfile.lock\`, then run \`pod install\` again
|
|
239
|
+
|
|
240
|
+
**Android:**
|
|
241
|
+
- Clean Gradle cache: \`cd android && ./gradlew clean && cd ..\`
|
|
242
|
+
- Ensure Android SDK is properly installed and environment variables are set
|
|
243
|
+
- Check that you have an Android emulator running or device connected
|
|
244
|
+
|
|
245
|
+
## Security Best Practices
|
|
246
|
+
|
|
247
|
+
- **Use unauthenticated access only for testing**
|
|
248
|
+
- **Implement authenticated access for production** using Cognito User Pools
|
|
249
|
+
- **Apply least-privilege IAM policies** - only grant necessary permissions
|
|
250
|
+
- **Monitor Cognito usage** in CloudWatch
|
|
251
|
+
- **Set up CloudTrail** to audit API calls
|
|
252
|
+
- **Consider using Cognito User Pools** for user authentication in production
|
|
253
|
+
|
|
254
|
+
## Reference Links
|
|
255
|
+
|
|
256
|
+
- [Create a Cognito Identity Pool](https://docs.aws.amazon.com/cognito/latest/developerguide/tutorial-create-identity-pool.html)
|
|
257
|
+
- [Cognito Identity Pools Documentation](https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-identity.html)
|
|
258
|
+
- [AWS SDK for JavaScript v3 - React Native](https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/getting-started-react-native.html)
|
|
259
|
+
- [React Native Environment Setup](https://reactnative.dev/docs/environment-setup)
|
|
260
|
+
- [IAM Policies for ${serviceName}](https://docs.aws.amazon.com/service-authorization/latest/reference/list_${serviceName.toLowerCase()}.html)
|
|
261
|
+
|
|
262
|
+
## Production Considerations
|
|
263
|
+
|
|
264
|
+
For production applications:
|
|
265
|
+
|
|
266
|
+
1. **Use Cognito User Pools** for user authentication
|
|
267
|
+
2. **Implement authenticated identities** instead of unauthenticated
|
|
268
|
+
3. **Use fine-grained IAM policies** based on user attributes
|
|
269
|
+
4. **Enable MFA** for sensitive operations
|
|
270
|
+
5. **Implement proper error handling** and user feedback
|
|
271
|
+
6. **Use environment variables** for configuration (not hardcoded values)
|
|
272
|
+
7. **Monitor and log** all authentication and authorization events
|
|
273
|
+
8. **Test on both iOS and Android** platforms
|
|
274
|
+
`;
|
|
275
|
+
|
|
276
|
+
fs.writeFileSync(join(actualProjectDir, "COGNITO_SETUP.md"), cognitoSetup);
|
|
277
|
+
|
|
278
|
+
console.log(`\nReact Native project created successfully at: ${actualProjectDir}`);
|
|
279
|
+
console.log(`\nNote: Project directory name was sanitized to "${projectName}" (alphanumeric only)`);
|
|
280
|
+
console.log("\nNext steps:");
|
|
281
|
+
console.log(`1. cd ${projectName}`);
|
|
282
|
+
console.log("2. npm install");
|
|
283
|
+
console.log("3. Update REGION and IDENTITY_POOL_ID in App.js");
|
|
284
|
+
console.log("4. cd ios && pod install && cd .. (macOS only)");
|
|
285
|
+
console.log("5. npm run ios (or npm run android)");
|
|
286
|
+
};
|