diff --git a/app/controllers/admin/document_data_dictionaries_controller.rb b/app/controllers/admin/document_data_dictionaries_controller.rb new file mode 100644 index 00000000..be176196 --- /dev/null +++ b/app/controllers/admin/document_data_dictionaries_controller.rb @@ -0,0 +1,147 @@ +module Admin + class DocumentDataDictionariesController < ApplicationController + before_action :set_document + before_action :set_document_data_dictionary, only: %i[ show edit update destroy ] + + # GET /document_data_dictionaries or /document_data_dictionaries.json + def index + @document_data_dictionaries = DocumentDataDictionary.all + if params[:document_id] + @document_data_dictionaries = DocumentDataDictionary.where(friendlier_id: @document.friendlier_id).order(position: :asc) + else + @pagy, @document_data_dictionaries = pagy(DocumentDataDictionary.all.order(friendlier_id: :asc, updated_at: :desc), items: 20) + end + end + + # GET /document_data_dictionaries/1 or /document_data_dictionaries/1.json + def show + end + + # GET /document_data_dictionaries/new + def new + @document_data_dictionary = DocumentDataDictionary.new + end + + # GET /document_data_dictionaries/1/edit + def edit + end + + # POST /document_data_dictionaries or /document_data_dictionaries.json + def create + @document_data_dictionary = DocumentDataDictionary.new(document_data_dictionary_params) + + respond_to do |format| + if @document_data_dictionary.save + format.html { redirect_to admin_document_document_data_dictionaries_path(@document), notice: "Document data dictionary was successfully created." } + format.json { render :show, status: :created, location: @document_data_dictionary } + else + format.html { render :new, status: :unprocessable_entity } + format.json { render json: @document_data_dictionary.errors, status: :unprocessable_entity } + end + end + end + + # PATCH/PUT /document_data_dictionaries/1 or /document_data_dictionaries/1.json + def update + respond_to do |format| + if @document_data_dictionary.update(document_data_dictionary_params) + format.html { redirect_to admin_document_document_data_dictionaries_path(@document), notice: "Document data dictionary was successfully updated." } + format.json { render :show, status: :ok, location: @document_data_dictionary } + else + format.html { render :edit, status: :unprocessable_entity } + format.json { render json: @document_data_dictionary.errors, status: :unprocessable_entity } + end + end + end + + # DELETE /document_data_dictionaries/1 or /document_data_dictionaries/1.json + def destroy + @document_data_dictionary.destroy! + + respond_to do |format| + format.html { redirect_to admin_document_document_data_dictionaries_path(@document), status: :see_other, notice: "Document data dictionary was successfully destroyed." } + format.json { head :no_content } + end + end + + # DELETE /admin/document_data_dictionaries/destroy_all + # + # Destroys all document data dictionaries provided in the file parameter. If successful, redirects + # with a success notice. Otherwise, redirects with an error notice. + def destroy_all + return if request.get? + + logger.debug("Destroy Data Dictionaries") + unless params.dig(:document_data_dictionary, :data_dictionaries, :file) + raise ArgumentError, "File does not exist or is invalid." + end + + respond_to do |format| + if DocumentDataDictionary.destroy_all(params.dig(:document_data_dictionary, :data_dictionaries, :file)) + format.html { redirect_to admin_document_document_data_dictionaries_path, notice: "Data dictionaries were destroyed." } + else + format.html { redirect_to admin_document_document_data_dictionaries_path, notice: "Data dictionaries could not be destroyed." } + end + rescue => e + format.html { redirect_to admin_document_document_data_dictionaries_path, notice: "Data dictionaries could not be destroyed. #{e}" } + end + end + + # GET/POST /documents/1/data_dictionaries/import + # + # Imports document data dictionaries from a file. If successful, redirects with a success notice. + # Otherwise, redirects with an error notice. + def import + return if request.get? + + logger.debug("Import Data Dictionaries") + + unless params.dig(:document_data_dictionary, :data_dictionaries, :file) + raise ArgumentError, "File does not exist or is invalid." + end + + if DocumentDataDictionary.import(params.dig(:document_data_dictionary, :data_dictionaries, :file)) + logger.debug("Data dictionaries were created successfully.") + if params[:document_id] + redirect_to admin_document_document_data_dictionaries_path(@document), notice: "Data dictionaries were created successfully." + else + redirect_to admin_document_document_data_dictionaries_path, notice: "Data dictionaries were created successfully." + end + else + logger.debug("Data dictionaries could not be created.") + if params[:document_id] + redirect_to admin_document_document_data_dictionaries_path(@document), warning: "Data dictionaries could not be created." + else + redirect_to admin_document_document_data_dictionaries_path, warning: "Data dictionaries could not be created." + end + end + rescue => e + logger.debug("Data dictionaries could not be created. #{e}") + if params[:document_id] + redirect_to admin_document_document_data_dictionaries_path(@document), notice: "Data dictionaries could not be created. #{e}" + else + redirect_to admin_document_document_data_dictionaries_path, notice: "Data dictionaries could not be created. #{e}" + end + end + + private + + # Sets the document based on the document_id parameter. + # If not nested, it does nothing. + def set_document + return unless params[:document_id] # If not nested + + @document = Document.includes(:leaf_representative).find_by!(friendlier_id: params[:document_id]) + end + + # Sets the document data dictionary based on the id parameter. + def set_document_data_dictionary + @document_data_dictionary = DocumentDataDictionary.find(params[:id]) + end + + # Only allow a list of trusted parameters through. + def document_data_dictionary_params + params.require(:document_data_dictionary).permit(:friendlier_id, :label, :type, :values, :definition, :definition_source, :parent_friendlier_id, :position) + end + end +end \ No newline at end of file diff --git a/app/models/document_data_dictionary.rb b/app/models/document_data_dictionary.rb new file mode 100644 index 00000000..41a628ea --- /dev/null +++ b/app/models/document_data_dictionary.rb @@ -0,0 +1,2 @@ +class DocumentDataDictionary < ApplicationRecord +end diff --git a/app/views/admin/document_data_dictionaries/_document_data_dictionary.html.erb b/app/views/admin/document_data_dictionaries/_document_data_dictionary.html.erb new file mode 100644 index 00000000..3339b63b --- /dev/null +++ b/app/views/admin/document_data_dictionaries/_document_data_dictionary.html.erb @@ -0,0 +1,37 @@ +
+

