
## Asset READMEs

URL: https://docs.atlan.com/product/capabilities/build-apps/sdks/python/how-tos/manage-readmes

> Manage asset READMEs programmatically using the Atlan Python SDK (pyatlan). Add, edit, and remove HTML-formatted documentation.

# Manage asset READMEs

READMEs can only be added to [assets](https://docs.atlan.com/llms/platform/python/build-your-first-metadata-workflow/llms.txt) after an asset exists. (The asset itself must be created first.)

:::tip[README content is written in HTML]
The content of a README needs to be HTML. The HTML should be everything that would be *inside* the `<body></body>` tags, but not include the `<body></body>` tags themselves. (So it should also exclude the outer `<html></html>` tags.)
:::

## Add to existing asset

Each README can be assigned to only a single asset. To create a README and assign it to an asset:

### dbt

```yaml showLineNumbers title="Add or edit README on an existing asset"
models:
 - name: TOP_BEVERAGE_USERS # (1)
 meta:
 atlan:
 readme: | # (2)
 # Overview
 This table was changed. 
 Add helpful context for **consumers** here in Markdown.
```

1. Provide the name of the object.
2. Put your README content (Markdown) under `meta.atlan.readme`. To edit, simply change the value.

### Java

```java showLineNumbers title="Add to an existing asset"
final String readmeContent = "

# Overview

Details about this term...
"; // (1)
GlossaryTerm term = GlossaryTerm.refByGuid("b4113341-251b-4adc-81fb-2420501c30e6") // (2)
 .toBuilder()
 .name("Example Term") // (3)
 .build();
Readme readme = Readme.creator( // (4)
 term, // (5)
 readmeContent)
 .build();
AssetMutationResponse response = readme.save(client); // (6)
assert response.getCreatedAssets().size() == 1 // (7)
assert response.getUpdatedAssets().size() == 1 // (8)
```

1. Pick up your HTML content from somewhere (here it's defined directly in the code).
2. The README must be attached to some asset. You could either first search for or retrieve that asset, or build up a reference directly (as in this example).
3. The asset you send to the README creation **must** have its name populated, not only its GUID or qualifiedName.
4. Use the `creator()` method to initialize the README with all [necessary attributes for creating it](https://docs.atlan.com/llms/platform/python/create-asset/llms.txt).
5. For a README, you need to send the asset to attach it to and the content for the README itself (the HTML).
6. Call the `save()` method to actually create the README and attach it to the asset. Because this operation will persist the asset in Atlan, you must [provide it an `AtlanClient`](https://docs.atlan.com/llms/platform/python/set-up-sdk/llms.txt) through which to connect to the tenant.
7. The response will include that single asset that was created (the README).
8. The response will also include a single asset that was updated (the asset to which we've attached the README).

### Python

```python showLineNumbers title="Add to an existing asset"
from pyatlan.client.atlan import AtlanClient
from pyatlan.model.assets import Readme, AtlasGlossaryTerm

client = AtlanClient()
content = "

# Overview

More Details about this term...
" # (1)
readme = Readme.creator( # (2)
 asset=AtlasGlossaryTerm.ref_by_guid(guid="b4113341-251b-4adc-81fb-2420501c30e6"), # (3)
 content=content, # (4)
 asset_name="Example Term") # (5)
response = client.asset.save(readme) # (6)
assert (readmes := response.assets_created(asset_type=Readme)) # (7)
assert (glossaries := response.assets_updated(asset_type=AtlasGlossaryTerm)) # (8)
```

1. Pick up your HTML content from somewhere (here it's defined directly in the code).
2. Use the `create()` method to initialize the README with all [necessary attributes for creating it](https://docs.atlan.com/llms/platform/python/create-asset/llms.txt).
3. We need to give the asset to attach the README to.
4. The content for the README itself (the HTML).
5. The name of the asset to which we want to attach the README.
 - Note: The name is only required because we're using the `ref_by_guid` method to create the `AtlasGlossaryTerm` consequently it won't have a name. If we had an asset we had previosly retrieved via a search or using the `asset.get_by_guid` method we could leave the `asset_name` parameter out and the name from the given `asset` would be used.
6. Call the `save()` method to actually create the README and attach it to the asset.
7. Assert that the README was created.
8. Assert a GlossaryTerm was updated (the asset to which we've attached the README).

### Kotlin

```kotlin showLineNumbers title="Add to an existing asset"
val readmeContent = "

# Overview

Details about this term...
" // (1)
val term = GlossaryTerm.refByGuid("b4113341-251b-4adc-81fb-2420501c30e6") // (2)
 .toBuilder()
 .name("Example Term") // (3)
 .build()
val readme = Readme.creator( // (4)
 term, // (5)
 readmeContent)
 .build()
val response = readme.save(client) // (6)
assert(response.createdAssets.size == 1) // (7)
assert(response.updatedAssets.size == 1) // (8)
```

1. Pick up your HTML content from somewhere (here it's defined directly in the code).
2. The README must be attached to some asset. You could either first search for or retrieve that asset, or build up a reference directly (as in this example).
3. The asset you send to the README creation **must** have its name populated, not only its GUID or qualifiedName.
4. Use the `creator()` method to initialize the README with all [necessary attributes for creating it](https://docs.atlan.com/llms/platform/python/create-asset/llms.txt).
5. For a README, you need to send the asset to attach it to and the content for the README itself (the HTML).
6. Call the `save()` method to actually create the README and attach it to the asset. Because this operation will persist the asset in Atlan, you must [provide it an `AtlanClient`](https://docs.atlan.com/llms/platform/python/set-up-sdk/llms.txt) through which to connect to the tenant.
7. The response will include that single asset that was created (the README).
8. The response will also include a single asset that was updated (the asset to which we've attached the README).

### Raw REST API

:::warning[Note that you are actually creating a new README asset]
When adding a README through the API, you are really creating a new instance of a README asset. At the same time, you're attaching this new object to an existing asset.
:::
```json showLineNumbers title="POST /api/meta/entity/bulk"
{
 "entities": [ // (1)
 }
 }
 ]
}
```

1. All assets must be wrapped in an `entities` array.
2. You must provide the exact type name for the README asset, which will always be `Readme` (case-sensitive).
3. You must also provide a name for the README. This won't show up on the UI, but should be a concatenation of the name of the asset the README will be attached to and ` Readme`.
4. You must also provide a unique `qualifiedName` for the README. This won't show up on the UI, but should be a concatenation of the GUID of the asset the README will be attached to and `/readme`.
5. The content of the README should be provided in the `description` field. Note that this must be HTML, which must further be url-encoded.

 :::tip[Use a library]
Most languages will provide a library to url-encode and url-decode strings. Use this, wherever possible. For an example of translating raw HTML into url-encoded form (or decoding an encoded form) you can also try [urlencoder.org](https://urlencoder.org) and [urldecoder.org](https://urldecoder.org), respectively.
 :::
6. Finally, you need to include the reference information for the asset the README should be attached to.

## Retrieve README from existing asset

To retrieve a README and its content for an existing asset:

### Java

```java showLineNumbers title="Retrieve README's content from an existing asset"
String termQn = "fb45981203221-atlan"; // (1)
var results = client.assets.select() // (2)
 .where(Asset.QUALIFIED_NAME.eq(termQn))
 .includeOnResults(Asset.README)
 .includeOnRelations(Readme.DESCRIPTION) 
 .stream()
 .toList();
System.out.println(results.get(0).getReadme().getDescription()); // (3)
```

1. Store the qualified name of the asset (GlossaryTerm) connected to the README in the termQn variable.
2. Configure the search to match the qualified name, include the README, and fetch its description.
3. Extract and print the README's content.

### Python

```python showLineNumbers title="Retrieve README's content from an existing asset"
from pyatlan.client.atlan import AtlanClient
from pyatlan.model.assets import AtlasGlossaryTerm, Readme
from pyatlan.model.fluent_search import CompoundQuery, FluentSearch

client = AtlanClient()

term_qn = "fb45981203221-atlan" # (1)

response = ( # (2)
 FluentSearch()
 .select()
 .where(CompoundQuery.asset_type(AtlasGlossaryTerm))
 .where(AtlasGlossaryTerm.QUALIFIED_NAME.eq(term_qn))
 .include_on_results(AtlasGlossaryTerm.README)
 .include_on_relations(Readme.DESCRIPTION)
 .execute(client=client)
)
if first := response.current_page(): 
 readme_content = first[0].readme.description # (3)
 print(readme_content)
```

1. Store the asset's qualified name in the term_qn variable.
2. Filter by asset type, match the qualified name, include the README, and fetch its description.
3. Extract and print the README's content.

### Kotlin

```kotlin showLineNumbers title="Retrieve README's content from an existing asset"
val assetQualifiedName = "fb45981203221-atlan" // (1)

val description = client.assets.select() // (2)
 .where(Asset.QUALIFIED_NAME.eq(assetQualifiedName))
 .includeOnResults(Asset.README) 
 .includeOnRelations(Readme.DESCRIPTION) 
 .stream()
 .toList()
 .firstOrNull()
 ?.readme 
 ?.description 
 ?: "README description not found."

println("README Description: $description") // (3)
```

1. Store the qualified name of the asset in the assetQualifiedName variable.
2. Search for the asset, include the README, and fetch its description.
3. Extract and print the README's content.

### Raw REST API

```json showLineNumbers title="GET /api/meta/entity/bulk?guid=b4113341-251b-4adc-81fb-2420501c30e6"
// (1)
```

1. When retrieving the README, you need to use the README's GUID, not the GUID of the asset to which it's attached.

## Update README attached to existing asset

To update a README and its content for an existing asset:

### Java

```java showLineNumbers title="Updating README's content"
String termQn = "fb45981203221-atlan"; // (1)
List assets = client.assets.select() 
 .where(Asset.QUALIFIED_NAME.eq(termQn))
 .includeOnResults(Asset.README)
 .includeOnRelations(Readme.DESCRIPTION)
 .includeOnRelations(Readme.NAME)
 .stream()
 .toList();

Asset asset = assets.get(0);
String newDescription = "
This is the updated README description
";
Readme updatedReadme = Readme.updater(asset.getGuid(), asset.getName()) //(2)
 .description(newDescription)
 .build();

AssetMutationResponse response = updatedReadme.save(client); // (3)
```

1. Store the qualified name of the asset (GlossaryTerm) connected to the README in the termQn variable.
2. Use Readme.updater() to update the README's description.
3. Save the updated README. Because this operation will persist the asset in Atlan, you must [provide it an `AtlanClient`](https://docs.atlan.com/llms/platform/python/set-up-sdk/llms.txt) through which to connect to the tenant.

### Python

```python showLineNumbers title="Updating README's content"
from pyatlan.client.atlan import AtlanClient
from pyatlan.model.assets import AtlasGlossaryTerm, Readme
from pyatlan.model.fluent_search import CompoundQuery, FluentSearch

client = AtlanClient()

term_qn = "fb45981203221-atlan" # (1)

response = ( 
 FluentSearch()
 .select()
 .where(CompoundQuery.asset_type(AtlasGlossaryTerm))
 .where(AtlasGlossaryTerm.QUALIFIED_NAME.eq(term_qn))
 .include_on_results(AtlasGlossaryTerm.README)
 .include_on_relations(Readme.DESCRIPTION)
 .execute(client=client)
)

if first := response.current_page(): 
 current_content = first[0].readme.description 
 updated_content = "
Added new information to the Readme.
"
 updated_readme = Readme.creator( # (2)
 asset=first[0],
 content=updated_content
 )
 save_response = client.asset.save(updated_readme) # (3)
```

1. Store the asset's qualified name in the term_qn variable.
2. Use Readme.creator() to create a new README for the same asset (AtlasGlossaryTerm).
3. Save the updated README.

### Kotlin

```kotlin showLineNumbers title="Updating README's content"
val assetQualifiedName = "fb45981203221-atlan" // (1)

val assets: List = client.assets.select()
 .where(Asset.QUALIFIED_NAME.eq(termQualifiedName))
 .includeOnResults(Asset.README) 
 .includeOnRelations(Readme.DESCRIPTION)
 .includeOnRelations(Readme.NAME)
 .stream()
 .toList()

val asset = assets.firstOrNull()

val newDescription = "
Final Changes
"
val updatedReadme = Readme.updater(asset.guid, asset.name) // (2)
 .description(newDescription)
 .build()

val response = updatedReadme.save(client) // (3)
```

1. Store the asset's qualified name in the assetQualifiedName variable.
2. Use Readme.updater() to update the README's description.
3. Save the updated README. Because this operation will persist the asset in Atlan, you must [provide it an `AtlanClient`](https://docs.atlan.com/llms/platform/python/set-up-sdk/llms.txt) through which to connect to the tenant.

### Raw REST API

```json showLineNumbers title=""
// (1)
```

1. REST API for updating a README isn't available

## Remove README from Existing Asset

To remove a README from an existing asset, delete the README itself. (A README is treated as a separate asset with its own GUID.)

To hard-delete (purge) a README, provide the README's GUID:

### dbt

```yaml showLineNumbers title="Remove README via dbt"
models:
 - name: TOP_BEVERAGE_USERS # (1)
 meta:
 atlan:
 # readme attribute removed to delete README (2)
```

1. Provide the name of the object.
2. To remove the README, delete the `readme` attribute from `meta.atlan`. No other changes are needed.

### Java

```java showLineNumbers title="Hard-delete (purge) a README asset"
AssetMutationResponse response =
 Asset.purge(client, "b4113341-251b-4adc-81fb-2420501c30e6"); // (1)
Asset deleted = response.getDeletedAssets().get(0); // (2)
Readme readme;
if (deleted instanceof Readme)

```

1. Call the purge() method with the README's GUID to remove it permanently. Because this operation will persist the asset in Atlan, you must [provide it an `AtlanClient`](https://docs.atlan.com/llms/platform/python/set-up-sdk/llms.txt) through which to connect to the tenant.
2. You can distinguish what was purged through the `getDeletedAssets()` method. This lists only the assets deleted by the operation.
3. If the deleted asset is a README, cast it to the Readme type.

### Python

```python showLineNumbers title="Hard-delete (purge) a README asset"
from pyatlan.client.atlan import AtlanClient
from pyatlan.model.assets import Readme

client = AtlanClient()
response = client.asset.purge_by_guid("b4113341-251b-4adc-81fb-2420501c30e6") # (1)
if deleted := response.assets_deleted(asset_type=Readme): # (2)
 Readme = deleted[0] # (3)
```

1. Use the asset.purge_by_guid() method with the README's GUID to perform the hard-delete.
2. Use the assets_deleted(asset_type=Readme) method to filter for deleted READMEs.
3. If a README was deleted, access its details through the returned response.

### Kotlin

```kotlin showLineNumbers title="Hard-delete (purge) a README asset"
val response =
 Asset.purge(client, "b4113341-251b-4adc-81fb-2420501c30e6") // (1)
val deleted = response.deletedAssets[0] // (2)
val readme = if (deleted is Readme) deleted else null // (3)
```

1. Call the purge() method with the README's GUID to permanently remove. Because this operation will persist the asset in Atlan, you must [provide it an `AtlanClient`](https://docs.atlan.com/llms/platform/python/set-up-sdk/llms.txt) through which to connect to the tenant.
2. You can distinguish what was purged through the `deletedAssets` method. This lists only the assets deleted by the operation.
3. Verify and cast the deleted asset to the README type

### Raw REST API

```json showLineNumbers title="DELETE /api/meta/entity/bulk?guid=b4113341-251b-4adc-81fb-2420501c30e6&deleteType=PURGE"
// (1)
```

1. When deleting a README via the API, specify its GUID in the URL and use a deleteType of PURGE.

For more options on deleting README assets: [Deleting an asset](https://docs.atlan.com/llms/platform/python/delete-asset/llms.txt).

:::warning[The README will have its own GUID, separate from the asset to which it's attached]
When deleting the README, you need to use the README's GUID, not the GUID of the asset to which it's attached.
:::

---
