Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -364,13 +364,13 @@ public void onSubscribe(SubscribeEventContext context) {
repository.setDescription("Onboarding Repo Demo");
repository.setDisplayName(" Test Onboarding repo");
repository.setSubdomain(subdomain);
repository.setHashAlgorithms("SHA-256");

// Using SDMAdminServiceImpl onboardRepository() to onboard repository
SDMAdminService sdmAdminService = new SDMAdminServiceImpl();
String response = sdmAdminService.onboardRepository(repository);
}
```

```java
@After(event = DeploymentService.EVENT_UNSUBSCRIBE)
public void afterUnsubscribe(UnsubscribeEventContext context) {
Expand All @@ -385,6 +385,11 @@ public void onSubscribe(SubscribeEventContext context) {
String res = sdmAdminService.offboardRepository(subdomain);
}
```

> **Note**
>
> Unsubscription will fail if an error occurs while deleting the repository, except when the error indicates that the repository was not found — in that case, the unsubscription will succeed.

When the application is deployed as a SaaS application with above code, a repository is onboarded automatically when a tenant subscribes the SaaS application. The same repository is deleted when the tenant unsubscribes from the SaaS application.
The necessary params for the Repository onboarding can be found in the [documentation](https://help.sap.com/docs/document-management-service/sap-document-management-service/internal-repository).

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -302,22 +302,29 @@ private void formResponse(
String error = "";
try {
String responseString = EntityUtils.toString(response.getEntity());
JSONObject jsonResponse = new JSONObject(responseString);
int responseCode = response.getStatusLine().getStatusCode();
if (responseCode == 201 || responseCode == 200) {
JSONObject jsonResponse = new JSONObject(responseString);
JSONObject succinctProperties = jsonResponse.getJSONObject("succinctProperties");
status = "success";
objectId = succinctProperties.getString("cmis:objectId");
} else {
String message = jsonResponse.getString("message");
if (responseCode == 409
&& "Malware Service Exception: Virus found in the file!".equals(message)) {
status = "virus";
} else if (responseCode == 409) {
status = "duplicate";
} else if (responseCode == 403) {
if (responseCode == 409) {
JSONObject jsonResponse = new JSONObject(responseString);
String message = jsonResponse.getString("message");
JSONObject succinctProperties = jsonResponse.getJSONObject("succinctProperties");
objectId = succinctProperties.getString("cmis:objectId");
if ("Malware Service Exception: Virus found in the file!".equals(message)) {
status = "virus";
} else {
status = "duplicate";
}
} else if ((responseCode == 403)
&& (responseString.equals("User does not have required scope"))) {
status = "unauthorized";
} else {
JSONObject jsonResponse = new JSONObject(responseString);
String message = jsonResponse.getString("message");
status = "fail";
error = message;
}
Expand Down
205 changes: 172 additions & 33 deletions sdm/src/main/java/com/sap/cds/sdm/service/SDMAdminServiceImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
import com.sap.cloud.security.config.ClientCredentials;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpDelete;
Expand All @@ -35,50 +37,147 @@ public class SDMAdminServiceImpl implements SDMAdminService {
@java.lang.Override
public String onboardRepository(Repository repository)
throws JsonProcessingException, UnsupportedEncodingException {
SDMCredentials sdmCredentials = tokenHandler.getSDMCredentials();
var httpClient =
tokenHandler.getHttpClient(
null, null, repository.getSubdomain(), "TECHNICAL_CREDENTIALS_FLOW");
if (repository == null) {
logger.error("Repository object is null. Cannot proceed with onboarding.");
throw new IllegalArgumentException("Repository object cannot be null.");
}

SDMCredentials sdmCredentials;
try {
sdmCredentials = tokenHandler.getSDMCredentials();
if (sdmCredentials == null || sdmCredentials.getUrl() == null) {
logger.error("SDM credentials are missing or invalid.");
throw new ServiceException("SDM credentials are missing or invalid.");
}
} catch (Exception e) {
logger.error("Failed to retrieve SDM credentials: " + e.getMessage());
throw new ServiceException("Failed to retrieve SDM credentials.", e);
}

HttpClient httpClient = null;
try {
httpClient =
tokenHandler.getHttpClient(
null, null, repository.getSubdomain(), "TECHNICAL_CREDENTIALS_FLOW");
if (httpClient == null) {
logger.error("Failed to create HTTP client.");
throw new ServiceException("Failed to create HTTP client.");
}
} catch (Exception e) {
logger.error("Error while creating HTTP client: " + e.getMessage());
throw new ServiceException("Error while creating HTTP client.", e);
}

String sdmUrl = sdmCredentials.getUrl() + SDMConstants.REST_V2_REPOSITORIES;
HttpPost onboardingReq = new HttpPost(sdmUrl);
ObjectMapper objectMapper = new ObjectMapper();
RepositoryBody onboardRepository = new RepositoryBody();
repository.setExternalId(REPOSITORY_ID);
onboardRepository.setRepository(repository);
String json = objectMapper.writeValueAsString(onboardRepository);
StringEntity entity = new StringEntity(json);

try {
Copy link
Collaborator

Choose a reason for hiding this comment

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

can we have a single try and multiple catch blocks instead of multiple try

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Sure

repository.setExternalId(REPOSITORY_ID);
onboardRepository.setRepository(repository);
} catch (Exception e) {
logger.error("Failed to set repository details: " + e.getMessage());
throw new ServiceException("Failed to set repository details.", e);
}

String json;
try {
json = objectMapper.writeValueAsString(onboardRepository);
} catch (JsonProcessingException e) {
logger.error("Failed to serialize repository object to JSON: " + e.getMessage());
throw new ServiceException("Failed to serialize repository object to JSON.", e);
}

StringEntity entity;
try {
entity = new StringEntity(json);
} catch (UnsupportedEncodingException e) {
logger.error("Failed to create StringEntity: " + e.getMessage());
throw new ServiceException("Failed to create StringEntity.", e);
}

onboardingReq.setEntity(entity);
// Set the content type of the request
onboardingReq.setHeader("Content-Type", "application/json");

try (var response = (CloseableHttpResponse) httpClient.execute(onboardingReq)) {
String responseString = EntityUtils.toString(response.getEntity());

if ((responseString.contains(REPOSITORY_ID + " already exists"))
&& response.getStatusLine().getStatusCode() == 409) {
return String.format(
SDMConstants.REPOSITORY_ALREADY_EXIST, repository.getDisplayName(), REPOSITORY_ID);
}
JsonObject jsonObject = JsonParser.parseString(responseString).getAsJsonObject();
String repositoryId = jsonObject.get("id").getAsString();

JsonObject jsonObject;
Copy link
Collaborator

Choose a reason for hiding this comment

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

can we test update subscription flow as well

jsonObject = JsonParser.parseString(responseString).getAsJsonObject();

String repositoryId;
if (jsonObject.has("id") && !jsonObject.get("id").isJsonNull()) {
repositoryId = jsonObject.get("id").getAsString();
} else {
logger.error(
String.format(SDMConstants.ONBOARD_REPO_ERROR_MESSAGE, repository.getDisplayName())
+ " : "
+ responseString);
throw new ServiceException(
String.format(SDMConstants.ONBOARD_REPO_ERROR_MESSAGE, repository.getDisplayName()),
responseString);
}

return String.format(
SDMConstants.ONBOARD_REPO_MESSAGE, repository.getDisplayName(), repositoryId);
} catch (IOException e) {
} catch (Exception e) {
logger.error(
String.format(SDMConstants.ONBOARD_REPO_ERROR_MESSAGE, repository.getDisplayName())
+ " : "
+ e.getMessage());
throw new ServiceException(
String.format(SDMConstants.ONBOARD_REPO_ERROR_MESSAGE, repository.getDisplayName()),
e.getMessage());
String.format(SDMConstants.ONBOARD_REPO_ERROR_MESSAGE, repository.getDisplayName()), e);
}
}

@java.lang.Override
public String offboardRepository(String subdomain) {
SDMCredentials sdmCredentials = tokenHandler.getSDMCredentials();
ClientCredentials clientCredentials =
new ClientCredentials(sdmCredentials.getClientId(), sdmCredentials.getClientSecret());
SDMCredentials sdmCredentials;
try {
sdmCredentials = tokenHandler.getSDMCredentials();
if (sdmCredentials == null
|| sdmCredentials.getUrl() == null
|| sdmCredentials.getBaseTokenUrl() == null) {
logger.error("SDM credentials are missing or invalid.");
throw new ServiceException("SDM credentials are missing or invalid.");
}
} catch (Exception e) {
logger.error("Failed to retrieve SDM credentials: " + e.getMessage());
throw new ServiceException("Failed to retrieve SDM credentials.", e);
}

ClientCredentials clientCredentials;
try {
Copy link
Collaborator

Choose a reason for hiding this comment

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

can we have a single try and multiple catch blocks? this improves performance, better error handling easy maintainence

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Sure

clientCredentials =
new ClientCredentials(sdmCredentials.getClientId(), sdmCredentials.getClientSecret());
if (clientCredentials.getId() == null || clientCredentials.getSecret() == null) {
logger.error("Client credentials are missing or invalid.");
throw new ServiceException("Client credentials are missing or invalid.");
}
} catch (Exception e) {
logger.error("Failed to create client credentials: " + e.getMessage());
throw new ServiceException("Failed to create client credentials.", e);
}

String baseTokenUrl = sdmCredentials.getBaseTokenUrl();
if (subdomain != null && !subdomain.equals("")) {
String providersubdomain =
baseTokenUrl.substring(baseTokenUrl.indexOf("/") + 2, baseTokenUrl.indexOf("."));
baseTokenUrl = baseTokenUrl.replace(providersubdomain, subdomain);
if (subdomain != null && !subdomain.isEmpty()) {
try {
String providersubdomain =
baseTokenUrl.substring(baseTokenUrl.indexOf("/") + 2, baseTokenUrl.indexOf("."));
baseTokenUrl = baseTokenUrl.replace(providersubdomain, subdomain);
} catch (Exception e) {
logger.error("Failed to replace subdomain in base token URL: " + e.getMessage());
throw new ServiceException("Failed to replace subdomain in base token URL.", e);
}
}

var destination =
OAuth2DestinationBuilder.forTargetUrl(sdmCredentials.getUrl())
.withTokenEndpoint(baseTokenUrl)
Expand All @@ -92,44 +191,84 @@ public String offboardRepository(String subdomain) {
builder.maxConnectionsPerRoute(SDMConstants.MAX_CONNECTIONS_PER_ROUTE);
builder.maxConnectionsTotal(SDMConstants.MAX_CONNECTIONS_TOTAL);
DefaultHttpClientFactory factory = builder.build();
HttpClient httpClient = factory.createHttpClient(destination);
HttpClient httpClient;
try {
httpClient = factory.createHttpClient(destination);
if (httpClient == null) {
logger.error("Failed to create HTTP client.");
throw new ServiceException("Failed to create HTTP client.");
}
} catch (Exception e) {
logger.error("Error while creating HTTP client: " + e.getMessage());
throw new ServiceException("Error while creating HTTP client.", e);
}

String sdmUrl = sdmCredentials.getUrl() + SDMConstants.REST_V2_REPOSITORIES + "/";
HttpGet getRepos = new HttpGet(sdmUrl);
String repoId = "";
try (var response = (CloseableHttpResponse) httpClient.execute(getRepos)) {
repoId = getRepositoryId(EntityUtils.toString(response.getEntity()));
String responseString = EntityUtils.toString(response.getEntity());
repoId = getRepositoryId(responseString);
if (repoId == null || repoId.isEmpty()) {
logger.error("Repository ID not found");
return "Repository with ID " + SDMConstants.REPOSITORY_ID + " not found.";
}
} catch (IOException e) {
logger.error("Error in offboarding repository : " + e.getMessage());
throw new ServiceException("Error in offboarding ", e.getMessage());
logger.error("Error while fetching repository ID: " + e.getMessage());
throw new ServiceException("Error while fetching repository ID.", e);
} catch (Exception e) {
logger.error("Unexpected error while fetching repository ID: " + e.getMessage());
throw new ServiceException("Unexpected error while fetching repository ID.", e);
}

sdmUrl = sdmCredentials.getUrl() + SDMConstants.REST_V2_REPOSITORIES + "/" + repoId;
HttpDelete offboardingReq = new HttpDelete(sdmUrl);
// Set the content type of the request
offboardingReq.setHeader("Content-Type", "application/json");
try (var response = (CloseableHttpResponse) httpClient.execute(offboardingReq)) {
logger.info("Repository <" + REPOSITORY_ID + "> Offboarded");
return "Repository <" + REPOSITORY_ID + "> Offboarded";
int statusCode = response.getStatusLine().getStatusCode();
String responseString = EntityUtils.toString(response.getEntity());

if (statusCode != 200) { // Failed to offboard
if (statusCode == 404) { // Exception isn't thrown in case of missing repository
logger.warn("Repository with ID " + SDMConstants.REPOSITORY_ID + " not found.");
return "Repository with ID " + SDMConstants.REPOSITORY_ID + " not found.";
}
logger.error("Failed to offboard repository : " + responseString);
throw new ServiceException("Failed to offboard repository.", responseString);
}

logger.info("Repository " + repoId + " Offboarded");
return "Repository " + repoId + " Offboarded";
} catch (IOException e) {
logger.error("Error in offboarding repository : " + e.getMessage());
throw new ServiceException("Error in offboarding ", e.getMessage());
logger.error("Error while offboarding repository: " + e.getMessage());
throw new ServiceException("Error while offboarding repository.", e);
} catch (Exception e) {
logger.error("Unexpected error while offboarding repository: " + e.getMessage());
throw new ServiceException("Unexpected error while offboarding repository.", e);
}
}

private String getRepositoryId(String jsonString) {
ObjectMapper objectMapper = new ObjectMapper();
try {
JsonNode rootNode = objectMapper.readTree(jsonString);
JsonNode repoInfos = rootNode.path("repoAndConnectionInfos");
JsonNode repoInfosNode = rootNode.path("repoAndConnectionInfos");

List<JsonNode> repoInfos = new ArrayList<>();
if (repoInfosNode.isArray()) {
repoInfosNode.forEach(repoInfos::add);
} else if (!repoInfosNode.isMissingNode() && !repoInfosNode.isNull()) {
repoInfos.add(repoInfosNode); // wrap single object in a list
}

// Iterate through the array to find the correct externalId and retrieve the id
for (JsonNode repoInfo : repoInfos) {
JsonNode repository = repoInfo.path("repository");
if (repository.path("externalId").asText().equals(SDMConstants.REPOSITORY_ID)) {
return repository.path("id").asText();
}
}
} catch (Exception e) {
throw new ServiceException(String.format(e.getMessage()));
throw new ServiceException("Failed to parse repository response", e);
}
return null;
}
Expand Down
30 changes: 19 additions & 11 deletions sdm/src/main/java/com/sap/cds/sdm/service/SDMServiceImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -145,22 +145,30 @@ private void formResponse(
String error = "";
try {
String responseString = EntityUtils.toString(response.getEntity());
JSONObject jsonResponse = new JSONObject(responseString);
int responseCode = response.getStatusLine().getStatusCode();

if (responseCode == 201 || responseCode == 200) {
JSONObject succinctProperties = jsonResponse.getJSONObject("succinctProperties");
status = "success";
JSONObject jsonResponse = new JSONObject(responseString);
JSONObject succinctProperties = jsonResponse.getJSONObject("succinctProperties");
objectId = succinctProperties.getString("cmis:objectId");
} else {
String message = jsonResponse.getString("message");
if (responseCode == 409
&& "Malware Service Exception: Virus found in the file!".equals(message)) {
status = "virus";
} else if (responseCode == 409) {
status = "duplicate";
} else if (responseCode == 403) {
if (responseCode == 409) {
JSONObject jsonResponse = new JSONObject(responseString);
String message = jsonResponse.getString("message");
JSONObject succinctProperties = jsonResponse.getJSONObject("succinctProperties");
objectId = succinctProperties.getString("cmis:objectId");
if ("Malware Service Exception: Virus found in the file!".equals(message)) {
status = "virus";
} else {
status = "duplicate";
}
} else if ((responseCode == 403)
&& (responseString.equals("User does not have required scope"))) {
status = "unauthorized";
} else {
JSONObject jsonResponse = new JSONObject(responseString);
String message = jsonResponse.getString("message");
status = "fail";
error = message;
}
Expand All @@ -173,8 +181,8 @@ private void formResponse(
if (!objectId.isEmpty()) {
finalResponse.put("objectId", objectId);
}
} catch (IOException e) {
throw new ServiceException(SDMConstants.getGenericError("upload"));
} catch (Exception e) {
throw new ServiceException(e.getMessage());
}
}

Expand Down
Loading
Loading