Changing app key when migrating to Cloud

Hi! We’re working on adding support for Cloud migration to our app. We’re trying to use the recommended atlassian-app-cloud-migration-listener method.

We have multiple private apps that are “forks” of our main Cloud app, which we use for development and testing. (Each has its own app key.) So we wanted to make the Server app sync to one of those until we finish testing everything. (The “production” version of the Cloud app has the same key as the Server app, but we don’t want to use that one during development.)

The DiscoverableListener has a method called getCloudAppKey which looks like it should work for this. We return one of the dev apps’ key here. But the problem is that that method is never called, whatever we do.

In the Prepare your apps step it seems that the getServerAppKey method is called, but it does not seem what that one returns. (We tried returning null, the current server app key, a non-existent app key, and the desired cloud app key.) Regardless of what we return, the getCloudAppKey method is never called, the migration assistant says that the app is installed on Cloud, and if we try to create a migration the app is never visible.

So, is there any way of doing this, or do we need to fork the server app as well?

Hi @bbutnaru,

Thanks for your message. Let me try and answer it for you.

It sounds like the Listener is exported already as you mention getServerAppKey() is called in the Prepare your apps section. I suggest making sure you’re using Atlassian Spring Scanner 2 as a minimum, and having the Listener annotated as

public class MyDiscoverableListener implements DiscoverableListener {

Use dev-mode as this will ensure that the Listener is called regardless of its status in App Assessment stage.

As you already have a server app key, and it sounds like you have it registered in the Marketplace Migration API then this information may override your app key values in the Listener for App Assessment.

Along with dev-mode, I suggest that you use a new server and cloud app key (so yes, fork the code just for development), put them in getServerAppKey() and getCloudAppKey(). This will ensure that your development code will be called. Then when you’re ready to start production you can privately release a new version of the cloud app for testing.

I hope this helps.



Hi @jrichards,

Thanks a lot for the hints! Sorry for replying late, I was busy actually using your answer :slight_smile:

  • Yes, our listener is working, we’ve been using the Spring Scanner 2 thing.
  • Thanks for reminding me about the dev mode, I had forgotten to enable it. With it I could indeed trigger app migrations and do a big part of the migration code :partying_face: (Still having some trouble with some mappings, but that’s for another question.)
  • All the dev work we did until here was with forked versions of both the client and server apps. I’m not sure getServerAppKey() and getCloudAppKey() actually do anything, or if they do I have no idea what. I couldn’t get anything to work until they were both identical, matching the forks’ key.

The reward for a job well done is traditionally a harder job, so since you’ve helped a bunch I’ll ask some more on the same subject.

  1. We’d like to be able to test the Assess and Prepare steps of the migrations, without clients being able to see our partial progress until we’re ready. This would help make sure that we understand all that the clients will see.
  2. It would be very convenient during development to be able to migrate from the real server app (with the already-existing key) to a dev-only cloud app (the forked one, with a different key). We have a lot of testing data that is tied to the production app that would be pretty hard to migrate to a dev-only fork.

I couldn’t figure out a way of doing either of these using the DiscoverableListener API. I’m wondering if the Marketplace Migration API (MMAPI) would help.

What I would like to do is to send to the MMAPI for our public app’s key a set of AppMigrationInformation that uses the development fork of the cloud app as the cloudAddonKey and sets cloudVersionAvailability to PRIVATE. (I’m not exactly sure what the other properties do, but they look like they should be mostly optional.)

I would hope that this should allow us to:

