class

Analyzer::CSharp::Abp

Inherits Analyzer::CSharp::Common < Analyzer < FileHelper < Reference < Object

ABP Framework conventional ("auto API") controllers. ABP turns every application service in an assembly registered with options.ConventionalControllers.Create(typeof(XModule).Assembly) into an MVC controller, so an ABP app has almost no hand-written controllers and the attribute-routed ASP.NET analyzer sees none of its API.

The route mirrors ABP's ConventionalRouteBuilder:

/api/<rootPath>/<service>[/{id}][/<action>[/{xId}]]

rootPath defaults to app; <service> is the class name minus its AppService/ApplicationService/Service postfix, kebab-cased; the verb comes from the method-name prefix (HttpMethodHelper.ConventionalPrefixes) and whatever is left of the name becomes the <action> segment.

Services in a project no Create call covers are left alone: ABP modules ship hand-written controllers for their app services, and reporting the conventional route as well would invent a second, unreachable endpoint.

Constants

ABP_DTO_FIELDS = {"PagedAndSortedResultRequestDto" => ["Sorting", "SkipCount", "MaxResultCount"] of ::String, "ExtensiblePagedAndSortedResultRequestDto" => ["Sorting", "SkipCount", "MaxResultCount"] of ::String, "PagedResultRequestDto" => ["SkipCount", "MaxResultCount"] of ::String, "ExtensiblePagedResultRequestDto" => ["SkipCount", "MaxResultCount"] of ::String, "LimitedResultRequestDto" => ["MaxResultCount"] of ::String, "EntityDto" => ["Id"] of ::String}

Request DTO bases from Volo.Abp.Application.Dtos, which are never in the scanned project.

CONTROLLER_POSTFIXES = ["AppService", "ApplicationService", "IntegrationService", "Service"] of ::String

ApplicationService.CommonPostfixes, in ABP's removal order.

CREATE_CALL_RE = /ConventionalControllers\s*\.\s*Create\s*\(\s*typeof\s*\(\s*(?:global::)?(?:\w+\.)*(\w+)\s*\)/
CRUD_BASE_RE = /\b(Crud|ReadOnly|AbstractKeyCrud|AbstractKeyReadOnly)AppService\s*</
CRUD_SHAPES = {3 => {2, nil, 1, 1}, 4 => {2, 3, 1, 1}, 5 => {2, 3, 4, 4}, 6 => {2, 3, 4, 5}, 7 => {3, 4, 5, 6}}

Generic-argument positions per arity: {key, list input, create, update} (nil list input = ABP's PagedAndSortedResultRequestDto default).

ENUM_DECL_RE = /\benum\s+(\w+)/
HTTP_ATTR_RE = /\bHttp(Get|Post|Put|Delete|Patch|Head|Options)(?:Attribute)?\s*(?:\(\s*@?"([^"]*)")?/
METHOD_RE = /^\s*public\s+(?:(?:virtual|override|async|new|sealed)\s+)*(?!static\b|abstract\b|class\b|record\b|struct\b|interface\b|enum\b|event\b|const\b|delegate\b)(?:[\w<>\[\],.?\s]|\([^()]*\))+?\s+(\w+)\s*(?:<[^()]*>)?\s*\(/

The return type may be a tuple: Task<(int Count, string Name)>.

NO_BODY_VERBS = Set {"GET", "DELETE", "HEAD", "TRACE"}

Verbs whose complex parameters ABP does not bind from the body.

NON_ACTION_RE = /\[\s*NonAction\b/
POSITIONAL_RECORD_RE = /\brecord\s+(?:class\s+|struct\s+)?(\w+)\s*\(([^)]*)\)/
PRIMITIVE_TYPES = Set {"string", "int", "long", "short", "byte", "sbyte", "uint", "ulong", "ushort", "bool", "char", "decimal", "double", "float", "Guid", "DateTime", "DateTimeOffset", "TimeSpan", "String", "Int32", "Int64", "Boolean"}
READ_ONLY_SHAPES = {3 => {2, nil}, 4 => {2, 3}, 5 => {3, 4}}
REMOTE_OFF_RE = /\bRemoteService(?:Attribute)?\s*\(\s*(?:isEnabled\s*:\s*)?false\b|\bRemoteService(?:Attribute)?\s*\([^)]*\bIsEnabled\s*=\s*false\b/
ROOT_PATH_RE = /\bRootPath\s*=\s*@?"([^"]*)"/
ROUTE_ATTR_RE = /\bRoute(?:Attribute)?\s*\(\s*@?"([^"]*)"/
SERVICE_INTERFACE_RE = /\AI\w*(?:AppService|ApplicationService)\z/
SERVICE_ROOTS = Set {"ApplicationService", "CrudAppService", "ReadOnlyAppService", "AbstractKeyCrudAppService", "AbstractKeyReadOnlyAppService", "IApplicationService", "IRemoteService"}

Framework bases (and the marker interfaces) that make a class an application service.

SOURCE_GATE = /AppService|ApplicationService|ConventionalControllers|AbpModule/
VERB_PREFIXES = {"GET" => ["GetList", "GetAll", "Get"] of ::String, "PUT" => ["Put", "Update"] of ::String, "DELETE" => ["Delete", "Remove"] of ::String, "POST" => ["Create", "Add", "Insert", "Post"] of ::String, "PATCH" => ["Patch"] of ::String}

HttpMethodHelper.ConventionalPrefixes; the verb test is a case-insensitive StartsWith, anything unmatched is POST.

Class methods

tech_name
Source

Instance methods

analyze
Source
tech

Instance-side view of the same declaration. The per-file rescues live on this base class, which has no way to name the analyzer that is running inside them, so a skipped file could not be attributed to a tech. Deriving it from analyzer_for keeps the name written exactly once.

Source