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 support for promoteId #636

Merged
merged 1 commit into from
Sep 22, 2021
Merged

Add support for promoteId #636

merged 1 commit into from
Sep 22, 2021

Conversation

ank27
Copy link
Contributor

@ank27 ank27 commented Sep 13, 2021

PRs must be submitted under the terms of our Contributor License Agreement CLA.
Fixes: < Link to related issues that will be fixed by this pull request, if they exist >

Pull request checklist:

  • Briefly describe the changes in this PR.
  • Include before/after visuals or gifs if this PR includes visual changes.
  • Write tests for all new functionality. If tests were not written, please explain why.
  • Add example if relevant.
  • Document any changes to public APIs.
  • Apply changelog label ('breaking change', 'bug 🪲', 'build', 'docs', 'feature 🍏', 'performance ⚡', 'testing 💯') or use the label 'skip changelog'
  • Add an entry inside this element for inclusion in the mapbox-maps-android changelog: <changelog>Add support for PromoteId to be used with Feature state API.</changelog>.

Summary of changes

This PR adds support for promoteId which allows a developer to promote a particular string / source-property to be the feature state id in features.

promoteId is a feature for geojson and vector sources. The feature allows to promote feature's property to a feature id, so that promoted id can be used with FeatureState API.

@ank27 ank27 requested a review from a team September 13, 2021 16:52
Copy link
Contributor

@Chaoba Chaoba left a comment

Choose a reason for hiding this comment

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

LGTM

*/
@Keep
data class PromoteId @JvmOverloads constructor(
val sourceId: String? = null,
Copy link
Contributor

@kiryldz kiryldz Sep 14, 2021

Choose a reason for hiding this comment

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

if this param is optional - shouldn't it be actually second one? guess it will make usage easier (e.g. not use named arguments below in static method)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

i did it the same way initially, but then aligned it with the docs here
https://docs.mapbox.com/mapbox-gl-js/style-spec/sources/#vector-promoteId.
Either a property name, or an object of the form {<sourceLayer>: <propertyName>}

But definitely its better to use optional param as last args.

}
is HashMap<*, *> -> {
@Suppress("UNCHECKED_CAST")
val propertyMap = propertyName as HashMap<String, String>
Copy link
Contributor

Choose a reason for hiding this comment

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

what if it's not HashMap<String, String>? Will we have runtime crash? Perhaps use try-catch here to produce better exception text

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure how it works, could check like:

is HashMap<String, String> -> {

instead of

is HashMap<*, *> -> {

work here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Using HashMap<String,String> gives compile time error.
Cannot check for instance of erased type: kotlin.collections.HashMap<String, String> /* = java.util.HashMap<String, String>

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I will add more try{} catch{} for hashmap, but the args for this function returned from core in either string or map<string,string>.

val sourceId: String? = null,
val propertyName: String
) {
internal fun toValue(): Value {
Copy link
Contributor

Choose a reason for hiding this comment

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

Is it used in tests only?
if yes - do we need this function at all here? we could add it as extension function in tests or do smth similar

Copy link
Contributor Author

Choose a reason for hiding this comment

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

it also used in Builder methods of source in order to add the properties to source.

* @param sourceId source layer id of the feature, either source [GeoJsonSource] or [VectorSource].
* @param propertyName feature property name.
*
* For more information see [The online documentation]https://docs.mapbox.com/mapbox-gl-js/style-spec/types/#promoteId).
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
* For more information see [The online documentation]https://docs.mapbox.com/mapbox-gl-js/style-spec/types/#promoteId).
* For more information see [The online documentation](https://docs.mapbox.com/mapbox-gl-js/style-spec/types/#promoteId).

And also not sure this will work as expected in real generated docs

Copy link
Contributor

Choose a reason for hiding this comment

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

Also link does not lead me to promote id...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, the link is broken from gl-js side, I have asked @alexshalamov if that will be fixed or i will remove from here :)

/**
* Holds a property type to promote a specific feature for feature state API.
*
* @param sourceId source layer id of the feature, either source [GeoJsonSource] or [VectorSource].
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we have a check for that?

Copy link
Contributor Author

@ank27 ank27 Sep 14, 2021

Choose a reason for hiding this comment

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

promoteId is only available for GeoJson or vector source on builder method so we don't need to check here.

But if you are asking that if we initialize promoteId with the wrong sourceId and provide that to geojson builder -> IMO it should be handled by core itself, and shouldn't be applied to feature state, but I will test it.

is HashMap<*, *> -> {
@Suppress("UNCHECKED_CAST")
val propertyMap = propertyName as HashMap<String, String>
val key = propertyMap.keys.iterator().next()
Copy link
Contributor

Choose a reason for hiding this comment

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

what if we pass an empty hash map? 😄

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fair point, will add a check here. :D

companion object {
/**
* Construct a [PromoteId] object from a Property returned from the core.
* Can be either of type [String] or [HashMap] of <String,String>
Copy link
Contributor

Choose a reason for hiding this comment

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

Specify @throws as well as @param

* Construct a [PromoteId] object from a Property returned from the core.
* Can be either of type [String] or [HashMap] of <String,String>
*/
fun fromProperty(propertyName: Any) = when (propertyName) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Could you add tests for this method and cover all scenarios?

Copy link
Member

@pengdev pengdev Sep 21, 2021

Choose a reason for hiding this comment

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

I think we could split this function to two functions to make the API signature strongly typed, since we only support two types of data. i.e. fromProperty(propertyName: String) and fromProperty(propertyMap: Map<String, String>).

Thinking again, why would we need to have this method? The promoteId could either a property name, or an object of the form {: }, so the constructor should be well enough for this purpose. cc @ank27

Copy link
Member

Choose a reason for hiding this comment

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

If it's purely used to deserialise from a Value object, I think it's better to use fun fromValue(value: Value)

Copy link
Member

Choose a reason for hiding this comment

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

and nit: could be internal.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think we could split this function to two functions to make the API signature strongly typed, since we only support two types of data. i.e. fromProperty(propertyName: String) and fromProperty(propertyMap: Map<String, String>).

Thinking again, why would we need to have this method? The promoteId could either a property name, or an object of the form {: }, so the constructor should be well enough for this purpose. cc @ank27

It was made this way to handle the errors in case core provides other type formats.
hmm, I see that we can use Value as it is already wrapper for type.

PromoteId.fromProperty(hashMapOf(1 to 2))
}

internal data class MockType(val a: String, val b: String)
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
internal data class MockType(val a: String, val b: String)
private data class MockType(val a: String, val b: String)

throw RuntimeException("$propertyName must be in the format HashMap<String,String>")
}
}
else -> throw RuntimeException("Wrapping ${propertyName::class.java.simpleName} to PromoteId is not supported.")
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
else -> throw RuntimeException("Wrapping ${propertyName::class.java.simpleName} to PromoteId is not supported.")
else -> throw UnsupportedTypeException("Wrapping ${propertyName::class.java.simpleName} to PromoteId is not supported.")

