How does AWS CDK "know" which constructs have been created inside a class - aws-cdk

I am seeing AWS example CDK code from https://docs.aws.amazon.com/cdk/v2/guide/hello_world.html that looks like this
import * as cdk from 'aws-cdk-lib';
import { aws_s3 as s3 } from 'aws-cdk-lib';
export class HelloCdkStack extends cdk.Stack {
constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
super(scope, id, props);
new s3.Bucket(this, 'MyFirstBucket', {
versioned: true
});
}
}
And CDK synthesis tool somehow knows that this code creates a S3 bucket in the HelloCDK stack. Coming from Java, I have not seen this "Get all classes instantiated with a classes constructor and do something with them", especially because this code
new s3.Bucket(this, 'MyFirstBucket', {
versioned: true
});
Reads to me as a class instance with no pointer, and thus something that would get garbage collected.
How is AWS CDK using this info? Is this a pattern specific to typescript?

Anytime you create a construct in the CDK you have to provide it a scope as the first parameter. In your example, you have provided 'this' as the scope of the bucket. 'this', the stack, becomes the parent of the bucket in a tree the constructs maintain. This is the only internal pointer you have and need to any constructs that get created. Because there is this one reference then there's no garbage to clean up.

Without knowing too much about the details but there is a pointer in the arguments. The first argument attaches the construct to the CDK tree internally build cdk.out/tree.json .
Perhaps a bit like recursion. You can do recursion in Java.

Related

Service Dependencies in NestJS

I have many endpoints in my app:
/Route1
/Route2
...
/Route99
In a number of these routes, there is some common functionality such as getting specific data from one source such as a local file, or another resource such as a No SQL database or external HTTP endpoint. My problem is that these services need to have a service dependency themselves, and I am not sure that how I have currently done it is the best way to do it in NestJS.
Route1Service - Read a file of data, and return it. This uses the FileSystemService() to wrap all the error handling, different data types, path checking etc., of the NodeJS fs module. The Route1Service then returns this to the Route1Controller
#Injectable()
export class Route1Service {
private FS_:FileSystemService; // defined here instead of constructor, as I do not know how to set it in the constructor via NestJS, or if this is even the best way.
// constructor(private FS_: FileSystemService) { }
// Since I do not set it in the constructor
public DataServiceDI(FsService:FileSystemService):void {
this.FS_ = FsService;
}
public GetData(): string {
const Data:string = this.FS_.ReadLocalFile('a.txt');
return Data;
}
}
Route99Service might do the same thing, but with a different file (b.txt)
#Injectable()
export class Route99Service {
private FS_:FileSystemService;
public DataServiceDI(FsService:FileSystemService):void {
this.FS_ = FsService;
}
public GetData(): string {
const Data:string = this.FS_.ReadLocalFile('b.txt');
return Data;
}
}
This is a contrived example to illustrate my issue. Obviously a basic RouteService could be used, and pass the file name, but I am trying to illustrate the dependent service. I do not know how to define the module(s) to use this dependent service or if I should be doing it this way.
What I have been doing for my definition:
#Module({
controllers: [Route1Controller],
providers: [Route1Service, FileSystemService],
})
export class Route1Module {}
The controller than has the constructor with both Services:
#Controller('route1')
export class Route1Controller
constructor(
private Route1_: Route1Service,
private FsSystem_: FileSystemService
) { }
Now that my controller has the FsSystem service as a separate entity, I need to add a method on my Route1Service, DataServiceDI(), to allow me to pass the FileSystemService as a reference. Then my service can use this service to access the file system.
My question comes down to, is this the best practice for this sort of thing? Ultimately, in my code, these services (FileSystemService, NoSqlService) extend a common service type, so that all my services can have this DataServiceDI() in then (they extend a base service with this definition).
Is this the best approach for longer term maintainability? Is there an easier way to simply inject the proper service into my Route1Service so it is injected by NestJS, and I do not have to do the DI each time?
The current method works for me to be able to simply test the service, since I can easily mock the FileSystemServie, NoSqlService, etc., and then inject the mock.

cdk call CfnBucket (first level) from IBucket refernce

my objective is to call a first level cdk command starting from an interface: IBucket.
I can get the bucket reference starting from this:
const sourceBucket = props.glueTable.bucket;
Afterwords, I need to call:
cfnBucket.replicationConfiguration = {
The procedure is exactly as for the script below:
https://github.com/rogerchi/cdk-s3-bucketreplication/blob/main/src/index.ts
But, as you can see, this script requires:
readonly sourceBucket: s3.Bucket;
Since it is needed to call:
const sourceAccount = cdk.Stack.of(props.sourceBucket).account;
Finally, are there really no other ways to call a cloudformation level 1 command starting from a reference?
It looks odd.
Thank you in advance
Marco
There is an example on exactly this in the aws docs:
If a Construct is missing a feature or you are trying to work around an issue, you can modify the CFN Resource that is encapsulated by the Construct.
All Constructs contain within them the corresponding CFN Resource. For example, the high-level Bucket construct wraps the low-level CfnBucket construct. Because the CfnBucket corresponds directly to the AWS CloudFormation resource, it exposes all features that are available through AWS CloudFormation.
The basic approach to get access to the CFN Resource class is to use construct.node.defaultChild (Python: default_child), cast it to the right type (if necessary), and modify its properties. Again, let's take the example of a Bucket.
// Get the CloudFormation resource
const cfnBucket = bucket.node.defaultChild;
// Change its properties
cfnBucket.analyticsConfiguration = [
{
id: 'Config'
// ...
}
];
From https://docs.aws.amazon.com/cdk/latest/guide/cfn_layer.html
For you, it wouldn't be analyticsconfiguration but bucketreplication of course.

Grails Data Service Cannot Use Regular Service

Happy Another Covid Day. When I use generate-all, Grails creates the Data Service for me. I begin to understand what a data service is.
I also have my own service for my Author and Book classes to use. I name my service ImportService. I have methods in the ImportService to clean up my book data read from a CSV file before the Data Service saves my books to the database. I also follow the instruction to make the Data Service an Abstract Class. So, I can put my own method in the Data Service.
Since the Author has its own AuthorService, and the Book has its own BookService, I want the different Data Service to access the method in my ImportService. So, I don't have to copy and paste the import CSV code multiple times. So, I put the line ImportService importService in the AuthorServie class and the BookService class. That does not go well. importService is always NULL inside the Data Service classes. I google the problem. They say I cannot inject another service to the grails.gorm.services.Service.
There is a post that says to make a bean. I am new to Grails. I have no idea what they are talking about even with the codes posted. Part of my background is Assembly Language, C, and Pascal. My head is filled with lingo like Top Down, Subroutine, library, Address, and Pointer. I have no idea what a Bean is.
This is what it is. I am wondering whether this is a bug or by design that you cannot inject a service to the gorm service.
Thanks for your "Pointer".
See the project at https://github.com/jeffbrown/tom6502servicedi. That project uses Grails 4.0.3 and GORM 7.0.7.
https://github.com/jeffbrown/tom6502servicedi/blob/main/grails-app/services/tom6502servicedi/ImportService.groovy
package tom6502servicedi
class ImportService {
int getSomeNumber() {
42
}
}
https://github.com/jeffbrown/tom6502servicedi/blob/917c51ee173e7bb6844ca7d40ced5afbb8d9063f/grails-app/services/tom6502servicedi/AuthorService.groovy
package tom6502servicedi
import grails.gorm.services.Service
import org.springframework.beans.factory.annotation.Autowired
#Service(Author)
abstract class AuthorService {
#Autowired
ImportService importService
// ...
int getSomeNumberFromImportService() {
importService.someNumber
}
}
https://github.com/jeffbrown/tom6502servicedi/blob/917c51ee173e7bb6844ca7d40ced5afbb8d9063f/grails-app/controllers/tom6502servicedi/AuthorController.groovy
package tom6502servicedi
import grails.validation.ValidationException
import static org.springframework.http.HttpStatus.*
class AuthorController {
AuthorService authorService
// ...
def someNumber() {
render "The Number Is ${authorService.someNumberFromImportService}"
}
}
Sending a request to that someNumber action will verify that the ImportService is injected into the AuthorService and the AuthorService is injected into the AuthorController.
$ curl http://localhost:8080/author/someNumber
The Number Is 42

Possible to manual inject NgZone?

I'm trying to use Injector to inject my component/service but one of them requires NgZone as its dependency. From https://angular.io/api/core/Injector
export MyComponent{
constructor() {
const injector = Injector.create({
providers: [
{ provide: NgZone, deps: [ ] },
{ provide: MyService, deps: [ NgZone ] }
]
});
this.myService = injector.get(MyService);
}
}
Then in child class:
export MyOtherComponent extends MyComponent {
constructor() {
super();
}
public helloWorld() {
this.myService.stuff();
}
}
But I'm getting the following error:
ERROR Error: StaticInjectorError[MyService -> NgZone]:
NullInjectorError: No provider for NgZone!
at NullInjector.get (core.js:8896)
I tried with a dummy service that don't have anything in the constructor, and it worked.
Is there a way to provide NgZone manually through the deps like that?
Is there another way to get the "global" NgZone object (there should only be 1 instance of NgZone running right?)
MyService is also a downgraded service and is being used in both AngularJS and Angular7, not sure if that changes anything.
Edit: Reason I'm trying to do this, is because MyComponent is a component base class that will get extends upon and have many child class extending on that. If I could do it like this by manually injecting it internally, then I don't need to pass all those dependencies from the children. Imagine I have 6-7 dependencies and 30+ childrens, and lets say I need some new dependencies, I'd have to update every single one of them...
You could inject injector — it would be a single dependency. And all your children could then get what they need from this injector. Yes, you would need to provide that injector through your inheritance chain of super() calls, but at least it would be just one thing.
There's also this:
https://github.com/angular/angular/issues/16566#issuecomment-338188342
This comment states it is possible to use DI in abstract classes if you decorate them. As for NgZone instance — yes, I believe there must be only one and I also tried to get a hold of it once but couldn't come up with an elegant solution.
Guess I was brain dead last night. This morning after digging deeper, I think I've found a way to grab the global Zone and it seems to work (it triggered the change detection).
Since #waterplea also have the assumption that there should only be 1 instance of the NgZone, I decided to just look around in console and what do you know.
Then I tried to just pass the global Zone to it like this:
{ provide: NgZone, useValue: Zone },
And it gave me the error that this.ngZone.run is undefined. OK... digging deeper, oh hey, there is a root object in Zone and hey, look, a run function!
So I went and updated the code to this and it worked.
{ provide: NgZone, useValue: Zone.root },

Provide new instances of model class for dependency injection in Angular 2

I have a class that serves as a model for some data I get from a server. This data starts as an unwieldy xml object where text nodes have attributes so the json format I convert it into does not have simple string values. Instead I have:
#Injectable()
export class FooString {
_attr: string;
value: string;
isReadOnly(): boolean {
return this._attr && this._attr === 'ReadOnly';
}
isHidden(): boolean {
return this._attr && this._attr === 'Hid';
}
}
Then my model is like:
#Injectable()
export class Payment {
constructor(
public FooId: FooString,
public FooStat: FooString,
public FooName: FooString ) { }
}
Everything ends up with the same instance of FooString. How do I get discrete instances for each of them?
I have tried a factory, but it still only creates a single instance:
export let fooStringProvider = provide(FooString, {
useFactory: (): FooString => {
console.log('in foostring factory');
return new FooString();
}
});
new FooString();
new Payment();
;-)
Why using DI when they don't have dependencies and you don't want to maintain single instances per provider. Therefore, just use new.
When to use DI
There are a few criterias when using DI instead of new the right thing:
If you want Angular to maintain and share instances
If you want to work with an interface or base class but then you want to configure from the outside what implementation should actually be used at runtime - like the MockBackend for Http during testing.
If you class has dependencies to instances and/or values provided by DI
If you want to be able to easily test classes in isolation (https://en.wikipedia.org/wiki/Inversion_of_control)
probably others ...
If there are good arguments to use DI, but you also want new instances then you can just provide a factory.
This answer https://stackoverflow.com/a/36046754/217408 contains a concrete example how to do that.
Using DI is usually a good idea. There are IMHO no strong arguments against using DI. Only when none of the above arguments apply and providing factories is too cumbersome, use new Xxx() instead.

Resources