Open Closed

Angular Proxy Generation: Cannot read properties of null (reading 'replace') #6319


User avatar
0
MichelZ created
  • ABP Framework version: v7.4.2
  • UI Type: Angular /
  • Database System: EF Core (SQL Server)
  • Tiered (for MVC) or Auth Server Separated (for Angular): yes
  • Exception message and full stack trace:
  • Steps to reproduce the issue:

After updating from v6.x we are now trying to generate the Angular proxies, but we are getting: Cannot read properties of null (reading 'replace') when executing: abp generate-proxy -t ng -u http://localhost:5101

We can reach the api-definition document on: http://localhost:5101/api/abp/api-definition

Any pointers how to troubleshoot this? Does the abp cli generate logs or something we can use?


19 Answer(s)
  • User Avatar
    0
    Anjali_Musmade created
    Support Team Support Team Member

    Hello,

    Have you checked this https://stackoverflow.com/questions/70278156/abp-io-angular-cannot-read-property-replace-of-undefined

    Have you followed these migration guides https://docs.abp.io/en/abp/latest/Migration-Guides/Index

    Please check once and let me know if found helpful.

    Thanks

  • User Avatar
    0
    MichelZ created

    Hello,

    Have you checked this https://stackoverflow.com/questions/70278156/abp-io-angular-cannot-read-property-replace-of-undefined

    Have you followed these migration guides https://docs.abp.io/en/abp/latest/Migration-Guides/Index

    Please check once and let me know if found helpful.

    Thanks

    We have followed the migration guides. The application does work when deployed, but we have the need to generate the proxies now for added functionality, which does fail.

    I'm not sure what that stackoverflow thread is trying to tell me.

  • User Avatar
    0
    Anjali_Musmade created
    Support Team Support Team Member

    Hi,

    Above stack overflow discussion is about 2 scenarios

    1. If there is no model/service then also you are trying to generate proxy
    2. If you are targeting to live API address instead of local one

    If these are not the case then please check

    1. Check your ABP Framework and CLI version are same or not?
    2. or once run npm install to install all the necessary dependencies and then try
  • User Avatar
    0
    MichelZ created

    Hi,

    Above stack overflow discussion is about 2 scenarios

    1. If there is no model/service then also you are trying to generate proxy
    2. If you are targeting to live API address instead of local one
    1. We do have services of course
    2. We are not targeting live API, abp generate-proxy -t ng -u http://localhost:5101 <--- Localhost!

    > If these are not the case then please check

    1. Check your ABP Framework and CLI version are same or not?

    CLI is .1 newer (7.4.3 vs 7.4.2)

    1. or once run npm install to install all the necessary dependencies and then try

    The frontend builds fine with ng build / ng serve. Packages are all installed

    yarn install v1.22.19 [1/4] Resolving packages... success Already up-to-date. Done in 0.42s.

  • User Avatar
    0
    Anjali_Musmade created
    Support Team Support Team Member

    Hi,

    I think you need to change CLI version to 7.4.2 once try with this.

    For downgrading version you can follow below commands dotnet tool uninstall -g volo.abp.cli dotnet tool uninstall -g volo.abp.Suite Delete folder after running uninstall command (C:\Users\MyName.abp) dotnet tool install -g volo.abp.cli --version 7.4.2 after this sign in with abp login username -p password abp suite install --v 7.4.2

  • User Avatar
    0
    MichelZ created

    We have tried with version 7.4.2 now, with the same result.

  • User Avatar
    0
    MichelZ created

    We have tried this with the application before-upgrade now (v6.0.2) and it worked (I stated previously that it didn't, but I executed it in the wrong directory)

    So it seems there is some issue with the upgrade from 6.0.2 to 7.4.2? Any pointers?

  • User Avatar
    0
    Anjali_Musmade created
    Support Team Support Team Member

    Hi,

    Could you please share your package.json file?

  • User Avatar
    0
    MichelZ created
    {
      "name": "ProductName",
      "version": "0.0.0",
      "scripts": {
        "ng": "ng",
        "start": "ng serve --open",
        "start:development-staging": "ng server --open --configuration development-staging",
        "build": "ng build",
        "build:docker-compose": "ng build --configuration docker-compose",
        "build:development-staging": "ng build --configuration development-staging",
        "build:prod": "ng build --configuration production",
        "watch": "ng build --watch --configuration development",
        "test": "ng test",
        "lint": "ng lint"
      },
      "private": true,
      "dependencies": {
        "@abp/ng.components": "~7.4.2",
        "@abp/ng.core": "~7.4.2",
        "@abp/ng.oauth": "~7.4.2",
        "@abp/ng.setting-management": "~7.4.2",
        "@abp/ng.theme.shared": "~7.4.2",
        "@amcharts/amcharts4": "^4.10.38",
        "@angular-builders/jest": "^16.0.0",
        "@angular/animations": "^16.2.12",
        "@angular/common": "^16.2.12",
        "@angular/compiler": "^16.2.12",
        "@angular/core": "^16.2.12",
        "@angular/forms": "^16.2.12",
        "@angular/localize": "16.2.12",
        "@angular/platform-browser": "^16.2.12",
        "@angular/platform-browser-dynamic": "^16.2.12",
        "@angular/router": "^16.2.12",
        "@chiragrupani/karma-chromium-edge-launcher": "^2.1.1",
        "@ngxs/store": "^3.7.6",
        "@swimlane/ngx-charts": "^20.0.0",
        "@volo/abp.commercial.ng.ui": "~7.4.2",
        "@volo/abp.ng.account": "~7.4.2",
        "@volo/abp.ng.audit-logging": "~7.4.2",
        "@volo/abp.ng.gdpr": "~7.4.2",
        "@volo/abp.ng.identity": "~7.4.2",
        "@volo/abp.ng.language-management": "~7.4.2",
        "@volo/abp.ng.openiddictpro": "~7.4.2",
        "@volo/abp.ng.saas": "~7.4.2",
        "@volo/abp.ng.text-template-management": "~7.4.2",
        "@volo/abp.ng.theme.lepton": "7.4.2",
        "@volo/language-management": "~7.4.2",
        "@yaireo/tagify": "^4.17.0",
        "bootstrap-icons": "^1.11.2",
        "jest": "^29.7.0",
        "karma-junit-reporter": "^2.0.1",
        "ngx-bootstrap-multiselect": "^4.0.0",
        "ngx-editor": "^16.0.0",
        "rxjs": "7.8.1",
        "tslib": "^2.1.0",
        "zone.js": "~0.13.3"
      },
      "devDependencies": {
        "@abp/ng.schematics": "~7.4.2",
        "@angular-devkit/build-angular": "^16.2.10",
        "@angular-eslint/builder": "16.3.1",
        "@angular-eslint/eslint-plugin": "16.3.1",
        "@angular-eslint/eslint-plugin-template": "16.3.1",
        "@angular-eslint/schematics": "16.3.1",
        "@angular-eslint/template-parser": "16.3.1",
        "@angular/cli": "^16.2.10",
        "@angular/compiler-cli": "^16.2.12",
        "@angular/language-service": "^16.2.12",
        "@types/jasmine": "~3.6.0",
        "@types/node": "^18.11.0",
        "@typescript-eslint/eslint-plugin": "^5.59.2",
        "@typescript-eslint/parser": "^5.59.2",
        "eslint": "^8.39.0",
        "jasmine-core": "~4.0.0",
        "karma": "~6.3.0",
        "karma-chrome-launcher": "~3.1.0",
        "karma-coverage": "~2.1.0",
        "karma-jasmine": "~4.0.0",
        "karma-jasmine-html-reporter": "^1.7.0",
        "ng-packagr": "^16.2.3",
        "typescript": "~5.1.6"
      },
      "resolutions": {
        "prosemirror-view": "1.31.7"
      }
    }
    
    
  • User Avatar
    0
    MichelZ created

    I was able to get a stack trace by using this: yarn nx g @abp/nx.generators:generate-proxy --verbose maybe it helps?

    TypeError: Cannot read properties of null (reading 'replace')
        at sanitizeTypeName (W:\DEV\ProductName\abp\angular\node_modules\@abp\ng.schematics\utils\common.js:35:41)
        at W:\DEV\ProductName\abp\angular\node_modules\@abp\ng.schematics\utils\common.js:58:26
        at Array.forEach (<anonymous>)
        at W:\DEV\ProductName\abp\angular\node_modules\@abp\ng.schematics\utils\common.js:57:27
        at Array.forEach (<anonymous>)
        at W:\DEV\ProductName\abp\angular\node_modules\@abp\ng.schematics\utils\common.js:49:49
        at Array.forEach (<anonymous>)
        at sanitizeControllerTypeNames (W:\DEV\ProductName\abp\angular\node_modules\@abp\ng.schematics\utils\common.js:37:38)
        at W:\DEV\ProductName\abp\angular\node_modules\@abp\ng.schematics\commands\api\index.js:31:92
        at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    
  • User Avatar
    0
    MichelZ created

    From what I can see is that it fails here in @abp\ng.schematics\utils\common.js

    Object.values(controller.actions || {}).forEach(a => {
                a.returnValue.type = sanitizeTypeName(a.returnValue.type);
                a.returnValue.typeSimple = sanitizeTypeName(a.returnValue.typeSimple);
                a.parametersOnMethod?.forEach(p => {
                    p.type = sanitizeTypeName(p.type);
                    p.typeAsString = sanitizeTypeName(p.typeAsString);
                    p.typeSimple = sanitizeTypeName(p.typeSimple);
                });
                a.parameters?.forEach(p => {
                    p.type = sanitizeTypeName(p.type);
                    p.typeSimple = sanitizeTypeName(p.typeSimple);
                });
            });
    

    Specifically here:

    a.parameters?.forEach(p => {
                    p.type = sanitizeTypeName(p.type);   <--------
                    p.typeSimple = sanitizeTypeName(p.typeSimple);
                });
    

    Looking into my api-definition, I get for instance this generated parameter on a method:

    "paramters": [
         {
                                        "nameOnMethod": "agentApiKeyId",
                                        "name": "agentApiKeyId",
                                        "jsonName": null,
                                        "type": null,
                                        "typeSimple": null,
                                        "isOptional": false,
                                        "defaultValue": null,
                                        "constraintTypes": [],
                                        "bindingSourceId": "Path",
                                        "descriptorName": ""
                                    }
         ]
    

    notice how the type is null.

    This particular method is in a Controller (however we observe the same from ApplicationServices as well):

        [HttpPost]
        [Route("api-keys/{agentApiKeyId}")]
        public async Task<ActionResult> SetAgentApiKeyStatusAsync(AgentApiKeyId agentApiKeyId, bool isEnabled)
        {
            await _agentAppService.SetApiKeyStatusAsync(agentApiKeyId, isEnabled);
            return NoContent();
        }
    

    Now, AgentApiKeyId itself is a value object struct (generated by the StronglyTypedId project) :

    //------------------------------------------------------------------------------
    // <auto-generated>
    //     This code was generated by the StronglyTypedId source generator
    //
    //     Changes to this file may cause incorrect behavior and will be lost if
    //     the code is regenerated.
    // </auto-generated>
    //------------------------------------------------------------------------------
    
    #pragma warning disable 1591 // publicly visible type or member must be documented
    
    namespace MyCompany.MyProject.Agent.ValueObjectId
    {
        [System.Text.Json.Serialization.JsonConverter(typeof(AgentApiKeyIdSystemTextJsonConverter))]
        [System.ComponentModel.TypeConverter(typeof(AgentApiKeyIdTypeConverter))]
        readonly partial struct AgentApiKeyId : System.IComparable<AgentApiKeyId>, System.IEquatable<AgentApiKeyId>
        {
            public int Value { get; }
    
            public AgentApiKeyId(int value)
            {
                Value = value;
            }
    
            public static readonly AgentApiKeyId Empty = new AgentApiKeyId(0);
    
            public bool Equals(AgentApiKeyId other) => this.Value.Equals(other.Value);
            public override bool Equals(object obj)
            {
                if (ReferenceEquals(null, obj)) return false;
                return obj is AgentApiKeyId other && Equals(other);
            }
    
            public override int GetHashCode() => Value.GetHashCode();
    
            public override string ToString() => Value.ToString();
            public static bool operator ==(AgentApiKeyId a, AgentApiKeyId b) => a.Equals(b);
            public static bool operator !=(AgentApiKeyId a, AgentApiKeyId b) => !(a == b);
            public int CompareTo(AgentApiKeyId other) => Value.CompareTo(other.Value);
    
            public class EfCoreValueConverter : Microsoft.EntityFrameworkCore.Storage.ValueConversion.ValueConverter<AgentApiKeyId, int>
            {
                public EfCoreValueConverter() : this(null) { }
                public EfCoreValueConverter(Microsoft.EntityFrameworkCore.Storage.ValueConversion.ConverterMappingHints mappingHints = null)
                    : base(
                        id => id.Value,
                        value => new AgentApiKeyId(value),
                        mappingHints
                    ) { }
            }
    
            class AgentApiKeyIdTypeConverter : System.ComponentModel.TypeConverter
            {
                public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Type sourceType)
                {
                    return sourceType == typeof(int) || sourceType == typeof(string) || base.CanConvertFrom(context, sourceType);
                }
    
                public override object ConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
                {
                    return value switch
                    {
                        int intValue => new AgentApiKeyId(intValue),
                        string stringValue when !string.IsNullOrEmpty(stringValue) && int.TryParse(stringValue, out var result) => new AgentApiKeyId(result),
                        _ => base.ConvertFrom(context, culture, value),
                    };
                }
    
                public override bool CanConvertTo(System.ComponentModel.ITypeDescriptorContext context, System.Type sourceType)
                {
                    return sourceType == typeof(int) || sourceType == typeof(string) || base.CanConvertTo(context, sourceType);
                }
    
                public override object ConvertTo(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, System.Type destinationType)
                {
                    if (value is AgentApiKeyId idValue)
                    {
                        if (destinationType == typeof(int))
                        {
                            return idValue.Value;
                        }
    
                        if (destinationType == typeof(string))
                        {
                            return idValue.Value.ToString();
                        }
                    }
    
                    return base.ConvertTo(context, culture, value, destinationType);
                }
            }
    
            class AgentApiKeyIdSystemTextJsonConverter : System.Text.Json.Serialization.JsonConverter<AgentApiKeyId>
            {
                public override AgentApiKeyId Read(ref System.Text.Json.Utf8JsonReader reader, System.Type typeToConvert, System.Text.Json.JsonSerializerOptions options)
                {
                    return new AgentApiKeyId(reader.GetInt32());
                }
    
                public override void Write(System.Text.Json.Utf8JsonWriter writer, AgentApiKeyId value, System.Text.Json.JsonSerializerOptions options)
                {
                    writer.WriteNumberValue(value.Value);
                }
            }
        }
    }
    
    

    It seems like struct's are not correctly processed when generating the api-definition. Is this something that can be fixed?

  • User Avatar
    1
    liangshiwei created
    Support Team Fullstack Developer

    It seems like struct's are not correctly processed when generating the api-definition.

    It looks like this. I will investigate it.

  • User Avatar
    0
    liangshiwei created
    Support Team Fullstack Developer

    Hi,

    I could not reproduce the problem.

    Could you share a simple test project? My email is shiwei.liang@volosoft.com I will check it

  • User Avatar
    0
    liangshiwei created
    Support Team Fullstack Developer

    It looks like an issue with StronglyTypedId or ASP.NET Core

    In the API definition provided by openapi, the type of the parameter is null.

    You can try this temporary solution:

    [Dependency(ReplaceServices = true)]
    [ExposeServices(typeof(AbpApiDefinitionController))]
    public class MyAbpApiDefinitionController : AbpApiDefinitionController
    {
        public MyAbpApiDefinitionController(IApiDescriptionModelProvider modelProvider) : base(modelProvider)
        {
        }
    
        public override ApplicationApiDescriptionModel Get(ApplicationApiDescriptionModelRequestDto model)
        {
            return RemoveNullParameters(base.Get(model));
        }
    
        public ApplicationApiDescriptionModel RemoveNullParameters(ApplicationApiDescriptionModel model)
        {
            foreach (var action in 
                     model.Modules.Select(x => x.Value)
                         .Select(api => api.Controllers).SelectMany(controllerApi => controllerApi.Values)
                         .SelectMany(controller => controller.Actions.Values))
            {
    
                action.Parameters.RemoveAll(x => x.Type == null && x.TypeSimple == null);
                action.ParametersOnMethod.RemoveAll(x => x.Type == null && x.TypeSimple == null);
            }
    
            return model;
        }
    }
    
  • User Avatar
    0
    MichelZ created

    It must be something with ASP.NET Core then (or the way ABP uses the information), you can get rid of StronglyTypedId package / code and just use this for AgentId:

    namespace Repro.ProxyGeneration.Models.Test
    {
        public struct AgentId
        {
            public int Value { get; }
    
            public AgentId(int value)
            {
                Value = value;
            }
        }
    }
    
    

    This leads to the same issue. The workaround seems to work and my proxies do get generated again, thank you

  • User Avatar
    0
    liangshiwei created
    Support Team Fullstack Developer

    : )

  • User Avatar
    0
    MichelZ created

    Going further with this, I have tried it using just ASP.NET Core, and the type seems to be represented just fine in ApiExplorer with this. I'll send you the project

  • User Avatar
    0
    MichelZ created

    Going further with this, I have tried it using just ASP.NET Core, and the type seems to be represented just fine in ApiExplorer with this. I'll send you the project

    Actually, going further it seems the issue is not class vs struct, but that the type is used in a route path, e.g:

            [HttpPost("/class/{testClass}")]
            public ActionResult FromTestClass(TestClass testClass)
            {
                return NoContent();
            }
    

    This produces 2 parameters, one for the route {testClass} with no Type and one for the function itself with the correct type info.

    So this seems to be the culprit here

  • User Avatar
    0
    liangshiwei created
    Support Team Fullstack Developer

    This produces 2 parameters, one for the route {testClass} with no Type and one for the function itself with the correct type info. So this seems to be the culprit here

    Yes, I think this may be related to ASP.NET Core

Made with ❤️ on ABP v9.1.0-preview. Updated on November 11, 2024, 11:11