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

[backend] adding TagRule apis/repo Issue/1998 #2122

Merged
merged 36 commits into from
Jan 3, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
ab2b9dc
repo+api
heditar Dec 19, 2024
16bd922
fixed CreateInject imports
heditar Dec 19, 2024
4074944
fixed CreateInject imports
heditar Dec 19, 2024
def8373
api+repo+test
heditar Dec 23, 2024
005a612
apply spotless
heditar Dec 23, 2024
a610f46
add tagrule mapper
heditar Dec 23, 2024
e81ded6
add tagrule mapper
heditar Dec 23, 2024
8a27fc0
fixed migration file
heditar Dec 23, 2024
9c5d1a8
apply spotless
heditar Dec 23, 2024
b35e671
change exception to ElementNotFoundException
heditar Dec 23, 2024
1eeec11
change to mutable list for hibernate
heditar Dec 23, 2024
5eb41c4
change to orelseget
heditar Dec 24, 2024
2e33547
added on delete cascade
heditar Dec 24, 2024
35ff5ae
Merge branch 'release/1.11.0' into issue/1998
heditar Dec 24, 2024
4279a37
spotless
heditar Dec 24, 2024
baa57af
spotless
heditar Dec 24, 2024
79f6e9f
spotless
heditar Dec 24, 2024
0f1742c
fix unit tests
heditar Dec 24, 2024
099c08c
spotless
heditar Dec 24, 2024
1f87dfb
added default tag color + index on tag id
heditar Dec 27, 2024
4f410dc
fixed unit tests
heditar Dec 30, 2024
f130f33
fixed pr comments
heditar Jan 2, 2025
de9360d
added mvc tests
heditar Jan 2, 2025
125315f
spotless
heditar Jan 2, 2025
25e3e91
Merge branch 'release/1.11.0' into issue/1998
heditar Jan 2, 2025
de3132b
fixed tests
heditar Jan 2, 2025
1ecb1a5
spotless
heditar Jan 2, 2025
31dd72d
Merge branch 'release/1.11.0' into issue/1998
heditar Jan 2, 2025
b3e3f04
changed migration number
heditar Jan 2, 2025
e6103c6
fixed unit test
heditar Jan 2, 2025
7072ad9
fixed unit test
heditar Jan 2, 2025
448b0a3
spotless
heditar Jan 2, 2025
9f04092
Merge branch 'release/1.11.0' into issue/1998
heditar Jan 2, 2025
3a2e029
updated api tests
heditar Jan 3, 2025
8872153
updated mapper to instance
heditar Jan 3, 2025
1fa9a8d
Merge branch 'release/1.11.0' into issue/1998
heditar Jan 3, 2025
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
42 changes: 42 additions & 0 deletions openbas-api/src/main/java/io/openbas/migration/V3_56__TagRule.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package io.openbas.migration;

import java.sql.Connection;
import java.sql.Statement;
import org.flywaydb.core.api.migration.BaseJavaMigration;
import org.flywaydb.core.api.migration.Context;
import org.springframework.stereotype.Component;

@Component
public class V3_56__TagRule extends BaseJavaMigration {

@Override
public void migrate(Context context) throws Exception {
Connection connection = context.getConnection();
Statement select = connection.createStatement();
select.execute(
"""
CREATE TABLE tag_rules (
tag_rule_id varchar(255) not null,
tag_id varchar(255) not null
constraint tag_id_fk
references tags,
primary key (tag_rule_id)
);
CREATE INDEX idx_tag_id ON tag_rules (tag_id);
""");

select.execute(
"""
CREATE TABLE tag_rule_assets (
tag_rule_id varchar(255) not null
constraint tag_rule_id_fk
references tag_rules,
asset_id varchar(255) not null
constraint asset_id_fk
references assets
on delete cascade,
primary key (tag_rule_id, asset_id)
);
""");
}
}
104 changes: 104 additions & 0 deletions openbas-api/src/main/java/io/openbas/rest/tag_rule/TagRuleApi.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package io.openbas.rest.tag_rule;

import static io.openbas.database.model.User.ROLE_ADMIN;

import io.openbas.aop.LogExecutionTime;
import io.openbas.rest.helper.RestBehavior;
import io.openbas.rest.tag_rule.form.TagRuleInput;
import io.openbas.rest.tag_rule.form.TagRuleMapper;
import io.openbas.rest.tag_rule.form.TagRuleOutput;
import io.openbas.service.TagRuleService;
import io.openbas.utils.pagination.SearchPaginationInput;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import java.util.List;
import org.springframework.data.domain.Page;
import org.springframework.security.access.annotation.Secured;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;

