diff --git a/api/apps/api/src/modules/geo-feature-tags/geo-feature-tags.service.ts b/api/apps/api/src/modules/geo-feature-tags/geo-feature-tags.service.ts index ee2da121ed..018836d141 100644 --- a/api/apps/api/src/modules/geo-feature-tags/geo-feature-tags.service.ts +++ b/api/apps/api/src/modules/geo-feature-tags/geo-feature-tags.service.ts @@ -160,20 +160,27 @@ export class GeoFeatureTagsService { await apiQueryRunner.startTransaction(); try { - const previousTag = await this.geoFeatureTagsRepo.findOne({ + const previousTag = await apiQueryRunner.manager.findOne(GeoFeatureTag, { where: { projectId, featureId }, }); if (previousTag) { - await this.geoFeatureTagsRepo.delete(previousTag.id); + await apiQueryRunner.manager.delete(GeoFeatureTag, previousTag.id); } - await this.geoFeatureTagsRepo.save( - this.geoFeatureTagsRepo.create({ projectId, featureId, tag }), + await apiQueryRunner.manager.save(GeoFeatureTag, { + projectId, + featureId, + tag, + }); + + const updatedGeoFeature = await apiQueryRunner.manager.findOneOrFail( + GeoFeature, + { + where: { id: featureId, projectId }, + }, ); - const updatedGeoFeature = await this.geoFeaturesRepo.findOneOrFail({ - where: { id: featureId, projectId }, - }); + await apiQueryRunner.commitTransaction(); return right(updatedGeoFeature); } catch (err) { diff --git a/api/apps/api/test/geo-features/geo-feature-tags.e2e-spec.ts b/api/apps/api/test/geo-features/geo-feature-tags.e2e-spec.ts index 37c02251ae..cdc0fec9d9 100644 --- a/api/apps/api/test/geo-features/geo-feature-tags.e2e-spec.ts +++ b/api/apps/api/test/geo-features/geo-feature-tags.e2e-spec.ts @@ -302,4 +302,44 @@ describe('GeoFeatureTag PATCH (e2e)', () => { expect(response.status).toBe(HttpStatus.OK); await fixtures.ThenFeatureHasTag(projectId, featureId, equivalentTag); }); + + /** + * @see MRXN23-316 + */ + test('updating feature tags in a large batch of concurrent-ish operations should complete without errors', async () => { + // ARRANGE + const projectId = await fixtures.GivenProject('someProject'); + // create an array of 100 features + const features = await Promise.all( + Array.from({ length: 100 }, (_item, index) => index).map((featureId) => + fixtures.GivenFeatureOnProject(projectId, `feature${featureId}`), + ), + ); + await Promise.all( + features.map((feature) => + fixtures.GivenTagOnFeature(projectId, feature, 'oldTag'), + ), + ); + const newTag = 'newTag'; + + // ACT + const responses = await Promise.all( + features.map((feature) => + fixtures.WhenPatchingAGeoFeatureTag(projectId, feature, newTag), + ), + ); + + // ASSERT + responses.forEach((response) => + expect(response.status).toBe(HttpStatus.OK), + ); + responses.forEach((response) => + expect(response.body.data.type).toBe('geo_features'), + ); + await Promise.all( + features.map((feature) => + fixtures.ThenFeatureHasTag(projectId, feature, newTag), + ), + ); + }); });