+ Friendlier: + <%= document_data_dictionary.friendlier_id %> +

+ +

+ Label: + <%= document_data_dictionary.label %> +

+ +

+ Type: + <%= document_data_dictionary.type %> +

+ +

+ Values: + <%= document_data_dictionary.values %> +

+ +

+ Definition: + <%= document_data_dictionary.definition %> +

+ +

+ Definition source: + <%= document_data_dictionary.definition_source %> +

+ +

+ Parent friendlier: + <%= document_data_dictionary.parent_friendlier_id %> +

+ +
diff --git a/app/views/admin/document_data_dictionaries/_document_data_dictionary.json.jbuilder b/app/views/admin/document_data_dictionaries/_document_data_dictionary.json.jbuilder new file mode 100644 index 00000000..34e3aedf --- /dev/null +++ b/app/views/admin/document_data_dictionaries/_document_data_dictionary.json.jbuilder @@ -0,0 +1,2 @@ +json.extract! document_data_dictionary, :id, :friendlier_id, :label, :type, :values, :definition, :definition_source, :parent_friendlier_id, :created_at, :updated_at +json.url document_data_dictionary_url(document_data_dictionary, format: :json) diff --git a/app/views/admin/document_data_dictionaries/_form.html.erb b/app/views/admin/document_data_dictionaries/_form.html.erb new file mode 100644 index 00000000..dc1b1b63 --- /dev/null +++ b/app/views/admin/document_data_dictionaries/_form.html.erb @@ -0,0 +1,19 @@ + +<%= simple_form_for(@document_data_dictionary) do |f| %> + <%= f.error_notification %> + <%= f.error_notification message: f.object.errors[:base].to_sentence if f.object.errors[:base].present? %> + +
+ <%= f.input :friendlier_id %> + <%= f.input :label %> + <%= f.input :type %> + <%= f.input :values %> + <%= f.input :definition %> + <%= f.input :definition_source %> + <%= f.input :parent_friendlier_id %> +
+ +
+ <%= f.button :submit %> +
+<% end %> diff --git a/app/views/admin/document_data_dictionaries/edit.html.erb b/app/views/admin/document_data_dictionaries/edit.html.erb new file mode 100644 index 00000000..5c1c6ad8 --- /dev/null +++ b/app/views/admin/document_data_dictionaries/edit.html.erb @@ -0,0 +1,12 @@ +<% content_for :title, "Editing document data dictionary" %> + +

Editing document data dictionary

+ +<%= render "form", document_data_dictionary: @document_data_dictionary %> + +
+ +
+ <%= link_to "Show this document data dictionary", @document_data_dictionary %> | + <%= link_to "Back to document data dictionaries", document_data_dictionaries_path %> +
diff --git a/app/views/admin/document_data_dictionaries/index.html.erb b/app/views/admin/document_data_dictionaries/index.html.erb new file mode 100644 index 00000000..25d8fe7d --- /dev/null +++ b/app/views/admin/document_data_dictionaries/index.html.erb @@ -0,0 +1,16 @@ +

<%= notice %>

+ +<% content_for :title, "Document data dictionaries" %> + +

Document data dictionaries

+ +
+ <% @document_data_dictionaries.each do |document_data_dictionary| %> + <%= render document_data_dictionary %> +

+ <%= link_to "Show this document data dictionary", document_data_dictionary %> +

+ <% end %> +
+ +<%= link_to "New document data dictionary", new_admin_document_document_data_dictionary_path(@document) %> diff --git a/app/views/admin/document_data_dictionaries/index.json.jbuilder b/app/views/admin/document_data_dictionaries/index.json.jbuilder new file mode 100644 index 00000000..b7a0992d --- /dev/null +++ b/app/views/admin/document_data_dictionaries/index.json.jbuilder @@ -0,0 +1 @@ +json.array! @document_data_dictionaries, partial: "document_data_dictionaries/document_data_dictionary", as: :document_data_dictionary diff --git a/app/views/admin/document_data_dictionaries/new.html.erb b/app/views/admin/document_data_dictionaries/new.html.erb new file mode 100644 index 00000000..edcce3b8 --- /dev/null +++ b/app/views/admin/document_data_dictionaries/new.html.erb @@ -0,0 +1,11 @@ +<% content_for :title, "New document data dictionary" %> + +

New document data dictionary

+ +<%= render "form", document_data_dictionary: @document_data_dictionary %> + +
+ +
+ <%= link_to "Back to document data dictionaries", document_data_dictionaries_path %> +
diff --git a/app/views/admin/document_data_dictionaries/show.html.erb b/app/views/admin/document_data_dictionaries/show.html.erb new file mode 100644 index 00000000..7ed4ba8a --- /dev/null +++ b/app/views/admin/document_data_dictionaries/show.html.erb @@ -0,0 +1,10 @@ +

<%= notice %>