@RestController
public class TagRuleApi extends RestBehavior {

public static final String TAG_RULE_URI = "/api/tag-rules";

private final TagRuleService tagRuleService;
private final TagRuleMapper tagRuleMapper;

public TagRuleApi(TagRuleService tagRuleService, TagRuleMapper tagRuleMapper) {
super();
this.tagRuleService = tagRuleService;
this.tagRuleMapper = tagRuleMapper;
}

@LogExecutionTime
@GetMapping(TagRuleApi.TAG_RULE_URI + "/{tagRuleId}")
@Operation(summary = "Get TagRule by Id")
public TagRuleOutput findTagRule(@PathVariable @NotBlank final String tagRuleId) {
heditar marked this conversation as resolved.
Show resolved Hide resolved
return tagRuleService.findById(tagRuleId).map(tagRuleMapper::toTagRuleOutput).orElse(null);
}

@LogExecutionTime
@GetMapping(TagRuleApi.TAG_RULE_URI)
@Operation(summary = "Get All TagRules")
public List<TagRuleOutput> tags() {
return tagRuleService.findAll().stream().map(tagRuleMapper::toTagRuleOutput).toList();
}

@Secured(ROLE_ADMIN)
@LogExecutionTime
@DeleteMapping(TagRuleApi.TAG_RULE_URI + "/{tagRuleId}")
@Transactional(rollbackFor = Exception.class)
@Operation(summary = "Delete TagRule", description = "TagRule needs to exists")
@ApiResponses(
value = {
@ApiResponse(responseCode = "200", description = "TagRule deleted"),
@ApiResponse(responseCode = "404", description = "TagRule not found")
})
public void deleteTagRule(@PathVariable @NotBlank final String tagRuleId) {
tagRuleService.deleteTagRule(tagRuleId);
}

@Secured(ROLE_ADMIN)
@LogExecutionTime
@PostMapping(TagRuleApi.TAG_RULE_URI)
@Transactional(rollbackFor = Exception.class)
@Operation(summary = "Create TagRule", description = "Tag and assets needs to exists")
@ApiResponses(
value = {
@ApiResponse(responseCode = "200", description = "TagRule created"),
@ApiResponse(responseCode = "404", description = "Tag or Asset not found")
})
public TagRuleOutput createTagRule(@Valid @RequestBody final TagRuleInput input) {
return tagRuleMapper.toTagRuleOutput(
tagRuleService.createTagRule(input.getTagName(), input.getAssets()));
}

@Secured(ROLE_ADMIN)
@LogExecutionTime
@PutMapping(TagRuleApi.TAG_RULE_URI + "/{tagRuleId}")
@Transactional(rollbackFor = Exception.class)
@Operation(summary = "Update TagRule", description = "Tag and assets needs to exists")
@ApiResponses(
value = {
@ApiResponse(responseCode = "200", description = "TagRule updated"),
@ApiResponse(responseCode = "404", description = "TagRule, Tag or Asset not found")
})
public TagRuleOutput updateTagRule(
@PathVariable @NotBlank final String tagRuleId,
@Valid @RequestBody final TagRuleInput input) {
return tagRuleMapper.toTagRuleOutput(
tagRuleService.updateTagRule(tagRuleId, input.getTagName(), input.getAssets()));
}

@LogExecutionTime
@PostMapping(TagRuleApi.TAG_RULE_URI + "/search")
@Operation(summary = "Search TagRule")
public Page<TagRuleOutput> searchTagRules(
@RequestBody @Valid SearchPaginationInput searchPaginationInput) {
return tagRuleService.searchTagRule(searchPaginationInput).map(tagRuleMapper::toTagRuleOutput);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package io.openbas.rest.tag_rule.form;

import static io.openbas.config.AppConfig.MANDATORY_MESSAGE;

import com.fasterxml.jackson.annotation.JsonProperty;
import jakarta.validation.constraints.NotBlank;
import java.util.ArrayList;
import java.util.List;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.extern.jackson.Jacksonized;

@Builder(toBuilder = true)
@Getter
@Jacksonized
@EqualsAndHashCode
public class TagRuleInput {
@NotBlank(message = MANDATORY_MESSAGE)
@JsonProperty("tag_name")
private String tagName;

@JsonProperty("tag_rule_assets")
private List<String> assets = new ArrayList<>();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package io.openbas.rest.tag_rule.form;

import io.openbas.database.model.Asset;
import io.openbas.database.model.TagRule;
import java.util.stream.Collectors;
import org.springframework.stereotype.Component;

@Component
public class TagRuleMapper {
heditar marked this conversation as resolved.
Show resolved Hide resolved
public TagRuleOutput toTagRuleOutput(final TagRule tagRule) {
return TagRuleOutput.builder()
.id(tagRule.getId())
.tagName(tagRule.getTag().getName())
.assets(
tagRule.getAssets().stream().collect(Collectors.toMap(Asset::getId, Asset::getName)))
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package io.openbas.rest.tag_rule.form;

import static io.openbas.config.AppConfig.MANDATORY_MESSAGE;

import com.fasterxml.jackson.annotation.JsonProperty;
import jakarta.validation.constraints.NotBlank;
import java.util.HashMap;
import java.util.Map;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;

@Builder(toBuilder = true)
@Getter
@EqualsAndHashCode
public class TagRuleOutput {
@NotBlank(message = MANDATORY_MESSAGE)
@JsonProperty("tag_rule_id")
private String id;

@NotBlank(message = MANDATORY_MESSAGE)
@JsonProperty("tag_name")
private String tagName;

@JsonProperty("tag_rule_assets")
Map<String, String> assets = new HashMap<>();
}
99 changes: 99 additions & 0 deletions openbas-api/src/main/java/io/openbas/service/TagRuleService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package io.openbas.service;

import static io.openbas.utils.pagination.PaginationUtils.buildPaginationJPA;

import com.cronutils.utils.VisibleForTesting;
import io.openbas.database.model.Asset;
import io.openbas.database.model.Tag;
import io.openbas.database.model.TagRule;
import io.openbas.database.repository.AssetRepository;
import io.openbas.database.repository.TagRepository;
import io.openbas.database.repository.TagRuleRepository;
import io.openbas.rest.exception.ElementNotFoundException;
import io.openbas.utils.pagination.SearchPaginationInput;
import jakarta.validation.constraints.NotBlank;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Service;

@Service
@RequiredArgsConstructor
public class TagRuleService {
private final TagRuleRepository tagRuleRepository;
private final TagRepository tagRepository;
private final AssetRepository assetRepository;

public Optional<TagRule> findById(String id) {
return tagRuleRepository.findById(id);
heditar marked this conversation as resolved.
Show resolved Hide resolved
}

public List<TagRule> findAll() {
return StreamSupport.stream(tagRuleRepository.findAll().spliterator(), false)
.collect(Collectors.toList());
}

public TagRule createTagRule(@NotBlank final String tagName, final List<String> assetIds) {
// if the tag or one of the asset doesn't exist we exist throw a ElementNotFoundException
TagRule tagRule = new TagRule();
tagRule.setTag(getTag(tagName));
tagRule.setAssets(getAssets(assetIds));
return tagRuleRepository.save(tagRule);
}

public TagRule updateTagRule(
@NotBlank final String tagRuleId, final String tagName, final List<String> assetIds) {

// verify that the tag rule exists
TagRule tagRule =
tagRuleRepository
.findById(tagRuleId)
.orElseThrow(
() -> new ElementNotFoundException("TagRule not found with id: " + tagRuleId));

tagRule.setTag(getTag(tagName));

// if one of the asset doesn't exist throw a ResourceNotFoundException
tagRule.setAssets(getAssets(assetIds));

return tagRuleRepository.save(tagRule);
}

public Page<TagRule> searchTagRule(SearchPaginationInput searchPaginationInput) {
return buildPaginationJPA(tagRuleRepository::findAll, searchPaginationInput, TagRule.class);
}

public void deleteTagRule(@NotBlank final String tagRuleId) {
// verify that the TagRule exists
tagRuleRepository
.findById(tagRuleId)
.orElseThrow(() -> new ElementNotFoundException("TagRule not found with id: " + tagRuleId));
tagRuleRepository.deleteById(tagRuleId);
}

@VisibleForTesting
protected Tag getTag(@NotBlank final String tagName) {
// TODO: tag name normalization needs to be implemented in a reusable method
return tagRepository
.findByName(tagName.toLowerCase())
.orElseThrow(() -> new ElementNotFoundException("Tag not found with name: " + tagName));
}

@VisibleForTesting
protected List<Asset> getAssets(final List<String> assetIds) {
return assetIds == null
? new ArrayList<>()

Check warning on line 89 in openbas-api/src/main/java/io/openbas/service/TagRuleService.java

View check run for this annotation

Codecov / codecov/patch

openbas-api/src/main/java/io/openbas/service/TagRuleService.java#L89

Added line #L89 was not covered by tests
: assetIds.stream()
.map(
id ->
assetRepository
.findById(id)
.orElseThrow(
() -> new ElementNotFoundException("Asset not found with id: " + id)))
.collect(Collectors.toList());
}
}
Loading
Loading