import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner

@RunWith(RobolectricTestRunner::class)
Copy link
Contributor

Choose a reason for hiding this comment

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

do really need this runner?


@Test
fun fromProperty_HashMap() {
val expected = PromoteId("abc", "source")
Copy link
Contributor

Choose a reason for hiding this comment

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

nice to add one more test PromoteId("abc", null)

Copy link
Contributor

Choose a reason for hiding this comment

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

and PromoteId("", "source")

throw RuntimeException("$propertyName must be in the format HashMap<String,String>")
}
}
else -> throw UnsupportedOperationException("Wrapping ${propertyName::class.java.simpleName} to PromoteId is not supported.")
Copy link
Contributor

Choose a reason for hiding this comment

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

can we somehow check this exception?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

added tests for this

@ank27 ank27 force-pushed the ak-promote-id branch 2 times, most recently from 48c13ce to fe59c97 Compare September 21, 2021 17:45
@ank27 ank27 requested a review from pengdev September 21, 2021 17:46
* source, the same property is used across all its source layers.
*/
fun promoteId(value: PromoteId) = apply {
val propertyValue = PropertyValue("promoteId", TypeUtils.wrapToValue(value.toValue()))
Copy link
Member

Choose a reason for hiding this comment

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

Nit: do we still need wrapToValue since toValue() should have been Value type already?

Copy link
Member

@pengdev pengdev left a comment

Choose a reason for hiding this comment

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

LGTM with a nit

@ank27 ank27 merged commit b3e6017 into main Sep 22, 2021
@ank27 ank27 deleted the ak-promote-id branch September 22, 2021 08:14
@ank27 ank27 self-assigned this Sep 23, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants