Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add @Multipart/@Part Retrofit lint updates #320

Merged
merged 3 commits into from
Oct 14, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -55,18 +55,25 @@ class RetrofitUsageDetector : Detector(), SourceCodeScanner {
val annotationsByFqcn = node.uAnnotations.associateBy { it.qualifiedName }

val isFormUrlEncoded = FQCN_FORM_ENCODED in annotationsByFqcn
val isMultipart = FQCN_MULTIPART in annotationsByFqcn
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit before you merge: move this down to the line before the if statement that uses it

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

resolved in 6161a32


if (isFormUrlEncoded && !isBodyMethod) {
node.report("@FormUrlEncoded requires @PUT, @POST, or @PATCH.")
return
}

if (isMultipart && !isBodyMethod) {
node.report("@Multipart requires @PUT, @POST, or @PATCH.")
return
}

val hasPath =
(httpAnnotation.findDeclaredAttributeValue("value")?.evaluate() as? String)?.isNotBlank()
?: false

var hasBodyParam = false
var hasFieldParams = false
var hasPartParams = false
var hasUrlParam = false

for (parameter in node.uastParameters) {
Expand Down Expand Up @@ -101,6 +108,12 @@ class RetrofitUsageDetector : Detector(), SourceCodeScanner {
} else {
hasUrlParam = true
}
} else if (parameter.hasAnnotation(FQCN_PART)) {
if (!isBodyMethod) {
httpAnnotation.report("@Part param requires @PUT, @POST, or @PATCH.")
} else {
hasPartParams = true
}
}
}

Expand All @@ -112,7 +125,13 @@ class RetrofitUsageDetector : Detector(), SourceCodeScanner {
quickFixData = LintFix.create().removeNode(context, annotation.sourcePsiElement!!),
)
}
} else if (isBodyMethod && !hasBodyParam && !hasFieldParams) {
} else if (isMultipart) {
if (hasBodyParam || hasFieldParams) {
httpAnnotation.report("@Multipart methods should only contain @Part parameters.")
} else if (!hasPartParams) {
httpAnnotation.report("@Multipart methods should contain at least one @Part parameter.")
}
} else if (isBodyMethod && !hasBodyParam && !hasFieldParams && !hasPartParams) {
httpAnnotation.report("This annotation requires an `@Body` parameter.")
}
if (!hasPath && !hasUrlParam) {
Expand Down Expand Up @@ -144,7 +163,9 @@ class RetrofitUsageDetector : Detector(), SourceCodeScanner {
private val HTTP_BODY_ANNOTATIONS =
setOf("retrofit2.http.PATCH", "retrofit2.http.POST", "retrofit2.http.PUT")
private const val FQCN_FORM_ENCODED = "retrofit2.http.FormUrlEncoded"
private const val FQCN_MULTIPART = "retrofit2.http.Multipart"
private const val FQCN_FIELD = "retrofit2.http.Field"
private const val FQCN_PART = "retrofit2.http.Part"
private const val FQCN_FIELD_MAP = "retrofit2.http.FieldMap"
private const val FQCN_BODY = "retrofit2.http.Body"
private const val FQCN_URL = "retrofit2.http.Url"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ class RetrofitUsageDetectorTest : BaseSlackLintTest() {

import retrofit2.http.Body
import retrofit2.http.GET
import retrofit2.http.Multipart
import retrofit2.http.Part
import retrofit2.http.POST

interface Example {
Expand All @@ -104,6 +106,22 @@ class RetrofitUsageDetectorTest : BaseSlackLintTest() {

@POST("/")
fun correct(@Body input: String): String

@Multipart
@POST("/")
fun multipartCorrect(@Part input: String): String

@Multipart
@GET("/")
fun multipartBadMethod(@Part input: String): String

@Multipart
@POST("/")
fun multipartBadParameterType(@Body input: String): String

@Multipart
@POST("/")
fun multipartMissingPartParameter(): String
}
"""
)
Expand All @@ -112,17 +130,26 @@ class RetrofitUsageDetectorTest : BaseSlackLintTest() {
.run()
.expect(
"""
src/test/Example.kt:8: Error: @Body param requires @PUT, @POST, or @PATCH. [RetrofitUsage]
@GET("/")
~~~~~~~~~
src/test/Example.kt:11: Error: This annotation requires an @Body parameter. [RetrofitUsage]
@POST("/")
~~~~~~~~~~
src/test/Example.kt:15: Error: Duplicate @Body param!. [RetrofitUsage]
fun doubleBody(@Body input: String, @Body input2: String): String
~~~~~~~~~~~~~~~~~~~~
3 errors, 0 warnings
"""
src/test/Example.kt:10: Error: @Body param requires @PUT, @POST, or @PATCH. [RetrofitUsage]
@GET("/")
~~~~~~~~~
src/test/Example.kt:13: Error: This annotation requires an @Body parameter. [RetrofitUsage]
@POST("/")
~~~~~~~~~~
src/test/Example.kt:17: Error: Duplicate @Body param!. [RetrofitUsage]
fun doubleBody(@Body input: String, @Body input2: String): String
~~~~~~~~~~~~~~~~~~~~
src/test/Example.kt:28: Error: @Multipart requires @PUT, @POST, or @PATCH. [RetrofitUsage]
fun multipartBadMethod(@Part input: String): String
~~~~~~~~~~~~~~~~~~
src/test/Example.kt:31: Error: @Multipart methods should only contain @Part parameters. [RetrofitUsage]
@POST("/")
~~~~~~~~~~
src/test/Example.kt:35: Error: @Multipart methods should contain at least one @Part parameter. [RetrofitUsage]
@POST("/")
~~~~~~~~~~
6 errors, 0 warnings
"""
.trimIndent()
)
}
Expand Down
Loading