Nswag: question: How can I extend a service generated in Typescript?

Created on 24 Oct 2017  Â·  23Comments  Â·  Source: RicoSuter/NSwag

Hi,
I need to add a field to the Service "isLoading" and set its value before sending the request to the server to be true and when the response is back to be false.

How can I do this with NSWAG (Angular 4 and Typescript)?

Thank you
Bilal

question

Most helpful comment

Sample (but without ExtendedClasses just extension code):

https://github.com/RSuter/NSwag/blob/master/src/NSwag.Sample.NetCoreAngular/nswag.json#L53

All 23 comments

Can you post a sample?

Hi @RSuter ,
I just made it work :-)

Here is a sample:
````
@Injectable()
export class UsersServiceProxyExtensions extends UsersServiceProxy {

public isLoading = false;

constructor(private _http: Http, @Optional() @Inject(API_BASE_URL) _baseUrl?: string) {
    super(_http, _baseUrl);
}

public getUsers(input: GetUsersInputDto): Observable<GetUsersOutputDto> {
    const self = this;
    self.isLoading = true;

    return super.getUsers(input).do(() => self.isLoading = !self.isLoading);
}

}
````

Regards
Bilal H.

To avoid implementing an additional class, you can also mark the method getUsers as protected (ProtectedMethods setting) and extend the UsersServiceProxy with extension code (ExtensionCode and ExtensionClasses = ["UsersServiceProxy"])...

Hi @RSuter,
I am using the .nswag file to do the generations, so I must enable this setting ProtectedMethods in the configuration file?

Where would I place the extension code? Any sample?

Thanks

Sample (but without ExtendedClasses just extension code):

https://github.com/RSuter/NSwag/blob/master/src/NSwag.Sample.NetCoreAngular/nswag.json#L53

Thanks, @RSuter a lot.

@RSuter
Hi,
I am adding the following to the nswag config file:
"extendedClasses": ["Users"], "protectedMethods": ["GetUsers"],
I am not seeing any code generated in the extensionCode.

Users ==> UsersController
GetUsers ==> name of the action on the server side

ProtectedMethods must be "UsersServiceProxy.getUsers"
ExtendedClasses must be "UsersServiceProxy"

And ExtensionCode must be (I think) something like:

```
import * as generated from "./apiClients";

class UsersServiceProxy extends generated.UsersServiceProxy {
public getUsers() {
return this.getUsersCore();
}
}

@RSuter Thanks, it works!

@RSuter
It seems I am facing more issues now.
In my second case, I need to change the existing function (the way is done by NSWAG) to completely something new.

Here is the code:
````
import { Http } from '@angular/http';
import * as generated from './apiClients';
import { GetUsersInputDto, GetUsersOutputDto, DataSourceResult } from '../src/app/shared/service-proxies/service-proxies';
import {
toDataSourceRequestString,
translateDataSourceResultGroups,
translateAggregateResults,
DataResult,
DataSourceRequestState
} from '@progress/kendo-data-query';
import { GridDataResult, DataStateChangeEvent } from '@progress/kendo-angular-grid';

import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';

class UsersServiceProxy extends generated.UsersServiceProxy {
public isLoading = false;

getSecurityMatrix(state: DataSourceRequestState, filter: string, email: string): Observable<DataResult> {
    setTimeout(() => {
        this.isLoading = true;
    });

    const queryStr = `${toDataSourceRequestString(state)}`; // Serialize the state
    const hasGroups = state.group && state.group.length;
    const filterQs = filter ? `&filter=${filter}` : '';
    const emailQs = email ? `&email=${email}` : '';

    return this.http
        .get(`${this.baseUrl}?${queryStr}${emailQs}${filterQs}`) // Send the state to the server
        .do(() => this.isLoading = false)
        .map(response => response.json())
        .map(({data, total/*, aggregateResults*/}) => // Process the response
            (<GridDataResult>{
                data: hasGroups ? translateDataSourceResultGroups(data) : data,
                total: total
            })
        );
}

}
````

Now when I run NSWAG, I get the following exception:
``` System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary. at System.ThrowHelper.ThrowKeyNotFoundException() at System.Collections.Generic.Dictionary2.get_Item(TKey key)
at NJsonSchema.CodeGeneration.ExtensionCode.GetExtensionClassBody(String className)
at NSwag.CodeGeneration.TypeScript.Models.TypeScriptClientTemplateModel.get_HasExtendedConstructor()
at NSwag.CodeGeneration.TypeScript.Templates.AngularClientTemplate.TransformText()
at NSwag.CodeGeneration.TypeScript.Templates.AngularClientTemplate.Render()
at NSwag.CodeGeneration.ClientGeneratorBase`3.GenerateFile(SwaggerDocument document, ClientGeneratorOutputType type)
at NSwag.Commands.SwaggerToTypeScriptClientCommand.<b__115_0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at NSwag.Commands.SwaggerToTypeScriptClientCommand.d__115.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at NSwag.Commands.SwaggerToTypeScriptClientCommand.d__114.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at NSwag.Commands.NSwagDocumentBase.d__28.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at NSwag.Commands.Document.ExecuteDocumentCommandBase.d__5.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at NSwag.Commands.Document.ExecuteDocumentCommandBase.d__4.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at NConsole.CommandLineProcessor.d__12.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at NConsole.CommandLineProcessor.d__11.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at NConsole.CommandLineProcessor.Process(String[] args, Object input)
at NSwag.Commands.NSwagCommandProcessor.Process(String[] args)child_process.js:524
throw err;
^

Error: Command failed: "...\node_modules\nswag\bin/binaries/full/nswag.exe" run
at checkExecSyncError (child_process.js:481:13)
at Object.execSync (child_process.js:521:13)
at Object. (D:\Projects\EdAdminTool.Client\node_modules\nswag\bin\nswag.js:34:22)
at Module._compile (module.js:570:32)
at Object.Module._extensions..js (module.js:579:10)
at Module.load (module.js:487:32)
at tryModuleLoad (module.js:446:12)
at Function.Module._load (module.js:438:3)
at Module.runMain (module.js:604:10)
at run (bootstrap_node.js:389:7)
````

This is the JSON generated:
````
"/api/Users/GetSecurityMatrix": {
"get": {
"tags": [
"Users"
],
"operationId": "ApiUsersGetSecurityMatrixGet",
"consumes": [

    ],
    "produces": [
      "application/json"
    ],
    "parameters": [
      {
        "name": "request",
        "in": "query",
        "required": false
      },
      {
        "name": "filter",
        "in": "query",
        "required": false,
        "type": "string"
      },
      {
        "name": "email",
        "in": "query",
        "required": false,
        "type": "string"
      }
    ],
    "responses": {
      "200": {
        "description": "Success",
        "schema": {
          "$ref": "#/definitions/DataSourceResult"
        }
      }
    }
  }
},

````

I think the extension code does not contain all extended classes

@RSuter ,
Can you illustrate more on that? I sent you by email the json also just in case. Thanks

Can you post the content of ExtensionCode and the specified ExtendedClasses?

Actually, I am not able to generate the client-side code. That's why I sent you the json file, I dunno what happened!

I think the problem is that you specified an ExtendedTypes which is not present in ExtensionCode...

@RSuter
Here is the ExtensionCode:

````
import { Http } from '@angular/http';

import * as generated from './apiClients';

import { GetUsersInputDto, GetUsersOutputDto, DataSourceResult } from '../src/app/shared/service-proxies/service-proxies';
import {
toDataSourceRequestString,
translateDataSourceResultGroups,
translateAggregateResults,
DataResult,
DataSourceRequestState
} from '@progress/kendo-data-query';
import { GridDataResult, DataStateChangeEvent } from '@progress/kendo-angular-grid';

import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';

class UsersServiceProxy extends generated.UsersServiceProxy {
public isLoading = false;

public getUsers(input: GetUsersInputDto): Observable<GetUsersOutputDto> {
    setTimeout(() => {
        this.isLoading = true;
    }, 0);
    return this.getUsersCore(input).do(() => this.isLoading = false);
}

public getSecurityMatrix(request: DataSourceRequestState, filter: string, email: string): Observable<DataResult> {
    setTimeout(() => {
        this.isLoading = true;
    });

    const queryStr = `${toDataSourceRequestString(request)}`; // Serialize the state
    const hasGroups = request.group && request.group.length;
    const filterQs = filter ? `&filter=${filter}` : '';
    const emailQs = email ? `&email=${email}` : '';

    return this.http
        .get(`${this.baseUrl}?${queryStr}${emailQs}${filterQs}`) // Send the state to the server
        .do(() => this.isLoading = false)
        .map(response => response.json())
        .map(({data, total/*, aggregateResults*/}) => // Process the response
            (<GridDataResult>{
                // If there are groups, convert them to a compatible format
                data: hasGroups ? translateDataSourceResultGroups(data) : data,
                total: total,
                // Convert the aggregates if such exist
                // aggregateResult: translateAggregateResults(aggregateResults)

            })
        );
}

}
````

And the original class generated is:
````
@Injectable()
export class UsersServiceProxy {
private http: Http;
private baseUrl: string;
protected jsonParseReviver: (key: string, value: any) => any = undefined;

constructor(@Inject(Http) http: Http, @Optional() @Inject(API_BASE_URL) baseUrl?: string) {
    this.http = http;
    this.baseUrl = baseUrl ? baseUrl : "";
}

public isLoading = false;

public getUsers(input: GetUsersInputDto): Observable<GetUsersOutputDto> {
     setTimeout(() => {
        this.isLoading = true;
    }, 0);
    return this.getUsersCore(input).do(() => this.isLoading = false);
}

public getSecurityMatrix(request: any, filter: string, email: string): Observable<DataSourceResult> {
    setTimeout(() => {
        this.isLoading = true;
    });
    return this.getSecurityMatrixCore(request, filter, email).do(() => this.isLoading = false);
}



/**
 * @return Success
 */
protected getUsersCore(input: GetUsersInputDto): Observable<GetUsersOutputDto> {
    let url_ = this.baseUrl + "/api/Users/GetUsers";
    url_ = url_.replace(/[?&]$/, "");

    const content_ = JSON.stringify(input);

    let options_ = {
        body: content_,
        method: "post",
        headers: new Headers({
            "Content-Type": "application/json", 
            "Accept": "application/json"
        })
    };

    return this.http.request(url_, options_).flatMap((response_) => {
        return this.processGetUsers(response_);
    }).catch((response_: any) => {
        if (response_ instanceof Response) {
            try {
                return this.processGetUsers(response_);
            } catch (e) {
                return <Observable<GetUsersOutputDto>><any>Observable.throw(e);
            }
        } else
            return <Observable<GetUsersOutputDto>><any>Observable.throw(response_);
    });
}

protected processGetUsers(response: Response): Observable<GetUsersOutputDto> {
    const status = response.status; 

    let _headers: any = response.headers ? response.headers.toJSON() : {};
    if (status === 200) {
        const _responseText = response.text();
        let result200: any = null;
        let resultData200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
        result200 = resultData200 ? GetUsersOutputDto.fromJS(resultData200) : new GetUsersOutputDto();
        return Observable.of(result200);
    } else if (status !== 200 && status !== 204) {
        const _responseText = response.text();
        return throwException("An unexpected server error occurred.", status, _responseText, _headers);
    }
    return Observable.of<GetUsersOutputDto>(<any>null);
}

/**
 * @return Success
 */
protected getSecurityMatrixCore(request: any, filter: string, email: string): Observable<DataSourceResult> {
    let url_ = this.baseUrl + "/api/Users/GetSecurityMatrix?";
    if (request !== undefined)
        url_ += "request=" + encodeURIComponent("" + request) + "&"; 
    if (filter !== undefined)
        url_ += "filter=" + encodeURIComponent("" + filter) + "&"; 
    if (email !== undefined)
        url_ += "email=" + encodeURIComponent("" + email) + "&"; 
    url_ = url_.replace(/[?&]$/, "");

    let options_ = {
        method: "post",
        headers: new Headers({
            "Content-Type": "application/json", 
            "Accept": "application/json"
        })
    };

    return this.http.request(url_, options_).flatMap((response_) => {
        return this.processGetSecurityMatrix(response_);
    }).catch((response_: any) => {
        if (response_ instanceof Response) {
            try {
                return this.processGetSecurityMatrix(response_);
            } catch (e) {
                return <Observable<DataSourceResult>><any>Observable.throw(e);
            }
        } else
            return <Observable<DataSourceResult>><any>Observable.throw(response_);
    });
}

protected processGetSecurityMatrix(response: Response): Observable<DataSourceResult> {
    const status = response.status; 

    let _headers: any = response.headers ? response.headers.toJSON() : {};
    if (status === 200) {
        const _responseText = response.text();
        let result200: any = null;
        let resultData200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
        result200 = resultData200 ? DataSourceResult.fromJS(resultData200) : new DataSourceResult();
        return Observable.of(result200);
    } else if (status !== 200 && status !== 204) {
        const _responseText = response.text();
        return throwException("An unexpected server error occurred.", status, _responseText, _headers);
    }
    return Observable.of<DataSourceResult>(<any>null);
}

}
````

@RSuter
Hello Rico, any idea what might be going on here? Thanks

What ExtendedClasses have to specified?

@RSuter

"extensionCode": "service.extensions.ts", "extendedClasses": ["UsersServiceProxy"], "protectedMethods": ["UsersServiceProxy.getUsers", "UsersServiceProxy.getSecurityMatrix"],

Can't see a problem from looking at it... you should probably clone the repo, start NSwagStudio and debug this problem (choose Runtime: Debug)

Do you detailed steps on how to do so? I'm not using studio btw I'm just
writing the configuration file myself.

Thanks

On 27 Oct 2017 1:14 am, "Rico Suter" notifications@github.com wrote:

Can't see a problem from looking at it... you should probably clone the
repo, start NSwagStudio and debug this problem (choose Runtime: Debug)

—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub
https://github.com/RSuter/NSwag/issues/1012#issuecomment-339816562, or mute
the thread
https://github.com/notifications/unsubscribe-auth/ABHAnXe7Dyq3EL1VC9aK5rkr_ImSiuL5ks5swQQ-gaJpZM4QEZHR
.

Hi @RSuter
I downloaded NSWAG Studio and tried to debug. Things are a bit improving. Now I get this exception:

I already have defined the class UsersServiceProxy inside the service.extensions.ts file

Any idea?

Thanks

````
System.InvalidOperationException: The extension class 'UsersServiceProxy' is not defined.

Runtime: Default
at NJsonSchema.CodeGeneration.ExtensionCode.GetExtensionClassBody(String className)
at NSwag.CodeGeneration.TypeScript.Models.TypeScriptClientTemplateModel.get_HasExtendedConstructor()
at NSwag.CodeGeneration.TypeScript.Templates.AngularClientTemplate.TransformText()
at NSwag.CodeGeneration.TypeScript.Templates.AngularClientTemplate.Render()
at NSwag.CodeGeneration.ClientGeneratorBase`3.GenerateFile(SwaggerDocument document, ClientGeneratorOutputType type)
at NSwag.Commands.SwaggerToTypeScriptClientCommand.<b__120_0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at NSwag.Commands.SwaggerToTypeScriptClientCommand.d__120.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at NSwag.Commands.SwaggerToTypeScriptClientCommand.d__119.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at NSwag.Commands.NSwagDocument.d__7.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at NSwag.Commands.Document.ExecuteDocumentCommand.d__5.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at NSwag.Commands.Document.ExecuteDocumentCommand.d__4.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at NConsole.CommandLineProcessor.d__12.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at NConsole.CommandLineProcessor.d__11.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at NConsole.CommandLineProcessor.Process(String[] args, Object input)
at NSwag.Commands.NSwagCommandProcessor.Process(String[] args)
````

Was this page helpful?
0 / 5 - 0 ratings