+ +<%= render @document_data_dictionary %> + +
+ <%= link_to "Edit this document data dictionary", edit_document_data_dictionary_path(@document_data_dictionary) %> | + <%= link_to "Back to document data dictionaries", document_data_dictionaries_path %> + + <%= button_to "Destroy this document data dictionary", @document_data_dictionary, method: :delete %> +
diff --git a/app/views/admin/document_data_dictionaries/show.json.jbuilder b/app/views/admin/document_data_dictionaries/show.json.jbuilder new file mode 100644 index 00000000..aad64a2d --- /dev/null +++ b/app/views/admin/document_data_dictionaries/show.json.jbuilder @@ -0,0 +1 @@ +json.partial! "document_data_dictionaries/document_data_dictionary", document_data_dictionary: @document_data_dictionary diff --git a/db/migrate/20241204163117_create_document_data_dictionaries.rb b/db/migrate/20241204163117_create_document_data_dictionaries.rb new file mode 100644 index 00000000..8a959a4d --- /dev/null +++ b/db/migrate/20241204163117_create_document_data_dictionaries.rb @@ -0,0 +1,16 @@ +class CreateDocumentDataDictionaries < ActiveRecord::Migration[7.2] + def change + create_table :document_data_dictionaries do |t| + t.string :friendlier_id + t.string :label + t.string :type + t.string :values + t.string :definition + t.string :definition_source + t.string :parent_friendlier_id + t.integer :position + + t.timestamps + end + end +end diff --git a/lib/generators/geoblacklight_admin/config_generator.rb b/lib/generators/geoblacklight_admin/config_generator.rb index 106ec528..ece79c7d 100644 --- a/lib/generators/geoblacklight_admin/config_generator.rb +++ b/lib/generators/geoblacklight_admin/config_generator.rb @@ -205,6 +205,17 @@ def set_routes end end + # Data Dictionaries + resources :document_data_dictionaries, path: "data_dictionaries" do + collection do + get "import" + post "import" + + get "destroy_all" + post "destroy_all" + end + end + # DocumentDownloads resources :document_downloads, path: "downloads" do collection do diff --git a/lib/generators/geoblacklight_admin/templates/.env.development.example b/lib/generators/geoblacklight_admin/templates/.env.development.example index 1772f5cb..73e63f95 100644 --- a/lib/generators/geoblacklight_admin/templates/.env.development.example +++ b/lib/generators/geoblacklight_admin/templates/.env.development.example @@ -11,7 +11,7 @@ SOLR_URL=http://127.0.0.1:8983/solr/blacklight-core # PostgreSQL POSTGRES_HOST=127.0.0.1 -POSTGRES_PORT=5555 +POSTGRES_PORT=5432 POSTGRES_DB=geoblacklight_admin_development POSTGRES_USER=postgres POSTGRES_PASSWORD=postgres diff --git a/test/controllers/document_data_dictionary_controller_test.rb b/test/controllers/document_data_dictionary_controller_test.rb new file mode 100644 index 00000000..d01079dc --- /dev/null +++ b/test/controllers/document_data_dictionary_controller_test.rb @@ -0,0 +1,79 @@ +require "test_helper" + +class DocumentDataDictionaryControllerTest < ActionDispatch::IntegrationTest + setup do + @document = documents(:ls) + @document_data_dictionary = document_data_dictionaries(:one) + @file = fixture_file_upload("import_data_dictionary.csv", "text/csv") + + get "/users/sign_in" + sign_in_as users(:user_001) + post user_session_url + + follow_redirect! + assert_response :success + end + + test "should get index" do + get admin_document_document_data_dictionary_url(@document) + assert_response :success + end + + test "should get new" do + get new_admin_document_document_data_dictionary_url(@document) + assert_response :success + end + + test "should create document_data_dictionary" do + assert_difference("DocumentDataDictionary.count") do + post admin_document_document_data_dictionary_url(@document), params: {document_data_dictionary: { + friendlier_id: "35c8a641589c4e13b7aa11e37f3f00a1_0", + reference_type_id: ReferenceType.first.id, + url: "https://example.com" + }} + end + + assert_redirected_to admin_document_document_distributions_url(@document) + end + + test "should show document_data_dictionary" do + get admin_document_document_data_dictionary_url(@document, @document_data_dictionary) + assert_response :success + end + + test "should get edit" do + get edit_admin_document_document_data_dictionary_url(@document, @document_data_dictionary) + assert_response :success + end + + test "should update document_data_dictionary" do + patch admin_document_document_data_dictionary_url(@document, @document_data_dictionary), params: { + document_data_dictionary: { + friendlier_id: "35c8a641589c4e13b7aa11e37f3f00a1_0", + reference_type_id: ReferenceType.first.id, + url: "https://example2.com" + } + } + assert_redirected_to admin_document_document_data_dictionary_url(@document) + end + + test "should destroy document_data_dictionary" do + assert_difference("DocumentDataDictionary.count", -1) do + delete admin_document_document_data_dictionary_url(@document, @document_data_dictionary) + end + + assert_redirected_to admin_document_document_data_dictionary_url(@document) + end + + test "should import data dictionary successfully" do + post import_admin_document_document_data_dictionary_url(@document), params: {document_id: @document.friendlier_id, document_data_dictionary: {data_dictionary: {file: @file}}} + assert_redirected_to admin_document_document_data_dictionary_path(@document) + assert_equal "Data dictionary was created successfully.", flash[:notice] + end + + test "should not import data dictionary with invalid file" do + post import_admin_document_document_data_dictionary_url(@document), params: {document_id: @document.friendlier_id, document_data_dictionary: {data_dictionary: {file: nil}}} + assert_redirected_to admin_document_document_data_dictionary_path(@document) + assert_equal "Data dictionary could not be created. File does not exist or is invalid.", flash[:notice] + end +end