From 94b552fda8c492ac7fbc38c95a1a5e6ccd70120f Mon Sep 17 00:00:00 2001 From: Eric Shepherd Date: Tue, 10 Jun 2025 19:25:36 +0000 Subject: [PATCH 1/3] Add initial files for example --- .../example_code/glue/scenario/Package.swift | 48 +++++ .../glue/scenario/Sources/entry.swift | 193 ++++++++++++++++++ .../glue/scenario/flight_etl_job_script.py | 81 ++++++++ swift/example_code/glue/scenario/test.sh | 0 4 files changed, 322 insertions(+) create mode 100644 swift/example_code/glue/scenario/Package.swift create mode 100644 swift/example_code/glue/scenario/Sources/entry.swift create mode 100644 swift/example_code/glue/scenario/flight_etl_job_script.py create mode 100644 swift/example_code/glue/scenario/test.sh diff --git a/swift/example_code/glue/scenario/Package.swift b/swift/example_code/glue/scenario/Package.swift new file mode 100644 index 00000000000..afe7973fd9b --- /dev/null +++ b/swift/example_code/glue/scenario/Package.swift @@ -0,0 +1,48 @@ +// swift-tools-version: 5.9 +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +// +// (swift-tools-version has two lines here because it needs to be the first +// line in the file, but it should also appear in the snippet below) +// +// snippet-start:[swift.cognito-identity-provider.scenario.package] +// swift-tools-version: 5.9 +// +// The swift-tools-version declares the minimum version of Swift required to +// build this package. + +import PackageDescription + +let package = Package( + name: "glue-scenario", + // Let Xcode know the minimum Apple platforms supported. + platforms: [ + .macOS(.v13), + .iOS(.v15) + ], + dependencies: [ + // Dependencies declare other packages that this package depends on. + .package( + url: "https://github.com/awslabs/aws-sdk-swift", + from: "1.0.0"), + .package( + url: "https://github.com/apple/swift-argument-parser.git", + branch: "main" + ) + ], + targets: [ + // Targets are the basic building blocks of a package, defining a module or a test suite. + // Targets can depend on other targets in this package and products + // from dependencies. + .executableTarget( + name: "glue-scenario", + dependencies: [ + .product(name: "AWSGlue", package: "aws-sdk-swift"), + .product(name: "AWSS3", package: "aws-sdk-swift"), + .product(name: "ArgumentParser", package: "swift-argument-parser") + ], + path: "Sources") + + ] +) +// snippet-end:[swift.cognito-identity-provider.scenario.package] diff --git a/swift/example_code/glue/scenario/Sources/entry.swift b/swift/example_code/glue/scenario/Sources/entry.swift new file mode 100644 index 00000000000..ec228d75e1a --- /dev/null +++ b/swift/example_code/glue/scenario/Sources/entry.swift @@ -0,0 +1,193 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +// +// An example that shows how to use the AWS SDK for Swift to demonstrate +// creating and using crawlers and jobs using AWS Glue. +// +// 0. Upload the Python job script to Amazon S3 so it can be used when +// calling `startJobRun()` later. +// 1. Create a crawler, pass it the IAM role and the URL of the public Amazon +// S3 bucket that contains the source data: +// s3://crawler-public-us-east-1/flight/2016/csv. +// 2. Start the crawler. This takes time, so after starting it, use a loop +// that calls `getCrawler()` until the state is "READY". +// 3. Get the database created by the crawler, and the tables in the +// database. Display them to the user. +// 4. Create a job. Pass it the IAM role and the URL to a Python ETL script +// previously uploaded to the user's S3 bucket. +// 5. Start a job run, passing the following custom arguments. These are +// expected by the ETL script, so must exactly match. +// * `--input_database: ` +// * `--input_table: ` +// * `--output_bucket_url: ` +// 6. Loop and get the job run until it returns one of the following states: +// "SUCCEEDED", "STOPPED", "FAILED", or "TIMEOUT". +// 7. Output data is stored in a group of files in the user's S3 bucket. +// Either direct the user to their location or download a file and display +// the results inline. +// 8. List the jobs for the user's account. +// 9. Get job run details for a job run. +// 10. Delete the demo job. +// 11. Delete the database and tables created by the example. +// 12. Delete the crawler created by the example. + +import ArgumentParser +import AWSS3 +import Foundation + +// snippet-start:[swift.glue.import] +import AWSClientRuntime +import AWSGlue +// snippet-end:[swift.glue.import] + +struct ExampleCommand: ParsableCommand { + @Option(help: "The AWS IAM role to use for AWS Glue calls.") + var role: String + + @Option(help: "The Amazon S3 bucket to use for this example.") + var bucket: String + + @Option(help: "The Amazon S3 URL of the data to crawl.") + var s3url: String = "s3://crawler-public-us-east-1/flight/2016/csv" + + @Option(help: "The Python script to run as a job with AWS Glue.") + var script: String = "./flight_etl_job_script.py" + + @Option(help: "The AWS Region to run AWS API calls in.") + var awsRegion = "us-east-1" + + @Flag(help: "If this flag is set, output files will have the '.json' extension.") + var rename = false + + @Option(help: "A prefix string to use when naming tables.") + var tablePrefix = "swift-glue-basics-table" + + @Option( + help: ArgumentHelp("The level of logging for the Swift SDK to perform."), + completion: .list([ + "critical", + "debug", + "error", + "info", + "notice", + "trace", + "warning" + ]) + ) + var logLevel: String = "error" + + static var configuration = CommandConfiguration( + commandName: "glue-scenario", + abstract: """ + Demonstrates various features of AWS Glue. + """, + discussion: """ + An example showing how to use AWS Glue to create, run, and monitor + crawlers and jobs. + """ + ) + +/* + /// Prompt for an input string of at least a minimum length. + /// + /// - Parameters: + /// - prompt: The prompt string to display. + /// - minLength: The minimum number of characters to allow in the + /// response. Default value is 0. + /// + /// - Returns: The entered string. + func stringRequest(_ prompt: String, minLength: Int = 1) -> String { + while true { + print(prompt, terminator: "") + let str = readLine() + + guard let str else { + continue + } + if str.count >= minLength { + return str + } else { + print("*** Response must be at least \(minLength) character(s) long.") + } + } + } + + /// Ask a yes/no question. + /// + /// - Parameter prompt: A prompt string to print. + /// + /// - Returns: `true` if the user answered "Y", otherwise `false`. + func yesNoRequest(_ prompt: String) -> Bool { + while true { + let answer = stringRequest(prompt).lowercased() + if answer == "y" || answer == "n" { + return answer == "y" + } + } + } +*/ + + /// Generate and return a unique file name that begins with the specified + /// string. + /// + /// - Parameters: + /// - prefix: Text to use at the beginning of the returned name. + /// + /// - Returns: A string containing a unique filename that begins with the + /// specified `prefix`. + /// + /// The returned name uses a random number between 1 million and 1 billion to + /// provide reasonable certainty of uniqueness for the purposes of this + /// example. + func tempName(prefix: String) -> String { + return "\(prefix)-\(Int.random(in: 1000000..<1000000000))" + } + + /// Called by ``main()`` to run the bulk of the example. + func runAsync() async throws { + let config = try await GlueClient.GlueClientConfiguration(region: awsRegion) + let glueClient = GlueClient(config: config) + + print("Welcome to the AWS SDK for Swift basic scenario for AWS Glue!") + + // Create random names for things that need them. + + let crawlerName = tempName(prefix: "swift-glue-basics-crawler") + let databaseName = tempName(prefix: "swift-glue-basics-db") + + // A name for the AWS Glue job. + + let jobName = tempName(prefix: "scenario-job") + + // A name to give the Python script upon upload to the Amazon S3 + // bucket, and the full URL of the <