- 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)
-
0
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
-
0
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.
-
0
Hi,
Above stack overflow discussion is about 2 scenarios
- If there is no model/service then also you are trying to generate proxy
- If you are targeting to live API address instead of local one
If these are not the case then please check
- Check your ABP Framework and CLI version are same or not?
- or once run
npm install
to install all the necessary dependencies and then try
-
0
Hi,
Above stack overflow discussion is about 2 scenarios
- If there is no model/service then also you are trying to generate proxy
- If you are targeting to live API address instead of local one
- We do have services of course
- 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
- Check your ABP Framework and CLI version are same or not?
CLI is .1 newer (7.4.3 vs 7.4.2)
- 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.
-
0
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
-
0
We have tried with version 7.4.2 now, with the same result.
-
0
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?
-
0
Hi,
Could you please share your
package.json
file? -
0
{ "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" } }
-
0
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)
-
0
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?
-
1
It seems like struct's are not correctly processed when generating the api-definition.
It looks like this. I will investigate it.
-
0
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
-
0
It looks like an issue with
StronglyTypedId
orASP.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; } }
-
0
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
-
0
: )
-
0
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
-
0
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
-
0
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