I've following stack which deploys the two constructs within. The construct DynamoDBConstruct exports the table name, and the construct IAMRoleConstruct consumes it. However, during deployment, it fails stating No export named dbTableName found, despite the fact that dependency is added/specified, the IAMRoleConstruct gets deployed first, why?
Stack:
public AllStacks(Construct scope, string id, IStackProps props = null) : base(scope, id, props)
{
var db = new DynamoDBConstruct(this, "DynamoDB");
var iam = new IAMRoleConstruct(this, "IAMRole");
iam.Node.AddDependency(db);
}
DynamoDBConstruct
public DynamoDBConstruct(Construct scope, string id): base(scope, id)
{
var dbTable = new Table(this, "dbTable", new TableProps()
{
PartitionKey = new Attribute
{
Name = "contactID",
Type = AttributeType.STRING
},
TableClass = TableClass.STANDARD,
TableName = (string)Node.TryGetContext("dbTableName"),
RemovalPolicy = RemovalPolicy.DESTROY
});
new CfnOutput(this, "OutputTableName", new CfnOutputProps()
{
ExportName = "dbTableName",
Value = dbTable.TableName
});
}
IAMRoleConstruct
public IAMRoleConstruct(Construct scope, string id) : base(scope, id)
{
var dbTableName = Fn.ImportValue("dbTableName");
/*
Some code
.
*/
}
With the disclaimer that I am not sure what language your code is in, I'm going to write in CDK's native language (which I recommend you to do as well) - Typescript.
The problem comes most likely from the fact that you are using the export within the same CDK/CFN stack. The export won't be available during stack creation, as that is part of the stack creation itself.
When you're working within a single stack, the simplest, most intuitive way of "moving data" from one construct to another is to just expose values through a public member of your class, e.g.:
class DynamoDBConstruct extends Construct {
public readonly tableName: string;
constructor(scope: Construct, id: string, props: Whatever) {
super(scope, id);
const table = new Table(this, 'Table', {
partitionKey: { name: 'id', type: AttributeType.STRING },
billingMode: BillingMode.PAY_PER_REQUEST,
// omitting table name on purpose - it will be generated by CDK
});
this.tableName = table.tableName;
}
}
Now inside your stack, you can simply use that table name:
class MyStack extends Stack {
constructor(scope: App, id: string, props: Whatever) {
const table = new DynamoDBConstruct(...);
const myOtherConstruct = new MyOtherConstruct(this, 'myOtherConstruct', {
// using table name here
tableName: table.tableName,
});
}
}
The reason for the error is that you are trying to produce and consume a Stack Output in the same stack. That won't work:
Docs: Output values are available after the stack operation is complete. Stack output values aren't available when a stack status is in any of the IN_PROGRESS status.
No worries! As #Victor says, there is a much easier alternative. Get rid of the Outputs. Instead, share data between your custom constructs by declaring public fields (e.g. public Table table) in the providing class, passing the references as props to the consuming class. This is what the CDK constructs do.
See the C# example stacks in the aws-cdk-examples repo.
Related
I'm building the infrastructure for an application using AWS-CDK.
I have a construct that builds multiple S3 buckets and another construct that creates a lambda function that fetches data from these buckets.
In order to be able to give my lambda permissions to fetch data from the bucket I need the buckets ARN.
Is there a way in which I could export the bucket arn from the construct that produces the buckets and import it into the lambda construct?
Sure, maybe something like this:
export class ConsumingStack extends cdk.Stack {
constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
const producingStack = new BucketProducingStack(this, 'BucketProducingStack');
const { bucket1, bucket2 } = producingStack;
//Create new lambda stack here
//const lambdaStack = new LambdaStack(this, { bucket1, bucket2} );
}
}
export class BucketProducingStack extends cdk.NestedStack {
bucket1: string;
bucket2: string;
constructor(scope: cdk.Construct, id: string, props?: cdk.NestedStackProps) {
const bucket1 = new Bucket(this, 'BucketOne');
const bucket2 = new Bucket(this, 'BucketTwo');
this.bucket1 = bucket1.bucketArn;
this.bucket2 = bucket2.bucketArn;
}
}
No guarantee this compiles as it was written entirely in this window, but hopefully conveys the idea.
If you are using python, you can add
#property
def main_source_bucket(self) -> _s3.IBucket:
return self.bucket
Reference it in your app stack like this ..
bucket = S3Construct(self, "bucket", "bucket1")
LambdaConstruct(self, "lambda1", "dev", bucket.main_source_bucket
I have an App that has two stacks, both within the same region/account. One of those stacks requires the ARN of a lambda that exists in the other stack. How do I reference this?
// within stackA constructor
public StackA(Construct scope, String id, StackProps props) {
SingletonFunction myLambda = SingletonFunction.Builder.create(this, "myLambda")
// some code here
.build()
CfnOutput myLambdaArn = CfnOutput.Builder.create(this, "myLambdaArn")
.exportName("myLambdaArn")
.description("ARN of the lambda that I want to use in StackB")
.value(myLambda.getFunctionArn())
.build();
}
App app = new App();
Stack stackA = new StackA(app, "stackA", someAProps);
Stack stackB = new StackB(app, "stackB", someBProps);
stackB.dependsOn(stackA);
How do pass the ARN into StackB?
CDK's official documentation has a complete example for sharing a S3 bucket between stacks. I copied it below for quicker reference.
/**
* Stack that defines the bucket
*/
class Producer extends cdk.Stack {
public readonly myBucket: s3.Bucket;
constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
super(scope, id, props);
const bucket = new s3.Bucket(this, 'MyBucket', {
removalPolicy: cdk.RemovalPolicy.DESTROY,
});
this.myBucket = bucket;
}
}
interface ConsumerProps extends cdk.StackProps {
userBucket: s3.IBucket;
}
/**
* Stack that consumes the bucket
*/
class Consumer extends cdk.Stack {
constructor(scope: cdk.App, id: string, props: ConsumerProps) {
super(scope, id, props);
const user = new iam.User(this, 'MyUser');
props.userBucket.grantReadWrite(user);
}
}
const producer = new Producer(app, 'ProducerStack');
new Consumer(app, 'ConsumerStack', { userBucket: producer.myBucket });
You can access resources in a different stack, as long as they are in the same account and AWS Region. The following example defines the stack stack1, which defines an Amazon S3 bucket. Then it defines a second stack, stack2, which takes the bucket from stack1 as a constructor property.
// Helper method to build an environment
static Environment makeEnv(String account, String region) {
return Environment.builder().account(account).region(region)
.build();
}
App app = new App();
Environment prod = makeEnv("123456789012", "us-east-1");
StackThatProvidesABucket stack1 = new StackThatProvidesABucket(app, "Stack1",
StackProps.builder().env(prod).build());
// stack2 will take an argument "bucket"
StackThatExpectsABucket stack2 = new StackThatExpectsABucket(app, "Stack,",
StackProps.builder().env(prod).build(), stack1.getBucket());
Option 1:
pass the data from Stack A to Stack B using the constructor :
You can extend cdk.stack and create a new class that will contain stackA.
In that stack, expose the relevant data you want by using public XXX: string\number (etc) ( See line 2 in the example).
Later, just pass this data into StackB constructor ( you can pass it using props as well).
Working code snippet:
Stack A:
export class StackA extends cdk.Stack {
public YourKey: KEY_TYPE;
constructor(scope: cdk.Construct, id: string, props: cdk.StackProps ) {
super(scope, id, props);
Code goes here...
// Output the key
new cdk.CfnOutput(this, 'KEY', { value: this.YourKey });
}
}
Stack B:
export class StackB extends cdk.Stack {
constructor(scope: cdk.Construct, id: string,importedKey: KEY_TYPE, props: cdk.props) {
super(scope, id, props)
Code goes here...
console.log(importedKey)
}
}
bin ts:
const importedKey = new StackA(app, 'id',props).YourKey;
new StackB(app, 'id',importedKey,props);
Option 2:
Sometimes it's just better to save this kind of stuff in the parameter store and read it from there.
More info here.
I found all of the answers to be on the right path, but none explained it fully and/or well. In this example, I'm passing a VPC from a VPC stack to an ECS cluster.
First, add a property to the originating stack. This property is set whenever the asset is created:
export class VpcStack extends cdk.Stack {
readonly vpc: Vpc;
constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// Here
this.vpc = new Vpc(this, 'vpc', {
maxAzs: 3,
cidr: '10.0.0.0/16',
});
});
}
Next, require this property as a parameter to the consuming stack:
// Create an interface that extends cdk.StackProps
// The VPC property is added here
interface EcsClusterStackProps extends cdk.StackProps {
vpc: Vpc,
}
export class EcsClusterStack extends cdk.Stack {
// Use your interface instead of the regular cdk.StackProps
constructor(scope: cdk.Construct, id: string, props: EcsClusterStackProps) {
super(scope, id, props);
// Use the passed-in VPC where you need it
new Cluster(this, "myCluster", {
capacity: {
instanceType: InstanceType.of(InstanceClass.M6I, InstanceSize.LARGE)
},
clusterName: "myCluster",
vpc: props.vpc, // Here
});
}
}
Third, pass the reference in your app file:
const app = new cdk.App();
// Create the VPC stack
const vpcStack = new VpcStack(app, 'vpc-stack', {
env: { account: process.env.CDK_DEFAULT_ACCOUNT, region: process.env.CDK_DEFAULT_REGION },
});
// Pass the VPC directly to the consuming stack's constructor
const ecsClusterStack = new EcsClusterStack(app, 'ecs-cluster-stack', {
vpc: vpcStack.vpc, // Here
});
Hopefully this helps clarify some of the ambiguous areas.
I have a TypeScript +2.4 project where I'm using Jest for my unit tests. The project has a lot of poco models, without a default value. For example:
export class Foo {
public id: number
public name: string;
public when: Date;
}
Each of these models is mapped from raw json to this class. It is a requirement for my tests that all properties are assigned, e.g. have values. This leads to the following test that has to be written for all models:
test('Foo() should have its properties assigned', () => {
const target: Foo = {
id: 1001, name: 'whatever', when: new Date()
};
// manually assert each propertie here
expect(target.id).toBeDefined();
expect(target.name).toBeDefined();
expect(target.when).toBeDefined();
}
To me, that's not so DRY to do for each test. Not to mention error prone and cumbersome. What I would like to do is create a helper that iterates through each property and asserts that it has a value assigned.
Example 1 - Object.keys
This example is incorrect because Object.keys only iterates through the already assigned properties, ignoring the non-set properties (and thus always is positive):
public static AssertAllPropertiesAreAssigned(target: object): void {
Object.keys(target).forEach((key, index) => {
expect(target[key]).toBeDefined();
});
Example 2 - Object.getOwnPropertyNames()
The same as example 1:
public static AssertAllPropertiesAreAssigned(target: object): void {
Object.getOwnPropertyNames(target).forEach((name, index) => {
expect(target[name]).toBeDefined();
});
Example 3 - Set default values
By assigning a default value to each poco, like null, I can make the earlier samples work. But I'd sure like to avoid that at all cost:
export class Foo {
public id: number = null;
public name: string = null;
public when: Date = null;
}
The question: is there a way to create a helper that asserts that each property of my TypeScript poco object is actually assigned a value, in my test? Or, as an alternative, does Jest have some util for this?
There are similar questions on SO, but they are not related to asserting the values in a test. This makes this question, as far as I've looked around, differ from the others:
How to dynamically assign value to class property in TypeScript
How to iterate through all properties and its values in Typescript class
Typescript looping trough class type properties
Also, I'm aware that the Javascript compiled output of my poco will probably leads to that the unset properties are simply not available:
var Foo = (function() {
// nothing here...
}());
But with TypeScript's strong typing power and recent changes and/or Jest helpers, there might be some additional options to get this done?
Most of your options aren't any better than the answers to those other questions: initialize the properties (good idea); use property decorators (tedious).
Personally, I think it should be an error to declare a class property as a can't-be-undefined type like string and then not define it in the constructor, but that feature isn't part of TypeScript yet, even if you turn on strictNullChecks (which you should). I don't know why you don't want to initialize the variables, but this would work:
export class Foo {
public id: number | undefined = void 0;
public name: string | undefined = void 0;
public when: Date | undefined = void 0;
}
Now an instance of Foo will have the relevant keys if you do Object.keys() even though the values will still be undefined.
Wait a minute, you're not even using the class at runtime:
const target: Foo = {
id: 1001, name: 'whatever', when: new Date()
}; // object literal, not constructed class instance
console.log(target instanceof Foo) // false
Then I suggest you use an interface instead of a class, and just turn on strictNullChecks:
export interface Foo {
id: number;
name: string;
when: Date;
}
const target: Foo = {
id: 1001, name: 'whatever', when: new Date()
};
const badTarget: Foo = {
id: 1002;
}; // error, Property 'name' is missing
Now TypeScript will not let you assign a possibly-undefined value to those properties and you don't have to bother looping over anything at runtime.
Hope that helps!
I have three objects:
class Customer: Object {
dynamic var solution: Solution!;
...
}
class Solution: Object {
dynamic var data: Data!;
...
}
class Data: Object {
...
}
Now i need to move the Data Object from Solution to Customer so that it becomes:
class Customer: Object {
dynamic var solution: Solution!;
dynamic var data: Data!;
...
}
I have no idea how I have to implement my Realm Migration method so that everything works fine and that I wont lose data.
I did some experiments with the Realm migrations sample app and came up with this potential solution:
In a migration block, you can only interact with your Realm file via the migration object. Any attempts to directly access the Realm file mid-migration will result in an exception.
That being said, it's possible to have nested calls to migration.enumerateObjects referencing different Realm model object classes. As such, it should simply be a matter of initially enumerating through the Customer objects, and in each iteration, enumerate through the Solution objects to find the corresponding one with the right data value. Once found, it should be possible to set the Customer object with the data from the Solution object.
Realm.Configuration.defaultConfiguration = Realm.Configuration(
schemaVersion: 1,
migrationBlock: { migration, oldSchemaVersion in
if (oldSchemaVersion < 1) {
migration.enumerateObjects(ofType: Customer.className()) { oldCustomerObject, newCustomerObject in
migration.enumerateObjects(ofType: Solution.className()) { oldSolutionObject, newSolutionObject in
//Check that the solution object is the one referenced by the customer
guard oldCustomerObject["solution"].isEqual(oldSolutionObject) else { return }
//Copy the data
newCustomerObject["data"] = oldSolutionObject["data"]
}
}
}
}
})
I feel I need to stress that this code is by no means tested and guaranteed to work in its present state. So I recommend you make sure you thoroughly test it on some dummy data you wouldn't miss beforehand. :)
Swift 4, Realm 3
I had to migrate a Realm object that linked to another object. I wanted to remove the explicit link and replace it with an object ID. TiM's solution got me most of the way there, and just needed a little refinement.
var config = Realm.Configuration()
config.migrationBlock = { migration, oldSchemaVersion in
if oldSchemaVersion < CURRENT_SCHEMA_VERSION {
// enumerate the first object type
migration.enumerateObjects(ofType: Message.className()) { (oldMsg, newMsg) in
// extract the linked object and cast from Any to DynamicObject
if let msgAcct = oldMsg?["account"] as? DynamicObject {
// enumerate the 2nd object type
migration.enumerateObjects(ofType: Account.className()) { (oldAcct, newAcct) in
if let oldAcct = oldAcct {
// compare the extracted object to the enumerated object
if msgAcct.isEqual(oldAcct) {
// success!
newMsg?["accountId"] = oldAcct["accountId"]
}
}
}
}
}
}
We can create Em.Object like this:
var foo = Em.Object.create({
somevar : '123'
});
and then use it:
foo.get('somevar');
but how to create a private property or method in Em.Object which would be accessable from the object but wouldnt be ourside?
There is indeed a way to have private variables in Ember objects, as follows:
MyObject = Ember.Object.extend({
init: function() {
// private variable
var a = 1;
// methods to get, set, or otherwise accesss the private variables
this.getA = function() {return a;};
this.setA = function(val) {a = val;}
// don't forget this!
this._super(...arguments);
}
});
now try
o1 = MyObject.create()
o2 = MyObject.create()
o1.setA(42);
o2.getA(); //1
In other words, you have to declare the private variables, AND any getters, setters, or other routines that want to use them, in the init hook. Of course, this means that those getters/setters will be present on each instance of the class, rather than in its prototype. That's slightly inefficient, but the same holds for any approach to private variables for classes in JavaScript.
It is conceivable that Ember could introduce a new private: {} hash on objects, but then Ember would need a lot of machinery to handle finding and controlling access to private variables across class hierarchies. It would be equivalent to redesigning or extending the language itself, which is not part of the Ember mission.
Meanwhile, the above approach works fine if the number of private instance variables is limited and the number of routines that need to access them is small. So the accepted answer that says this is impossible is, well, wrong.
It's not possible, since Ember.js does not provide any encapsulation mechanisms.
However, you can simply use some convention for private members. For example, prefix them with _ sign.
You could use a closure:
(function() {
var somePrivateProperty = 'xyz';
MyObject = Em.Object.extend({
someComputedProperty: function() {
return 'somePrivateProperty = ' + somePrivateProperty;
}).property()
})
})();
Is possible with a little trick:
var obj = Em.Em.Object.create(
new function(){
var privateVar = "this is private";
this.getPrivateVar = function(){
return privateVar ;
}
},
{
emberVar: "Ember var",
emberMethod : function(){
return this.getPrivateVar();
},
emberMethod1 : function(){
return privateVar ;
},
emberBinding : 'emberVar'
}
)
now if U try to get private var
obj.privateVar
> unknown
obj.getPrivateVar()
> "this is private"
obj.emberMethod()
> "this is private"
The only problem is that:
obj.emberMethod1()
> unknown