APIServer: How to make custom typed client accessible via the QerApiService

I've build the 'HelloWorld' custom method example with: IApiProviderFor<QER.CompositionApi.Portal.PortalApiProject>
End-point works: itshop.groot.net/.../helloworld: {"Message":"Hello world!"}
imx-api-ccc.tgz compiled and installed: node_modules\imx-api-ccc\TypedClient.d.ts

How can I make the typed client 'portal_helloworld' accessible via the QerApiService: await this.qerClient.client.portal_helloworld
(Property 'portal_helloworld_get' does not exist on type 'V2Client')

How and where do I incorporate/import the custom api?
projects\qer\src\lib\qer-api-client.service.ts
import { V2Client, Client, TypedClient } from 'imx-api-qer';

Thank you
Niels

  • Hi,

    Did you upload the .dll file to the Apiserver? In my case, I follow these steps:

    1. Develop my custom plugin.
    2. Compile and generate the CCC.Custom.. dll
    3. Upload the .dll file to the bin folder of the apiserver. Check the logs to make sure it gets loaded and the swagger explorer to check it's there.
    4. Upload the same .dll file to oneidentity folder.
    5. Generate the typescript client with imxclient compile-api
    6. Upload the imx-api-ccc.tgz to imx_modules of my development environment , and run npm install from that folder
  • Hello Juan Carlos,

    I repeated all the steps.

    The end point is there: https://itshop.groot.net/apiserverdev/portal/helloworld
    It's included in swagger.json:https://itshop.groot.net/apiserverdev/swagger/swagger.json

    "/portal/helloworld": {
          "get": {
            "tags": [],
            "operationId": "portal_helloworld_get",
            "parameters": [],
            "responses": {
              "500": {
                "description": "InternalServerError",
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/definitions/ExceptionData"
                  }
                }
              },
              "200": {
                "description": "OK",
                "schema": {
                  "$ref": "#/definitions/DataObject"
                }
              }
            }
          }
        },

    Upload the imx-api-ccc.tgz to imx_modules and installed it
    I can see it under node_modules\imx-api-ccc\TypedClient.d.ts

    Now I want to make it accessible (i'm not a webdeveloper, from this point it's pure on gut feeling ;-)
    Looking into 'projects\qer\src\lib\qer-api-client.service.ts' my reason of thought was this is the right place to import 'imx-api-ccc'

    import { V2Client, Client, TypedClient } from 'imx-api-qer';
    import { V2Client, Client, TypedClient } from 'imx-api-ccc';

    This will cause: This Duplicate identifier 'V2Client'

    But since the 'imx-api-qer' and 'imx-api-ccc' have the same identifiers how do I combine them and make
    it accessible via the QerApiService so that the this.qerClient.client.portal_helloworld is also included?

    Thx,
    Niels

  • It's a bit confusing, I agree. I'm not an expert, either. I'm struggling everyday with the Angular code Slight smile

    Because you compiled your plugin for the QER.CompositionApi.Portal.PortalApiProject , the V2Client for imx-api-qer should be enough, and your custom method GET portal/helloworld should have become v2client.portal_helloworld_get .

    In your code:

    1. Import the QerApiService
    2. Declare your own client.
    3. Check that the v2client has included your custom method.

    import { QerApiService } from '../qer-api-client.service';
    (...)
    private readonly myqerClient: QerApiService
    (...)
    console.log(this.myqerClient.v2Client.portal_helloworld_get)

    I'll try to reproduce your case to give you more input.

    See here, too:

    support.oneidentity.com/.../2

  • The v2client has not included the new custom method:
    Property 'portal_helloworld_get' does not exist on type 'V2Client'.


    When I run the ixmclient compile without the 'CCC.CompositionApi.Server.Plugin.dll' in the \bin
    .\imxclient compile-api /copyapi imx-api-ccc.tgz /packagename imx-api-ccc

    Info: Found 209 attribute-based API providers for QER.CompositionApi.Portal.PortalApiProject

    When I run the ixmclient compile with the 'CCC.CompositionApi.Server.Plugin.dll' in the \bin
    .\imxclient compile-api /copyapi imx-api-ccc.tgz /packagename imx-api-ccc

    Info: Plugin assembly loaded: CCC.CompositionApi.Server.Plugin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
    Info: Found 210 attribute-based API providers for QER.CompositionApi.Portal.PortalApiProject

    So that looks right one new attribute-based API provider.
    Then place the imx-api-ccc.tgz into D:\Projects\IdentityManager-Imx-Test\imxweb\imx-modules
    and run D:\Projects\IdentityManager-Imx-Test\imxweb\npm install

    In the source control I can only see the newly placed 'imx-api-ccc.tgz' no other changes occurred in the project.
    It feels like something is still missing...

    using QBM.CompositionApi.ApiManager;
    using QBM.CompositionApi.Crud;
    using QBM.CompositionApi.Definition;
    
    [assembly: QBM.CompositionApi.PlugIns.Module("CCC")]
    
    namespace QBM.CompositionApi
    
    {
    
        public class CustomMethod : IApiProviderFor<QER.CompositionApi.Portal.PortalApiProject>
        {
            public void Build(IApiBuilder builder)
            {
                // This is how a method can return objects of any type,
                // provided that the type can be serialized.
                builder.AddMethod(Method.Define("helloworld")
                    .AllowUnauthenticated()
                    .HandleGet(qr => new DataObject { Message = "Hello world!" }));
    
                // This is how posted data of any type can be processed.
                builder.AddMethod(Method.Define("helloworld/post")
                    .AllowUnauthenticated()
                    .Handle<PostedMessage, DataObject>("POST",
                        (posted, qr) => new DataObject
                        {
                            Message = "You posted the following message: " + posted.Input
                        }));
    
                // This is an example of a method that generates plain text (not JSON formatted).
                // You can use this to generate content of any type.
                builder.AddMethod(Method.Define("helloworld/text")
                    .AllowUnauthenticated()
                    .HandleGet(new ContentTypeSelector
                    {
                        // Specifiy the MIME type of the response.
                        new ResponseBuilder("text/plain", (qr, ct) =>
                        {
                            return System.Threading.Tasks.Task.FromResult(new System.Net.Http.HttpResponseMessage
                            {
                                Content = new System.Net.Http.StringContent("Hello world!")
                            });
                        })
                    }, typeof(string)));
            }
        }
    
    
        // This class defines the type of data object that will be sent to the client.
        public class DataObject
        {
            public string Message { get; set; }
        }
    
        // This class defines the type of data object that will be sent from the client to the server.
        public class PostedMessage
        {
            public string Input { get; set; }
        }
    }

  • Also found this: Missing API methods in TypedClient

    Changed the code build new .dll and .tgz but still no difference.
    ...
    [assembly: QBM.CompositionApi.PlugIns.Module("CCC")]
    namespace QBM.CompositionApi
    ...

    imxweb\node_modules\imx-api-ccc\TypedClient.d.ts

    import { ApiClient, ApiRequestOptions, EntitySchema, FkProviderItem, MethodDescriptor } from 'imx-qbm-dbts';
    export interface DataObject {
        Message?: string;
    }
    /** DTO for exception data. */
    export interface ExceptionData {
        /** Gets or sets the exception message. */
        Message?: string;
        /** Gets or sets the error number for ViException-typed exceptions. */
        Number: number;
    }
    export interface PostedMessage {
        Input?: string;
    }
    export declare class TypedClient {
        constructor(client: V2Client, translationProvider?: any);
    }
    /** @deprecated Use the V2ApiClientMethodFactory class for a stable method interface. */
    export declare class ApiClientMethodFactory {
        portal_helloworld_get(): MethodDescriptor<DataObject>;
        portal_helloworld_post_post(inputParameterName: PostedMessage): MethodDescriptor<DataObject>;
        portal_helloworld_text_get(): MethodDescriptor<string>;
    }
    /** @deprecated Use the V2Client class for a stable method interface. */
    export declare class Client {
        private readonly apiClient;
        private schemaProvider?;
        private readonly methodFactory;
        constructor(apiClient: ApiClient, schemaProvider?: {
            readonly schemas: {
                [key: string]: EntitySchema;
            };
        });
        get schemas(): {
            [key: string]: EntitySchema;
        };
        loadSchema(language?: string): Promise<void>;
        getFkProviderItems(methodKey: string): FkProviderItem[];
        /** Returns the runtime schema for the named method. */
        getSchema(methodKey: string): EntitySchema;
        portal_helloworld_get(requestOptions?: ApiRequestOptions): Promise<DataObject>;
        portal_helloworld_post_post(inputParameterName: PostedMessage, requestOptions?: ApiRequestOptions): Promise<DataObject>;
        portal_helloworld_text_get(requestOptions?: ApiRequestOptions): Promise<string>;
    }
    export declare class V2ApiClientMethodFactory {
        portal_helloworld_get(options?: {}, requestOptions?: ApiRequestOptions): MethodDescriptor<DataObject>;
        portal_helloworld_post_post(inputParameterName: PostedMessage, options?: {}, requestOptions?: ApiRequestOptions): MethodDescriptor<DataObject>;
        portal_helloworld_text_get(options?: {}, requestOptions?: ApiRequestOptions): MethodDescriptor<string>;
    }
    export declare class V2Client {
        private readonly apiClient;
        private schemaProvider?;
        private readonly methodFactory;
        constructor(apiClient: ApiClient, schemaProvider?: {
            readonly schemas: {
                [key: string]: EntitySchema;
            };
        });
        get schemas(): {
            [key: string]: EntitySchema;
        };
        loadSchema(language?: string): Promise<void>;
        getFkProviderItems(methodKey: string): FkProviderItem[];
        /** Returns the runtime schema for the named method. */
        getSchema(methodKey: string): EntitySchema;
        portal_helloworld_get(options?: {}, requestOptions?: ApiRequestOptions): Promise<DataObject>;
        portal_helloworld_post_post(inputParameterName: PostedMessage, options?: {}, requestOptions?: ApiRequestOptions): Promise<DataObject>;
        portal_helloworld_text_get(options?: {}, requestOptions?: ApiRequestOptions): Promise<string>;
    }
    

  • In my case, I created a new api project for the Apiserver and the plugin was attached to it, so I found no problems using my own v2client. Thing is, as you say, when you need to extend the current project (QER.CompositionApi.Portal.PortalApiProject)  with your custom api code. I've been working this morning with it and met another problem with the caches that I managed to solve at the end of the day. 

    I'm sure your .dll and tgz generation are right. But we still need the part about integrating the typedclient in the code and why , if the .dll was compiled for the PortaAPIProject, it doesn't get integrated in the QerAPIService.

    This was my case , which I finally solved, but in that I'm using my own v2client and nothing else:

    www.oneidentity.com/.../initializing-the-api-generated-imx-api-ccc-tgz-for-a-custom-app

  • Niels,

    Two workarounds:

    a)  Do not import V2Client from imx-api-qer. Instead import QerApiService from qer-api-client.service and reserve V2Client for your code:

    // import { V2Client } from 'imx-api-qer';
    import { QerApiService } from '../qer-api-client.service';
    import { V2Client} from 'imx-api-ccc';

    b) Create an alias for V2Client when importing your library.

    import { V2Client } from 'imx-api-qer';
    import { V2Client as myV2Client } from 'imx-api-ccc';
    
    (...)
     constructor(
        private readonly micliente: myV2Client,
        private readonly legacyClient: V2Client,

  • Hello Juan Carlos,

    Thank you for helping me out verifying and clarifying things and providing a solution!

    Till next time,
    Niels

  • Believe me, I've learnt more than you xDD