Create new asset - groovy task

Hi,
I need to create a groovy task that scans the content of a raw repo and create a new asset in that repo that is a new file containing some info about the assets already existing in that repo (ex a PULP_MANIFEST file).

I was able to scan the repo and create the new file content and now I need to create a new asset with that content.
Below is my code that it is supposed to create a new dummy asset in a specified raw repo. It is not doing what I expect and it throws no exception.
Please, can someone take a look and explain what’s wrong? Regards.

import org.slf4j.LoggerFactory
import org.sonatype.nexus.common.collect.AttributesMap

import org.sonatype.nexus.common.hash.HashAlgorithm
import org.sonatype.nexus.repository.Repository
import org.sonatype.nexus.repository.storage.Asset
import org.sonatype.nexus.repository.storage.Bucket

import org.sonatype.nexus.repository.storage.StorageFacet
import org.sonatype.nexus.repository.storage.StorageTx

import org.sonatype.nexus.repository.storage.TempBlob
import org.sonatype.nexus.repository.view.Payload
import org.sonatype.nexus.repository.view.payloads.StringPayload
import org.sonatype.nexus.repository.view.Content

import org.sonatype.nexus.script.plugin.internal.provisioning.RepositoryApiImpl

def log = LoggerFactory.getLogger(NewAsset.class)
def repoName = “my_files”

new NewAsset(log, repository, repoName).createDummy()

class NewAsset {
private final log
private final RepositoryApiImpl repo
private final String repoName

NewAsset(log, RepositoryApiImpl repo, String repoName) {
    this.log = log
    this.repo = repo
    this.repoName = repoName
}

Repository getRepository() {
    return this.repo.repositoryManager.get(this.repoName)
}

private void put(StorageTx tx, String path, Payload content) throws IOException {
    final List<HashAlgorithm> hashAlgorithms = Arrays.asList(HashAlgorithm.MD5, HashAlgorithm.SHA1, HashAlgorithm.SHA256)

    StorageFacet storageFacet = getRepository().facet(StorageFacet.class)
    TempBlob tempBlob = storageFacet.createTempBlob(content, hashAlgorithms)
    doPutContent(tx, path, tempBlob, content)
}

private void doPutContent(StorageTx tx, String path, TempBlob tempBlob, Payload payload) throws IOException {
    Asset asset = createAsset(tx, path)

    AttributesMap contentAttributes = null
    if (payload instanceof Content) {
        contentAttributes = ((Content) payload).getAttributes()
    }
    Content.applyToAsset(asset, Content.maintainLastModified(asset, contentAttributes))
    tx.setBlob(asset, path, tempBlob, null, payload.getContentType(), false)

    tx.saveAsset(asset)
}

private Asset createAsset(StorageTx tx, String assetName) {
    final Bucket bucket = tx.findBucket(getRepository())

    Asset asset = tx.createAsset(bucket, getRepository().getFormat())
    asset.name(assetName)

    asset.markAsDownloaded()

    return asset
}


void createDummy() throws IOException {
    StringPayload payload = new StringPayload("dummy text for now", "text/plain")
    final StorageFacet storageFacet = getRepository().facet(StorageFacet)
    final StorageTx tx = storageFacet.txSupplier().get()

    try {
        tx.begin()
        put(tx, "/pulp_manifest2", payload)
    } catch (Exception e) {
        log.info("Exception:", e.toString())
        tx.rollback()
    }
    finally {
        tx.close()
    }
}

}

Whenever possible I’d recommend using the REST APIs instead of groovy scripting. If there are REST APIs that you need that we have not provided please open a JIRA issue so we can add them at some point. In this case you would want to use POST /v1/components. The REST APIs are visible to logged-in administrators under System > API or at /swagger-ui/.

Note that depending on your repository manager version you may not have all the REST APIs. If not you are likely on an unpatched version of repository manager and you should strongly consider upgrading to get the latest security fixes.

