diff --git a/bubble-server/src/main/resources/models/defaults/cloudService.json b/bubble-server/src/main/resources/models/defaults/cloudService.json index 3fc53603..86c72024 100644 --- a/bubble-server/src/main/resources/models/defaults/cloudService.json +++ b/bubble-server/src/main/resources/models/defaults/cloudService.json @@ -271,10 +271,11 @@ "type": "compute", "driverClass": "bubble.cloud.compute.ec2.AmazonEC2Driver", "driverConfig": { - "regions": [{"name": "us-east-1", "description": "US East (N. Virginia)", - "location": {"country": "US", "lat": "37.926868", "lon": "-78.024902"}}], + "regions": [{ + "name": "us-east-1", "description": "US East (N. Virginia)", + "location": {"country": "US", "lat": "37.926868", "lon": "-78.024902"} + }], "config": [ - {"name": "region", "value": "US_EAST_1"}, {"name": "instanceType", "value": "{{INSTANCE_TYPE}}"}, {"name": "imageId", "value": "{{IMAGE_ID}}"}, {"name": "group", "value": "{{EC2_GROUP}}"} diff --git a/bubble-server/src/test/resources/models/system/cloudService.json b/bubble-server/src/test/resources/models/system/cloudService.json index 3de50e67..f5019223 100644 --- a/bubble-server/src/test/resources/models/system/cloudService.json +++ b/bubble-server/src/test/resources/models/system/cloudService.json @@ -1,300 +1,300 @@ [ { - "_subst": true, - "name": "S3Storage", - "type": "storage", - "driverClass": "bubble.cloud.storage.s3.S3StorageDriver", - "driverConfig": { - "region": "US_EAST_1", - "bucket": "{{BUBBLE_S3_BUCKET}}", - "prefix": "{{BUBBLE_S3_PREFIX}}", - "listFetchSize": 100 - }, - "credentials": { - "params": [ - {"name": "AWS_ACCESS_KEY_ID", "value": "{{AWS_ACCESS_KEY_ID}}"}, - {"name": "AWS_SECRET_KEY", "value": "{{AWS_SECRET_KEY}}"} - ] - }, - "template": true + "_subst": true, + "name": "S3Storage", + "type": "storage", + "driverClass": "bubble.cloud.storage.s3.S3StorageDriver", + "driverConfig": { + "region": "US_EAST_1", + "bucket": "{{BUBBLE_S3_BUCKET}}", + "prefix": "{{BUBBLE_S3_PREFIX}}", + "listFetchSize": 100 + }, + "credentials": { + "params": [ + {"name": "AWS_ACCESS_KEY_ID", "value": "{{AWS_ACCESS_KEY_ID}}"}, + {"name": "AWS_SECRET_KEY", "value": "{{AWS_SECRET_KEY}}"} + ] + }, + "template": true }, { - "_subst": true, - "name": "LocalStorage", - "type": "storage", - "driverClass": "bubble.cloud.storage.local.LocalStorageDriver", - "driverConfig": { "baseDir": "{{LOCALSTORAGE_BASE_DIR}}" }, - "template": false + "_subst": true, + "name": "LocalStorage", + "type": "storage", + "driverClass": "bubble.cloud.storage.local.LocalStorageDriver", + "driverConfig": { "baseDir": "{{LOCALSTORAGE_BASE_DIR}}" }, + "template": false }, { - "_subst": true, - "name": "GoogleGeoCoder", - "type": "geoCode", - "driverClass": "bubble.cloud.geoCode.google.GoogleGeoCodeDriver", - "credentials": { "params": [ {"name": "apiKey", "value": "{{GOOGLE_API_KEY}}"} ] }, - "template": true + "_subst": true, + "name": "GoogleGeoCoder", + "type": "geoCode", + "driverClass": "bubble.cloud.geoCode.google.GoogleGeoCodeDriver", + "credentials": { "params": [ {"name": "apiKey", "value": "{{GOOGLE_API_KEY}}"} ] }, + "template": true }, { - "_subst": true, - "name": "TOTPAuthenticator", - "type": "authenticator", - "driverClass": "bubble.cloud.authenticator.TOTPAuthenticatorDriver", - "template": true + "_subst": true, + "name": "TOTPAuthenticator", + "type": "authenticator", + "driverClass": "bubble.cloud.authenticator.TOTPAuthenticatorDriver", + "template": true }, { - "_subst": true, - "name": "SmtpServer", - "type": "email", - "driverClass": "{{BUBBLE_SMTP_DRIVER}}", - "driverConfig": { - "tlsEnabled": true - }, - "credentials": { - "params": [ - {"name": "user", "value": "{{BUBBLE_SMTP_USER}}"}, - {"name": "password", "value": "{{BUBBLE_SMTP_PASS}}"}, - {"name": "host", "value": "{{BUBBLE_SMTP_SERVER}}"}, - {"name": "port", "value": "{{BUBBLE_SMTP_PORT}}"} - ] - }, - "template": true + "_subst": true, + "name": "SmtpServer", + "type": "email", + "driverClass": "{{BUBBLE_SMTP_DRIVER}}", + "driverConfig": { + "tlsEnabled": true + }, + "credentials": { + "params": [ + {"name": "user", "value": "{{BUBBLE_SMTP_USER}}"}, + {"name": "password", "value": "{{BUBBLE_SMTP_PASS}}"}, + {"name": "host", "value": "{{BUBBLE_SMTP_SERVER}}"}, + {"name": "port", "value": "{{BUBBLE_SMTP_PORT}}"} + ] + }, + "template": true }, { - "_subst": true, - "name": "TwilioSms", - "type": "sms", - "driverClass": "{{BUBBLE_SMS_DRIVER}}", - "driverConfig": {}, - "credentials": { - "params": [ - {"name": "accountSID", "value": "{{TWILIO_ACCOUNT_SID}}"}, - {"name": "authToken", "value": "{{TWILIO_AUTH_TOKEN}}"}, - {"name": "fromPhoneNumber", "value": "{{TWILIO_FROM_PHONE_NUMBER}}"} - ] - }, - "template": true + "_subst": true, + "name": "TwilioSms", + "type": "sms", + "driverClass": "{{BUBBLE_SMS_DRIVER}}", + "driverConfig": {}, + "credentials": { + "params": [ + {"name": "accountSID", "value": "{{TWILIO_ACCOUNT_SID}}"}, + {"name": "authToken", "value": "{{TWILIO_AUTH_TOKEN}}"}, + {"name": "fromPhoneNumber", "value": "{{TWILIO_FROM_PHONE_NUMBER}}"} + ] + }, + "template": true }, { - "_subst": true, - "name": "GoogleGeoTime", - "type": "geoTime", - "driverClass": "bubble.cloud.geoTime.google.GoogleGeoTimeDriver", - "credentials": { "params": [ {"name": "apiKey", "value": "{{GOOGLE_API_KEY}}"} ] }, - "template": true + "_subst": true, + "name": "GoogleGeoTime", + "type": "geoTime", + "driverClass": "bubble.cloud.geoTime.google.GoogleGeoTimeDriver", + "credentials": { "params": [ {"name": "apiKey", "value": "{{GOOGLE_API_KEY}}"} ] }, + "template": true }, { - "_subst": true, - "name": "VultrCompute", - "type": "compute", - "driverClass": "bubble.cloud.compute.vultr.VultrDriver", - "driverConfig": { - "regions": [{ - "name": "Vultr - Dallas", - "internalName": "Dallas", - "location": {"city": "Dallas", "country": "US", "region": "TX", "lat": "32.779167", "lon": "-96.808889"} - }, { - "name": "Vultr - Los Angeles", - "internalName": "Los Angeles", - "location": {"city": "Los Angeles", "country": "US", "region": "CA", "lat": "34.05", "lon": "-118.25"} - }, { - "name": "Vultr - Miami", - "internalName": "Miami", - "location": {"city": "Miami", "country": "US", "region": "FL", "lat": "25.775278", "lon": "-80.208889"} - }, { - "name": "Vultr - Seattle", - "internalName": "Seattle", - "location": {"city": "Seattle", "country": "US", "region": "WA", "lat": "47.609722", "lon": "-122.333056"} - }, { - "name": "Vultr - New Jersey", - "internalName": "New Jersey", - "location": {"city": "Newark", "country": "US", "region": "NJ", "lat": "40.72", "lon": "-74.17"} - }, { - "name": "Vultr - Atlanta", - "internalName": "Atlanta", - "location": {"city": "Atlanta", "country": "US", "region": "GA", "lat": "33.755", "lon": "-84.39"} - }, { - "name": "Vultr - Chicago", - "internalName": "Chicago", - "location": {"city": "Chicago", "country": "US", "region": "IL", "lat": "41.881944", "lon": "-87.627778"} - }, { - "name": "Vultr - San Jose", - "internalName": "Silicon Valley", - "location": {"city": "San Jose", "country": "US", "region": "CA", "lat": "37.333333", "lon": "-121.9"} - }, { - "name": "Vultr - Toronto", - "internalName": "Toronto", - "location": {"city": "Toronto", "country": "CA", "region": "ON", "lat": "43.741667", "lon": "-79.373333"} - }, { - "name": "Vultr - London", - "internalName": "London", - "location": {"city": "London", "country": "GB", "region": "London", "lat": "51.507222", "lon": "-0.1275"} - }, { - "name": "Vultr - Paris", - "internalName": "Paris", - "location": {"city": "Paris", "country": "FR", "region": "Ile-de-Paris", "lat": "48.8567", "lon": "2.3508"} - }, { - "name": "Vultr - Frankfurt", - "internalName": "Frankfurt", - "location": {"city": "Frankfurt", "country": "DE", "region": "Hesse", "lat": "50.116667", "lon": "8.683333"} - }, { - "name": "Vultr - Singapore", - "internalName": "Singapore", - "location": {"city": "Singapore", "country": "SG", "region": "Singapore", "lat": "1.283333", "lon": "103.833333"} - }, { - "name": "Vultr - Tokyo", - "internalName": "Tokyo", - "location": {"city": "Tokyo", "country": "JP", "region": "Kantō", "lat": "35.689722", "lon": "139.692222"} - }, { - "name": "Vultr - Sydney", - "internalName": "Sydney", - "location": {"city": "Sydney", "country": "AU", "region": "NSW", "lat": "-33.865", "lon": "151.209444"} - }, { - "name": "Vultr - Amsterdam", - "internalName": "ams3", - "location": {"city": "Amsterdam", "country": "NL", "region": "North Holland", "lat": "52.366667", "lon": "4.9"} - }], - "sizes": [ - {"name": "small", "type": "small", "internalName": "1024 MB RAM,25 GB SSD,1.00 TB BW", "vcpu": 1, "memoryMB": 1024, "ssdGB": 25}, - {"name": "medium", "type": "medium", "internalName": "2048 MB RAM,55 GB SSD,2.00 TB BW", "vcpu": 1, "memoryMB": 2048, "ssdGB": 55}, - {"name": "large", "type": "large", "internalName": "4096 MB RAM,80 GB SSD,3.00 TB BW", "vcpu": 2, "memoryMB": 4096, "ssdGB": 80} - ], - "config": [{"name": "os", "value": "Ubuntu 18.04 x64"}] - }, - "credentials": { - "params": [ - {"name": "API-Key", "value": "{{VULTR_API_KEY}}"} - ] - }, - "template": true + "_subst": true, + "name": "VultrCompute", + "type": "compute", + "driverClass": "bubble.cloud.compute.vultr.VultrDriver", + "driverConfig": { + "regions": [{ + "name": "Vultr - Dallas", + "internalName": "Dallas", + "location": {"city": "Dallas", "country": "US", "region": "TX", "lat": "32.779167", "lon": "-96.808889"} + }, { + "name": "Vultr - Los Angeles", + "internalName": "Los Angeles", + "location": {"city": "Los Angeles", "country": "US", "region": "CA", "lat": "34.05", "lon": "-118.25"} + }, { + "name": "Vultr - Miami", + "internalName": "Miami", + "location": {"city": "Miami", "country": "US", "region": "FL", "lat": "25.775278", "lon": "-80.208889"} + }, { + "name": "Vultr - Seattle", + "internalName": "Seattle", + "location": {"city": "Seattle", "country": "US", "region": "WA", "lat": "47.609722", "lon": "-122.333056"} + }, { + "name": "Vultr - New Jersey", + "internalName": "New Jersey", + "location": {"city": "Newark", "country": "US", "region": "NJ", "lat": "40.72", "lon": "-74.17"} + }, { + "name": "Vultr - Atlanta", + "internalName": "Atlanta", + "location": {"city": "Atlanta", "country": "US", "region": "GA", "lat": "33.755", "lon": "-84.39"} + }, { + "name": "Vultr - Chicago", + "internalName": "Chicago", + "location": {"city": "Chicago", "country": "US", "region": "IL", "lat": "41.881944", "lon": "-87.627778"} + }, { + "name": "Vultr - San Jose", + "internalName": "Silicon Valley", + "location": {"city": "San Jose", "country": "US", "region": "CA", "lat": "37.333333", "lon": "-121.9"} + }, { + "name": "Vultr - Toronto", + "internalName": "Toronto", + "location": {"city": "Toronto", "country": "CA", "region": "ON", "lat": "43.741667", "lon": "-79.373333"} + }, { + "name": "Vultr - London", + "internalName": "London", + "location": {"city": "London", "country": "GB", "region": "London", "lat": "51.507222", "lon": "-0.1275"} + }, { + "name": "Vultr - Paris", + "internalName": "Paris", + "location": {"city": "Paris", "country": "FR", "region": "Ile-de-Paris", "lat": "48.8567", "lon": "2.3508"} + }, { + "name": "Vultr - Frankfurt", + "internalName": "Frankfurt", + "location": {"city": "Frankfurt", "country": "DE", "region": "Hesse", "lat": "50.116667", "lon": "8.683333"} + }, { + "name": "Vultr - Singapore", + "internalName": "Singapore", + "location": {"city": "Singapore", "country": "SG", "region": "Singapore", "lat": "1.283333", "lon": "103.833333"} + }, { + "name": "Vultr - Tokyo", + "internalName": "Tokyo", + "location": {"city": "Tokyo", "country": "JP", "region": "Kantō", "lat": "35.689722", "lon": "139.692222"} + }, { + "name": "Vultr - Sydney", + "internalName": "Sydney", + "location": {"city": "Sydney", "country": "AU", "region": "NSW", "lat": "-33.865", "lon": "151.209444"} + }, { + "name": "Vultr - Amsterdam", + "internalName": "ams3", + "location": {"city": "Amsterdam", "country": "NL", "region": "North Holland", "lat": "52.366667", "lon": "4.9"} + }], + "sizes": [ + {"name": "small", "type": "small", "internalName": "1024 MB RAM,25 GB SSD,1.00 TB BW", "vcpu": 1, "memoryMB": 1024, "ssdGB": 25}, + {"name": "medium", "type": "medium", "internalName": "2048 MB RAM,55 GB SSD,2.00 TB BW", "vcpu": 1, "memoryMB": 2048, "ssdGB": 55}, + {"name": "large", "type": "large", "internalName": "4096 MB RAM,80 GB SSD,3.00 TB BW", "vcpu": 2, "memoryMB": 4096, "ssdGB": 80} + ], + "config": [{"name": "os", "value": "Ubuntu 18.04 x64"}] + }, + "credentials": { + "params": [ + {"name": "API-Key", "value": "{{VULTR_API_KEY}}"} + ] + }, + "template": true }, { - "_subst": true, - "name": "DigitalOceanCompute", - "type": "compute", - "driverClass": "bubble.cloud.compute.digitalocean.DigitalOceanDriver", - "driverConfig": { - "regions": [{ - "name": "DigitalOcean - New York City 1", - "internalName": "nyc1", - "location": {"city": "New York", "country": "US", "region": "NY", "lat": "40.661", "lon": "-73.944"} - },{ - "name": "DigitalOcean - New York City 3", - "internalName": "nyc3", - "location": {"city": "New York", "country": "US", "region": "NY", "lat": "40.661", "lon": "-73.944"} - }, { - "name": "DigitalOcean - Singapore 1", - "internalName": "sgp1", - "location": {"city": "Singapore", "country": "SG", "region": "Singapore", "lat": "1.283333", "lon": "103.833333"} - }, { - "name": "DigitalOcean - London 1", - "internalName": "lon1", - "location": {"city": "London", "country": "GB", "region": "London", "lat": "51.507222", "lon": "-0.1275"} - }, { - "name": "DigitalOcean - Amsterdam 3", - "internalName": "ams3", - "location": {"city": "Amsterdam", "country": "NL", "region": "North Holland", "lat": "52.366667", "lon": "4.9"} - }, { - "name": "DigitalOcean - Frankfurt 1", - "internalName": "fra1", - "location": {"city": "Frankfurt", "country": "DE", "region": "Hesse", "lat": "50.116667", "lon": "8.683333"} - }, { - "name": "DigitalOcean - Toronto 1", - "internalName": "tor1", - "location": {"city": "Toronto", "country": "CA", "region": "ON", "lat": "43.741667", "lon": "-79.373333"} - }, { - "name": "DigitalOcean - San Francisco 2", - "internalName": "sfo2", - "location": {"city": "San Francisco", "country": "US", "region": "CA", "lat": "37.783333", "lon": "-122.416667"} - }, { - "name": "DigitalOcean - Bangalore 1", - "internalName": "blr1", - "location": {"city": "Bangalore", "country": "IN", "region": "Karnataka", "lat": "12.983333", "lon": "77.583333"} - }], - "sizes": [ - {"name": "small", "type": "small", "internalName": "s-1vcpu-1gb", "vcpu": 1, "memoryMB": 1024, "ssdGB": 25}, - {"name": "medium", "type": "medium", "internalName": "s-1vcpu-2gb", "vcpu": 1, "memoryMB": 2048, "ssdGB": 50}, - {"name": "large", "type": "large", "internalName": "s-2vcpu-4gb", "vcpu": 2, "memoryMB": 4096, "ssdGB": 80} - ], - "config": [{"name": "os", "value": "ubuntu-18-04-x64"}] - }, - "credentials": { - "params": [ - {"name": "apiKey", "value": "{{DIGITALOCEAN_API_KEY}}"} - ] - }, - "template": true + "_subst": true, + "name": "DigitalOceanCompute", + "type": "compute", + "driverClass": "bubble.cloud.compute.digitalocean.DigitalOceanDriver", + "driverConfig": { + "regions": [{ + "name": "DigitalOcean - New York City 1", + "internalName": "nyc1", + "location": {"city": "New York", "country": "US", "region": "NY", "lat": "40.661", "lon": "-73.944"} + },{ + "name": "DigitalOcean - New York City 3", + "internalName": "nyc3", + "location": {"city": "New York", "country": "US", "region": "NY", "lat": "40.661", "lon": "-73.944"} + }, { + "name": "DigitalOcean - Singapore 1", + "internalName": "sgp1", + "location": {"city": "Singapore", "country": "SG", "region": "Singapore", "lat": "1.283333", "lon": "103.833333"} + }, { + "name": "DigitalOcean - London 1", + "internalName": "lon1", + "location": {"city": "London", "country": "GB", "region": "London", "lat": "51.507222", "lon": "-0.1275"} + }, { + "name": "DigitalOcean - Amsterdam 3", + "internalName": "ams3", + "location": {"city": "Amsterdam", "country": "NL", "region": "North Holland", "lat": "52.366667", "lon": "4.9"} + }, { + "name": "DigitalOcean - Frankfurt 1", + "internalName": "fra1", + "location": {"city": "Frankfurt", "country": "DE", "region": "Hesse", "lat": "50.116667", "lon": "8.683333"} + }, { + "name": "DigitalOcean - Toronto 1", + "internalName": "tor1", + "location": {"city": "Toronto", "country": "CA", "region": "ON", "lat": "43.741667", "lon": "-79.373333"} + }, { + "name": "DigitalOcean - San Francisco 2", + "internalName": "sfo2", + "location": {"city": "San Francisco", "country": "US", "region": "CA", "lat": "37.783333", "lon": "-122.416667"} + }, { + "name": "DigitalOcean - Bangalore 1", + "internalName": "blr1", + "location": {"city": "Bangalore", "country": "IN", "region": "Karnataka", "lat": "12.983333", "lon": "77.583333"} + }], + "sizes": [ + {"name": "small", "type": "small", "internalName": "s-1vcpu-1gb", "vcpu": 1, "memoryMB": 1024, "ssdGB": 25}, + {"name": "medium", "type": "medium", "internalName": "s-1vcpu-2gb", "vcpu": 1, "memoryMB": 2048, "ssdGB": 50}, + {"name": "large", "type": "large", "internalName": "s-2vcpu-4gb", "vcpu": 2, "memoryMB": 4096, "ssdGB": 80} + ], + "config": [{"name": "os", "value": "ubuntu-18-04-x64"}] + }, + "credentials": { + "params": [ + {"name": "apiKey", "value": "{{DIGITALOCEAN_API_KEY}}"} + ] + }, + "template": true }, { - "_subst": true, - "name": "AmazonEC2Driver", - "type": "compute", - "driverClass": "bubble.cloud.compute.ec2.AmazonEC2Driver", - "driverConfig": { - "regions": - [{"name": "us-east-1", "description": "US East (N. Virginia)", - "location": {"country": "US", "lat": "37.926868", "lon": "-78.024902"}}], - "config": - [{"name": "region", "value": "US_EAST_1"}, - {"name": "instanceType", "value": "t2.micro"}, - {"name": "imageId", "value": "ami-0080e4c5bc078760e"}, - {"name": "group", "value": "sg-id"}] - }, - "credentials": { - "params": [ - {"name": "AWS_ACCESS_KEY_ID", "value": "{{AWS_ACCESS_KEY_ID}}"}, - {"name": "AWS_SECRET_KEY", "value": "{{AWS_SECRET_KEY}}"} - ] - }, - "template": true + "_subst": true, + "name": "AmazonEC2Driver", + "type": "compute", + "driverClass": "bubble.cloud.compute.ec2.AmazonEC2Driver", + "driverConfig": { + "regions": [{ + "name": "us-east-1", "description": "US East (N. Virginia)", + "location": {"country": "US", "lat": "37.926868", "lon": "-78.024902"} + }], + "config": [ + {"name": "instanceType", "value": "t2.micro"}, + {"name": "imageId", "value": "ami-0080e4c5bc078760e"}, + {"name": "group", "value": "sg-id"} + ]}, + "credentials": { + "params": [ + {"name": "AWS_ACCESS_KEY_ID", "value": "{{AWS_ACCESS_KEY_ID}}"}, + {"name": "AWS_SECRET_KEY", "value": "{{AWS_SECRET_KEY}}"} + ] + }, + "template": true }, { - "_subst": true, - "name": "InviteCode", - "type": "payment", - "priority": 200, - "driverClass": "bubble.cloud.payment.code.CodePaymentDriver", - "driverConfig": {}, - "credentials": {}, - "template": true + "_subst": true, + "name": "InviteCode", + "type": "payment", + "priority": 200, + "driverClass": "bubble.cloud.payment.code.CodePaymentDriver", + "driverConfig": {}, + "credentials": {}, + "template": true }, { - "_subst": true, - "name": "GoDaddyDns", - "type": "dns", - "driverClass": "bubble.cloud.dns.godaddy.GoDaddyDnsDriver", - "driverConfig": {}, - "credentials": { - "params": [ - {"name": "GODADDY_API_KEY", "value": "{{GODADDY_API_KEY}}"}, - {"name": "GODADDY_API_SECRET", "value": "{{GODADDY_API_SECRET}}"} - ] - }, - "template": true + "_subst": true, + "name": "GoDaddyDns", + "type": "dns", + "driverClass": "bubble.cloud.dns.godaddy.GoDaddyDnsDriver", + "driverConfig": {}, + "credentials": { + "params": [ + {"name": "GODADDY_API_KEY", "value": "{{GODADDY_API_KEY}}"}, + {"name": "GODADDY_API_SECRET", "value": "{{GODADDY_API_SECRET}}"} + ] + }, + "template": true }, { - "_subst": true, - "name": "Route53Dns", - "type": "dns", - "driverClass": "bubble.cloud.dns.route53.Route53DnsDriver", - "driverConfig": {}, - "credentials": { - "params": [ - {"name": "AWS_ACCESS_KEY_ID", "value": "{{AWS_ACCESS_KEY_ID}}"}, - {"name": "AWS_SECRET_KEY", "value": "{{AWS_SECRET_KEY}}"} - ] - }, - "template": true + "_subst": true, + "name": "Route53Dns", + "type": "dns", + "driverClass": "bubble.cloud.dns.route53.Route53DnsDriver", + "driverConfig": {}, + "credentials": { + "params": [ + {"name": "AWS_ACCESS_KEY_ID", "value": "{{AWS_ACCESS_KEY_ID}}"}, + {"name": "AWS_SECRET_KEY", "value": "{{AWS_SECRET_KEY}}"} + ] + }, + "template": true } ] \ No newline at end of file