  1. do the Assess & Prepare steps while dev mode is enabled, without actually showing anything to any customers that are testing migrations;
  2. migrate from the “public” server key to the “private” cloud key;
  3. change the cloudAddonKey to the “public” app key when we’re ready to update the cloud app, without making it visible to clients;
  4. then change the cloudVersionAvailability to PUBLIC when we finished testing and published the new server version, to make everything visible to clients.

The problem here is that most of that is guessing based on property names and imagining how I’d do an API, I’m worried that I might guess wrong and somehow get stuck with an app that claims to clients it can do migrations but doesn’t, and being unable to convince Marketplace to change its mind.

Could you maybe confirm/disconfirm if/what of my “hopes” above are correct? Anything I got even a tiny bit wrong?

(Also, this feels like too much to ask, but would it be plausible to get some really pedantly detailed documentation of what each property in the MMAPI’s AppMigrationInformation object does, how it interacts with the presence/absence of the listener in the server app, and who can see what and when?)

Hi @bbutnaru,

Thanks for the detailed post. I’ve been trying to get something along the lines of what you want working (with public Server app and private Cloud app) and I don’t think it’s possible at the moment. The best I can suggest is you create both private Server and Cloud entries in Marketplace with new app keys. I can’t see a way you can safely hide the /migration endpoint information from App Assessment while still using the public Server app key.

In terms of pedantic documentation, what I have found is

  1. App Assessment will use the Server app’s plugin key to get the list of installed apps on Server
  2. App Assessment will calculate the migration path using the plugin key to poll Marketplace for the /migration data and then use the plugin key to look for a registered listener.
  3. For registered listeners, App Assessment will then use getServerAppKey() to build a list of apps to call that are needed in cloud (or all apps in dev-mode is on). (for non-registered listener then they are core data only apps and don’t need a listener.)
  4. On the App Assessment Consent screen, the plugin key is used to poll the listener to get the list of scopes for consent. (If the plugin key doesn’t match getServerAppKey() then this is where you’ll have problems.)
  5. On App Assessment Install page, the plugin key is used to check marketplace for migration information. If there is a listener present, use the value from getCloudAppKey() to see if the Cloud version is installed otherwise use the cloudAppKey from the /migration endpoint to check if a Cloud app is installed.

In summary, make sure the Server plugin app key is the same as what getServerAppKey() returns or there will be a mismatch in App Assessment. If you provide a value for getCloudAppKey() that will be used to check if the app is installed in Cloud instead of what the /migration endpoint returns. (And this may all change in the future.)

With DiscoverableListener, if getServerAppKey() isn’t provided, then it’s calculated from the plugin key. I don’t think cloudVersionAvailability will hide the entry for you. What is means (afaik) is that it isn’t returned in search results.

I hope this helps.


Hi James, thank you very much for your efforts!

I’ll keep working on this, the hints look like they will help. I’ll post more when I get more done. For now, the part that confuses me most is that getServerAppKey() method:

Why even have that as part of the API, if you can return null and it just works (because the caller already knows the plugin key), the only other accepted value is the plugin key (that the caller already knows), and returning anything else is considered invalid (because the caller knows what it should have been)?!

(I get that sometimes APIs cannot be perfectly designed from the beginning and there are good reasons not to change them after, but I’d have expected at an explanation in the API docs, and maybe even marking the method as deprecated.)

I couldn’t actually get this to work, but I haven’t yet sent anything to the /migration endpoint, that might explain it. I’ll let you know when I have tested more carefully.

1 Like

HI @bbutnaru,

I’m going to correct something I wrote, because I think our internal documentation was a bit wrong. Usually, Server apps have a bundle key which is their OSGi bundle name, which is quite often their groupId and artifactId from the pom.xml or other build tool.

If you don’t provide getServerAppKey() then the DiscoverableListener will use the OSGi bundle name as the server app key. So, if your server app key is not the same as the OSGi bundle name, then you should return it in getServerAppKey().

If you don’t have anything in the Marketplace /migration endpoint and you have a registered listener then not much will work with App Assessment because it can’t calculate any of the required values.


Hi James, thanks for the update!

I’m sure I tried exactly this when I did my testing earlier (changing the app key without renaming the OSGi bundle), and I couldn’t get it to work then. (You might have noticed a tiny bit of frustration about it in my last message, sorry about that :face_with_hand_over_mouth:)

But I was much more confused about how things should work, I tried a lot of stuff, and it’s very likely that something else was the problem, such as the missing /migration info. I’m working on something else for a while, but I’ll try it again when I get back to migrations and I’ll report if I manage to make it work. Thanks again!

1 Like