If you need to continue the groovy scripting route, consider using the RawContentFacet.put(String path, Payload payload) method to simplify the above code significantly.

1 Like

Hi. Thanks for reply.

Is that the way you suggest? My repo is of raw hosted type. Nexus server is latest (3.24.0-02). When I ran it as a task I get this error in logs:

javax.script.ScriptException: javax.script.ScriptException: org.sonatype.nexus.repository.MissingFacetException: No facet of type RawContentFacet attached to repository ‘myRepo’

Looks like the error is from this code:

RawContentFacet facet = myRepo.facet(RawContentFacet.class)

The groovy code is:

import org.sonatype.nexus.content.raw.RawContentFacet
import org.sonatype.nexus.repository.Repository
import org.sonatype.nexus.repository.view.payloads.StringPayload

class AClass {
private final log
private final repo

AClass(log, repo) {
    this.log = log
    this.repo = repo
}

void doStuff() {
    String name = "myFile"
    String repoName = "myRepo" // of type raw hosted

    Repository myRepo = this.repo.repositoryManager.get(repoName)
    StringPayload content = new StringPayload("dummy text for now", null)

    RawContentFacet facet = myRepo.facet(RawContentFacet.class)
    facet.put(name, content)
}

}

new AClass(log, repository).doStuff()

Not sure why you aren’t seeing that facet. I can see that the content facet is assigned to hosted repos normally.

I still recommend using the REST APIs instead, we’ve disabled groovy scripting by default because it introduces security risks. The component and search apis should have everything you need and you won’t have problems with your script breaking when we make changes to our internal code. Groovy scripting should be a last-resort since we don’t support most things people do with the groovy scripts.

A possible problem is that with the current model paths do not internally start with a slash.

I would also agree with Martz, using Nexus internal APIs is inherently brittle and may break at any time - in particular we’re re-working persistence and formats in general, this is causing a lot of flux in the current APIs as we move code and in the longer term they will be removed in favour of new code.

Thanks for suggestions.

I have to sync a Foreman repo with a Nexus repo but Foreman requires a PULP_MANIFEST file in Nexus repo. The script I want to implement has to scan periodically the Nexus repo and generate in the same repo a PULP_MANIFEST file (based on the current Nexus repo state). This file content is based on the repo files metadata (file-name, file-sha256 and file-size).

So far, I was able to scan the Nexus repo in a groovy script and create the pulp manifest file content in Nexus /tmp folder. Now, I’m stuck with the part that creates (in the same repo) a new component/asset/file with the name and content of PULP_MANIFEST.
Basically, now I need the simplest groovy script that can run as a Nexus task and creates a new file in a raw hosted repo.

Let’s suppose I’ll do the same using REST API instead. First, I have to create a cron job that get metadata for each file in that repo and post a new file PULP_MANIFEST in that repo. Looking a few minutes in the REST API I see the /v1/assets response doesn’t contain the size of each asset.

In regard to my last example. Can someone, please, create a raw hosted repo named myRepo and run the code in a groovy task? Or point me to a working example.
I also understand that these scripts are related to the current internal API that can change in the future and they possible won’t work on next upgrades.

Thanks again for your support.

Hi,

I have managed to run the above code that throwed the exception: org.sonatype.nexus.repository.MissingFacetException: No facet of type RawContentFacet attached to repository ‘myRepo’.

I found that I had to import:

import org.sonatype.nexus.orient.raw.RawContentFacet

instead of:

import org.sonatype.nexus.content.raw.RawContentFacet

but now I get another runtime error:

java.lang.IllegalStateException: Unit of work has not been set

from org.sonatype.nexus.orient.raw.internal.RawContentFacetImpl
before executing:

@TransactionalStoreBlob
protected Content doPutContent(final String path, final TempBlob tempBlob, final Payload payload)
throws IOException

Can someone suggest what should be done? It must be something related to transactions. Thanks