# @node-ts-autumn/core

* [Install](#install)
* [Example Project](#example-project)
* [Controllers](#controllers)
* [Services](#services)
---
## core-module

typescript node mvc framework

### development state

alpha version

>Features
* incl. request mapping via annotation
* incl. path params via annotation
* incl. request params via annotation
* incl. post body via annotation
* _planned package scan for controllers_

> Template Rendering
* incl. PugViewResolver
* incl. HandlebarsViewResolver
* incl. ResourceResolver
* incl. JsonResponse
* _planned Angular2 prerendering ViewResolver_
---
## Install

### npm

needs [@node-ts-autumn/server](https://www.npmjs.com/package/@node-ts-autumn/server)

    npm install @node-ts-autumn/core --save

### tsconfig
```json
    {
        "compilerOptions": {
            "typeRoots": ["node_modules/@types", "node_modules/@node-ts-autumn"],
            "types": ["node","core"],
        },
    }
```
---
## Example Project

>Application

Configure the main _application_ by setting up an `Application` class that extends the `AbstractApplication` class from `@node-ts-autumn/core`.

This is an example `Application` class for the application every component configuration is handed to this class.

```typescript
import { BaseApplicationConfig, RootApplicationConfig, WebApplicationConfig } from '@node-ts-autumn/core';
import { AbstractApplication, Application } from '@node-ts-autumn/core';
import { HttpNodeRequest, HttpNodeError, HttpNodeResponse } from '@node-ts-autumn/server';

import { MyBaseApplicationConfig } from './config/base/base_application_config';
import { MyRootApplicationConfig } from './config/root/root_application_config';
import { MyWebApplicationConfig } from './config/web/web_application_config';

export class MyApplication extends AbstractApplication implements Application {

    constructor () {
        super();
    }

    public getBaseApplicationConfig (): BaseApplicationConfig {
        return new MyBaseApplicationConfig();
    }

    public getRootApplicationConfig (): RootApplicationConfig {
        return new MyRootApplicationConfig();
    }

    public getWebApplicationConfig (): WebApplicationConfig {
        return new MyWebApplicationConfig();
    }

    public getContext (): string {
        return '/';
    }
}
```

>RootApplicationConfig

Configure the _root application config_ by setting up a `RootApplicationConfig` class that extends the `AbstractRootApplicationConfig` class from `@node-ts-autumn/core`.

The existing abstract root application class is for now a __placeholder__, here the configuration for filters like security context or encoding filters will be configured.

```typescript
import { AbstractSiteConfig } from '@node-ts-autumn/core';

class RootApplicationConfig extends AbstractRootApplicationConfig {

}
```

>WebApplicationConfig

Configure the _web application config_ by setting up a `WebApplicationConfig` class that extends the `AbstractWebApplicationConfig` class from `@node-ts-autumn/core`.

For now the `WebApplicationConfig` has two main features:
+ setting up your main ViewResolver for rendering html (use of different ViewResolvers for html rendering is planned)
+ setting up your controller classes and mapping request to specific methods

```typescript
import { MediaType } from '@node-ts-autumn/core';
import { Configuration, ViewResolver, HandlebarsViewResolver, PugViewResolver, WebApplicationConfig, AbstractWebApplicationConfig } from '@node-ts-autumn/core';

import { MyController } from '../../app/controller/controller';

export class MyWebApplicationConfig extends AbstractWebApplicationConfig implements WebApplicationConfig {

    constructor () {
        super();
    }

    @Configuration('ViewResolver')
    private handlebarsViewResolver (): ViewResolver {
        let viewResolver: HandlebarsViewResolver = new HandlebarsViewResolver();
        viewResolver.setPrefix(global['basepath'] + '/views/handlebars');
        viewResolver.setSuffix('.html');
        viewResolver.setViewClass('html');
        viewResolver.registerPartials(['includes/head', 'includes/nav', 'layouts/layout']);
        return viewResolver;
    }

    public getControllerClasses (): Array<any> {
        let controllerClasses: Array<any> = new Array<any>();
        controllerClasses.push(new MyController());
        return controllerClasses;
    }
}
```
---
## Controllers

>RequestMapping

Controller classes can hold one or more methods which are mapped by the `@RequestMapping` annotation.
`@RequestMapping` requires an Object with the property `value` to which url-path the method should be mapped to.

```typescript
@RequestMethod({value: '/'}) // mappes the following method to '/' or ''

@RequestMethod({value: '/info'}) // mappes the following method to '/info'
```

The `@RequestMapping` Object can hold value, method (default = 'GET'), consumes (default = MediaType.APPLICATION\_FORM\_URLENCODED or 'application/x-www-form-urlencoded'), produces (default = MediaType.TEXT_HTML or 'text/html'))
```typescript
@RequestMethod({value:'/acceptform', method: 'POST', consumes: MediaType.APPLICATION_FORM_URLENCODED, produces: MediaType.TEXT_HTML }) // mappes the following method to '/acceptform' accepts form data and is mapped to the html ViewResolver

@RequestMethod({value:'/data', method: 'POST', consumes: MediaType.APPLICATION_JSON, produces: MediaType.APPLICATION_JSON }) // mappes the following method to '/data' accepts json data and is mapped to the internal JsonResolver
```

#### basic mapped method

```typescript
@RequestMapping({value: '/info'})                                   // every method needs the ModelAndView callback parameter - callback: (modelAndView: ModelAndView) => void
public info(callback: (modelAndView: ModelAndView) => void) {
    callback(new ModelAndView('info'));
}
```

>url-parameters, path-parameters and body-data

Methods mapped with the `@RequestMapping` annotation can get url-parameters, path-parameters and body-data as method parameters.
Methods hand of the ModelAndView class via callback to the ViewResolver which incorporates the model-data to the predefined view.


#### url parameter
```typescript
@RequestMapping({value: '/info'})                                   // mapped to info with x-www-form-urlencoded data
public info(@RequestParam id: number, @RequestParam name: string, callback: (modelAndView: ModelAndView) => void) {
    let mav = new ModelAndView('info');
    mav.addModel('id', id);
    mav.addModel('name', name);
    callback(mav);
}
```

#### body data

```typescript
@RequestMapping({value: '/acceptform', method: 'POST'})             // mapped to info with x-www-form-urlencoded post data
public info(@RequestBody TestModel: testModel, callback: (modelAndView: ModelAndView) => void) {
    let mav = new ModelAndView('info');
    mav.addModel('testModel', testModel);
    callback(mav);
}
```

#### path parameter

```typescript
@RequestMapping({value: '/all/{id}/info/{name}'})                   // mapped to infoAndId with path parameters
public infoAndId(@PathParam id: number, @PathParam name: string, callback: (modelAndView: ModelAndView) => void) {
    let modelAndView = new ModelAndView('info');
    modelAndView.addModel('id', id);
    modelAndView.addModel('name', name);
    callback(modelAndView);
}
```
---
## Services

Service class support will be further developed.

For now only incorporation in the controller classes is supported.

>Injectable

The `@Injectable` annotation allows for the annotated class to be injected in your controller.
f.e. a simple RestService

```typescript
@Injectable
export class TestHttpService {

    basic_options: http.RequestOptions;

    constructor() {
        this.basic_options = new Object();
        this.basic_options.host = 'localhost';
        this.basic_options.port = 4000;
        this.basic_options.path = '/json/testModel';
        this.basic_options.headers = {
            'Content-Type': 'application/json',
            'Accepts': 'application/json'
        };
    }

    public findAll(callback: (testModels: Array<TestModel>) => void): void {
        this.basic_options.method = 'GET';
        let req = this.requestFactory(callback);
        req.on('error', (err) => {
            logger.error(err);
        });
        req.end();
    }

    public findOne(id: number, callback: (testModel: TestModel) => void): void {
        this.basic_options.method = 'POST';
        let req = this.requestFactory(callback);
        req.on('error', (err) => {
            logger.error(err);
        });
        req.write(JSON.stringify({'id': id}));
        req.end();
    }

    public save(testModel: TestModel, id: number = null, callback: (testModel: TestModel) => void): void {
        this.basic_options.method = 'PUT';
        let req = this.requestFactory(callback);
        req.on('error', (err) => {
            logger.error(err);
        });
        req.write(JSON.stringify({'id': id, 'testModel': testModel}));
        req.end();
    }

    private requestFactory(callback: Function): http.ClientRequest {
        return http.request(this.basic_options, (res: http.IncomingMessage) => {
            res.setEncoding('utf8');
            let buffer = '';
            res.on('error', (err) => {
                logger.error(err);
            });
            res.on('data', (data) => {
                buffer += data;
            });
            res.on('end', () => {
                callback(JSON.parse(buffer).data);
            });
        });
    }
}
```

>@Inject

`@Inject` injects the annotated service class into the controller.

```typescript
import { Inject } from '@node-ts-autumn/core';
import { TestHttpService } from // your HttpService


export class TestController {

    @Inject
    testHttpService: TestHttpService;

    @RequestMapping({value: '/info'})
    public info(callback: (ModelAndView) => void): void {
        let testModels = this.testHttpService.findAll();
        let mav = new ModelAndView('info');
        mav.addModel('testModels', testModels);
        callback(mav);
    }
}
```
